From f03c104d807d9548db80d8bf5e6c639420de9a4d Mon Sep 17 00:00:00 2001 From: RedYetiDev <38299977+RedYetiDev@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:07:24 -0400 Subject: [PATCH] test: update wpt --- ...-partition-navigation.tentative.https.html | 128 ++++++ .../cross-partition.tentative.https.html | 419 ++++++++++++++---- .../wpt/FileAPI/blob/Blob-constructor.any.js | 12 +- test/fixtures/wpt/README.md | 40 +- test/fixtures/wpt/common/META.yml | 1 - .../wpt/common/dispatcher/dispatcher.js | 29 +- test/fixtures/wpt/common/dummy.json | 1 + test/fixtures/wpt/common/media.js | 16 +- test/fixtures/wpt/common/top-layer.js | 29 ++ ...ecompression-buffersource.tentative.any.js | 12 +- .../console-countReset-logging-manual.html | 6 +- .../console/console-log-large-array.any.js | 8 + .../wpt/console/console-log-symbol.any.js | 10 + test/fixtures/wpt/dom/abort/WEB_FEATURES.yml | 6 + .../wpt/dom/abort/abort-signal-any-crash.html | 23 + .../dom/abort/crashtests/any-on-abort.html | 11 + .../abort/resources/abort-signal-any-tests.js | 55 +++ .../wpt/dom/events/Event-dispatch-click.html | 56 +++ .../Event-dispatch-on-disabled-elements.html | 2 +- ...t-dispatch-single-activation-behavior.html | 164 +++++++ ...nt-dispatch-throwing-multiple-globals.html | 69 +++ .../events/EventTarget-constructible.any.js | 17 + .../fixtures/wpt/dom/events/event-global.html | 10 + .../events/pointer-event-document-move.html | 29 ++ ...entDefault-during-activation-behavior.html | 53 +++ .../wpt/dom/events/remove-all-listeners.html | 95 ++++ .../wpt/dom/events/scrolling/WEB_FEATURES.yml | 4 + .../events/scrolling/overscroll-deltas.html | 208 ++++++--- ...croll-event-fired-to-scrolled-element.html | 11 +- .../dom/events/scrolling/scroll_support.js | 125 +++++- ...d-after-sequence-of-scrolls.tentative.html | 76 +++- ...d-for-mandatory-snap-point-after-load.html | 87 ++++ ...d-event-fired-for-programmatic-scroll.html | 166 +++---- ...nd-event-fired-for-scroll-attr-change.html | 147 ++++++ ...ollend-event-fired-for-scrollIntoView.html | 8 +- .../scrollend-event-fired-to-document.html | 142 +++--- ...d-to-element-with-overscroll-behavior.html | 233 ++++++---- ...llend-event-fired-to-scrolled-element.html | 68 --- .../scrollend-event-fired-to-window.html | 90 ++-- ...ollend-event-fires-on-visual-viewport.html | 68 +++ ...end-event-fires-to-iframe-inner-frame.html | 30 ++ ...crollend-event-fires-to-iframe-window.html | 77 ++++ .../scrollend-event-for-user-scroll.html | 149 +------ ...crollend-event-not-fired-on-no-scroll.html | 114 +++++ .../scrollend-fires-to-text-input.html | 32 ++ .../scrolling/scrollend-user-scroll-common.js | 150 +++++++ ...ollend-with-snap-on-fractional-offset.html | 85 ++++ .../wheel-event-transactions-basic.html | 140 ++++++ ...t-transactions-multiple-action-chains.html | 101 +++++ ...nt-transactions-target-display-change.html | 100 +++++ ...el-event-transactions-target-elements.html | 74 ++++ .../wheel-event-transactions-target-move.html | 85 ++++ ...eel-event-transactions-target-removal.html | 92 ++++ ...heel-event-transactions-target-resize.html | 97 ++++ .../wpt/hr-time/raf-coarsened-time.https.html | 36 ++ .../raf-coarsened-time.https.html.headers | 2 + ...cross-realm-callback-report-exception.html | 28 ++ .../structured-clone/WEB_FEATURES.yml | 3 + ...ructured-clone-battery-of-tests-harness.js | 5 +- ...one-battery-of-tests-with-transferables.js | 147 ++++++ .../structured-clone-battery-of-tests.js | 257 ++++++++--- .../structured-clone-cross-realm-method.html | 20 + ...tructured-clone-detached-window-crash.html | 11 + .../structured-clone/structured-clone.any.js | 7 +- .../timers/clearinterval-from-callback.any.js | 19 + .../timers/evil-spec-example.any.js | 12 + .../webappapis/timers/evil-spec-example.html | 23 - ...cross-realm-callback-report-exception.html | 32 ++ ...cross-realm-callback-report-exception.html | 28 ++ test/fixtures/wpt/interfaces/dom.idl | 12 +- test/fixtures/wpt/interfaces/html.idl | 3 +- ...k-forward-cache-restoration.tentative.html | 95 ++++ .../droppedentriescount.any.js | 81 ++++ .../idlharness-shadowrealm.window.js | 2 + ...avigation-id-detached-frame.tentative.html | 30 ++ ...avigation-id-element-timing.tentative.html | 14 + .../navigation-id-initial-load.tentative.html | 49 ++ ...-long-task-task-attribution.tentative.html | 14 + .../navigation-id-mark-measure.tentative.html | 14 + .../navigation-id-reset.tentative.html | 53 +++ ...vigation-id-resource-timing.tentative.html | 14 + .../navigation-id-worker-created-entries.html | 27 ++ .../navigation-id.helper.js | 144 ++++++ .../abort-block-bfcache.window.js | 21 + ...tion-timing-attributes.tentative.window.js | 55 +++ ...g-bfcache-reasons-stay.tentative.window.js | 47 ++ ...igation-timing-bfcache.tentative.window.js | 28 ++ ...g-cross-origin-bfcache.tentative.window.js | 60 +++ ...avigation-timing-fetch.tentative.window.js | 40 ++ ...mes-without-attributes.tentative.window.js | 103 +++++ ...tion-timing-lock.https.tentative.window.js | 32 ++ ...ing-navigation-failure.tentative.window.js | 26 ++ ...on-timing-not-bfcached.tentative.window.js | 36 ++ ...ng-redirect-on-history.tentative.window.js | 53 +++ ...vigation-timing-reload.tentative.window.js | 50 +++ ...ng-same-origin-bfcache.tentative.window.js | 61 +++ ...ng-same-origin-replace.tentative.window.js | 43 ++ .../not-restored-reasons/test-helper.js | 57 +++ .../resources/child-frame.html | 7 + .../performance-timeline/resources/empty.html | 0 .../resources/going-back.html | 9 + .../resources/include-frames-helper.js | 60 +++ .../resources/include-frames-subframe.html | 43 ++ .../resources/json_resource.json | 4 + .../resources/make_long_task.js | 4 + .../navigation-id-detached-frame-page.html | 21 + .../resources/worker-navigation-id.js | 6 + ...upportedEntryTypes-cross-realm-access.html | 18 + .../tentative/detached-frame.html | 27 ++ .../tentative/include-frames-originA-A-A.html | 93 ++++ .../tentative/include-frames-originA-A.html | 76 ++++ .../tentative/include-frames-originA-AA.html | 51 +++ .../tentative/include-frames-originA-AB.html | 53 +++ .../tentative/include-frames-originA-B-A.html | 91 ++++ .../tentative/include-frames-originA-B-B.html | 57 +++ .../tentative/include-frames-originA-B.html | 50 +++ .../tentative/performance-entry-source.html | 37 ++ .../with-filter-options-originA.html | 26 ++ .../timing-removed-iframe.html | 16 + test/fixtures/wpt/resource-timing/META.yml | 1 - .../body-size-cross-origin.https.html | 63 +++ .../content-type-minimization.html | 79 ++++ .../wpt/resource-timing/content-type.html | 126 ++++++ ...-origin-start-end-time-with-redirects.html | 20 +- .../delivery-type.tentative.any.js | 90 ++++ .../entries-for-network-errors.sub.https.html | 16 + .../fetch-cross-origin-redirect.https.html | 2 +- .../resource-timing/iframe-failed-commit.html | 34 +- .../iframe-sequence-of-events.html | 16 +- .../resource-timing/initiator-type/link.html | 57 ++- .../initiator-type/script.html | 2 + .../resource-timing/initiator-type/video.html | 4 +- .../initiator-type/workers.html | 2 +- .../interim-response-times.h2.html | 74 ++++ .../interim-response-times.html | 72 +++ .../internal-resources-not-counted.html | 39 ++ .../nested-nav-fallback-timing.html | 33 ++ ...s-origin-css-fetched-memory-cache.sub.html | 53 +++ ...not-found-after-cross-origin-redirect.html | 25 +- .../queue-entry-regardless-buffer-size.html | 38 ++ ...ing-failed-fetch-web-bundle.tentative.html | 53 +++ .../resource-timing-failed-fetch.html | 34 ++ .../resource-timing/resource-timing-level1.js | 41 -- .../resource-timing/resources/child_script.js | 1 + .../resource-timing/resources/delay-load.html | 4 + .../resources/entry-invariants.js | 43 +- .../resource-timing/resources/frame-timing.js | 97 ++-- .../resources/get-resourceID.js | 30 ++ .../resources/loadingResources.js | 21 + .../resources/nested-contexts.js | 37 +- ...n-css-fetched-memory-cache-iframe.sub.html | 8 + .../resources/parent_script.js | 1 + .../resources/resource-loaders.js | 77 +++- .../resources/test-initiator.js | 16 + .../resource-timing/response-status-code.html | 165 +++++++ .../resource-timing/sizes-redirect-img.html | 8 +- .../tentative/document-initiated.html | 78 ++++ .../tentative/script-initiated.html | 24 + .../tentative/stylesheet-initiated.html | 23 + test/fixtures/wpt/resource-timing/tojson.html | 4 +- .../fixtures/wpt/resources/check-layout-th.js | 3 +- .../declarative-shadow-dom-polyfill.js | 25 -- test/fixtures/wpt/resources/idlharness.js | 1 + test/fixtures/wpt/resources/testdriver.js | 222 +++++++++- test/fixtures/wpt/resources/testharness.js | 194 ++++---- .../wpt/resources/testharnessreport.js | 25 -- .../streams/readable-byte-streams/tee.any.js | 33 ++ .../crashtests/from-cross-realm.https.html | 18 + .../transfer-with-messageport.window.js | 12 +- test/fixtures/wpt/url/META.yml | 1 - test/fixtures/wpt/url/README.md | 12 +- .../wpt/url/a-element-origin-xhtml.xhtml | 6 +- test/fixtures/wpt/url/a-element-origin.html | 6 +- test/fixtures/wpt/url/a-element-xhtml.xhtml | 6 +- test/fixtures/wpt/url/a-element.html | 6 +- test/fixtures/wpt/url/failure.html | 5 +- .../wpt/url/resources/a-element-origin.js | 5 +- test/fixtures/wpt/url/resources/a-element.js | 5 +- .../urltestdata-javascript-only.json | 18 + .../wpt/url/resources/urltestdata.json | 210 ++++++++- test/fixtures/wpt/url/url-constructor.any.js | 5 +- test/fixtures/wpt/url/url-origin.any.js | 5 +- test/fixtures/wpt/versions.json | 40 +- test/fixtures/wpt/wasm/jsapi/assertions.js | 5 + test/fixtures/wpt/wasm/jsapi/bad-imports.js | 1 + .../wpt/wasm/jsapi/constructor/compile.any.js | 2 +- .../instantiate-bad-imports.any.js | 2 +- .../wasm/jsapi/constructor/instantiate.any.js | 2 +- .../wasm/jsapi/constructor/multi-value.any.js | 2 +- .../wasm/jsapi/constructor/toStringTag.any.js | 2 +- .../wasm/jsapi/constructor/validate.any.js | 2 +- .../jsapi/exception/basic.tentative.any.js | 11 +- .../exception/constructor.tentative.any.js | 2 +- .../jsapi/exception/getArg.tentative.any.js | 2 +- .../jsapi/exception/identity.tentative.any.js | 2 +- .../wasm/jsapi/exception/is.tentative.any.js | 2 +- .../jsapi/exception/toString.tentative.any.js | 2 +- .../wasm/jsapi/function/call.tentative.any.js | 2 +- .../function/constructor.tentative.any.js | 2 +- .../jsapi/function/table.tentative.any.js | 2 +- .../wasm/jsapi/function/type.tentative.any.js | 2 +- .../wpt/wasm/jsapi/gc/casts.tentative.any.js | 332 ++++++++++++++ .../jsapi/gc/exported-object.tentative.any.js | 190 ++++++++ .../wpt/wasm/jsapi/gc/i31.tentative.any.js | 98 ++++ .../wpt/wasm/jsapi/global/constructor.any.js | 2 +- .../wpt/wasm/jsapi/global/toString.any.js | 2 +- .../wasm/jsapi/global/type.tentative.any.js | 2 +- .../wasm/jsapi/global/value-get-set.any.js | 2 +- .../wpt/wasm/jsapi/global/valueOf.any.js | 2 +- .../instance/constructor-bad-imports.any.js | 2 +- .../jsapi/instance/constructor-caching.any.js | 2 +- .../wasm/jsapi/instance/constructor.any.js | 2 +- .../wpt/wasm/jsapi/instance/exports.any.js | 2 +- .../wpt/wasm/jsapi/instance/toString.any.js | 2 +- .../wpt/wasm/jsapi/instanceTestFactory.js | 8 +- test/fixtures/wpt/wasm/jsapi/interface.any.js | 2 +- .../jsapi/js-string/basic.tentative.any.js | 383 ++++++++++++++++ .../js-string/constants.tentative.any.js | 61 +++ .../jsapi/js-string/imports.tentative.any.js | 26 ++ .../wpt/wasm/jsapi/js-string/polyfill.js | 170 +++++++ .../constructor-shared.tentative.any.js | 2 +- .../memory/constructor-types.tentative.any.js | 4 +- .../wpt/wasm/jsapi/memory/constructor.any.js | 2 +- .../wpt/wasm/jsapi/memory/grow.any.js | 2 +- .../wasm/jsapi/memory/type.tentative.any.js | 4 +- .../wpt/wasm/jsapi/module/constructor.any.js | 2 +- .../wasm/jsapi/module/customSections.any.js | 2 +- .../wpt/wasm/jsapi/module/exports.any.js | 8 +- .../wpt/wasm/jsapi/module/imports.any.js | 2 +- .../module/moduleSource.tentative.any.js | 35 ++ .../wpt/wasm/jsapi/module/toString.any.js | 2 +- .../fixtures/wpt/wasm/jsapi/prototypes.any.js | 2 +- .../table/constructor-types.tentative.any.js | 4 +- .../wpt/wasm/jsapi/table/constructor.any.js | 7 +- .../wpt/wasm/jsapi/table/get-set.any.js | 2 +- .../fixtures/wpt/wasm/jsapi/table/grow.any.js | 2 +- .../wpt/wasm/jsapi/table/length.any.js | 2 +- .../wpt/wasm/jsapi/table/toString.any.js | 2 +- .../wasm/jsapi/table/type.tentative.any.js | 2 +- .../jsapi/tag/constructor.tentative.any.js | 2 +- .../wasm/jsapi/tag/toString.tentative.any.js | 2 +- .../wpt/wasm/jsapi/tag/type.tentative.any.js | 2 +- .../wpt/wasm/jsapi/wasm-module-builder.js | 354 ++++++++++++--- .../resources/worker-source-phase.js | 10 + .../esm-integration/resources/worker.js | 3 +- .../script-src-allows-wasm.tentative.html | 21 + .../script-src-blocks-wasm.tentative.sub.html | 21 + .../source-phase.tentative.html | 35 ++ .../worker-import-source-phase.tentative.html | 13 + .../esm-integration/worker.tentative.html | 3 + .../broadcastchannel/WEB_FEATURES.yml | 3 + .../broadcastchannel/basics.any.js | 8 + ...P-iframe-opens-3P-window.partitioned.html} | 0 ...> localstorage-basic-partitioned.sub.html} | 2 +- ...orage-cross-origin-iframe.https.window.js} | 0 ...sessionStorage-basic-partitioned.sub.html} | 2 +- test/wpt/status/FileAPI/blob.json | 1 + test/wpt/status/WebCryptoAPI.json | 16 + test/wpt/status/html/webappapis/timers.json | 3 + test/wpt/status/performance-timeline.json | 53 +++ test/wpt/status/resource-timing.json | 3 + test/wpt/status/webstorage.json | 3 + 262 files changed, 9817 insertions(+), 1345 deletions(-) create mode 100644 test/fixtures/wpt/FileAPI/BlobURL/cross-partition-navigation.tentative.https.html create mode 100644 test/fixtures/wpt/common/dummy.json create mode 100644 test/fixtures/wpt/common/top-layer.js create mode 100644 test/fixtures/wpt/console/console-log-large-array.any.js create mode 100644 test/fixtures/wpt/console/console-log-symbol.any.js create mode 100644 test/fixtures/wpt/dom/abort/WEB_FEATURES.yml create mode 100644 test/fixtures/wpt/dom/abort/abort-signal-any-crash.html create mode 100644 test/fixtures/wpt/dom/abort/crashtests/any-on-abort.html create mode 100644 test/fixtures/wpt/dom/events/Event-dispatch-single-activation-behavior.html create mode 100644 test/fixtures/wpt/dom/events/Event-dispatch-throwing-multiple-globals.html create mode 100644 test/fixtures/wpt/dom/events/pointer-event-document-move.html create mode 100644 test/fixtures/wpt/dom/events/preventDefault-during-activation-behavior.html create mode 100644 test/fixtures/wpt/dom/events/remove-all-listeners.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/WEB_FEATURES.yml create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-mandatory-snap-point-after-load.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-scroll-attr-change.html delete mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-scrolled-element.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-inner-frame.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-window.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-event-not-fired-on-no-scroll.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-fires-to-text-input.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-user-scroll-common.js create mode 100644 test/fixtures/wpt/dom/events/scrolling/scrollend-with-snap-on-fractional-offset.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-basic.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-multiple-action-chains.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-display-change.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-elements.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-move.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-removal.html create mode 100644 test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-resize.html create mode 100644 test/fixtures/wpt/hr-time/raf-coarsened-time.https.html create mode 100644 test/fixtures/wpt/hr-time/raf-coarsened-time.https.html.headers create mode 100644 test/fixtures/wpt/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html create mode 100644 test/fixtures/wpt/html/webappapis/structured-clone/WEB_FEATURES.yml create mode 100644 test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method.html create mode 100644 test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-detached-window-crash.html create mode 100644 test/fixtures/wpt/html/webappapis/timers/clearinterval-from-callback.any.js create mode 100644 test/fixtures/wpt/html/webappapis/timers/evil-spec-example.any.js delete mode 100644 test/fixtures/wpt/html/webappapis/timers/evil-spec-example.html create mode 100644 test/fixtures/wpt/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html create mode 100644 test/fixtures/wpt/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html create mode 100644 test/fixtures/wpt/performance-timeline/back-forward-cache-restoration.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/droppedentriescount.any.js create mode 100644 test/fixtures/wpt/performance-timeline/idlharness-shadowrealm.window.js create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-detached-frame.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-element-timing.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-initial-load.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-long-task-task-attribution.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-mark-measure.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-reset.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-resource-timing.tentative.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id-worker-created-entries.html create mode 100644 test/fixtures/wpt/performance-timeline/navigation-id.helper.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/abort-block-bfcache.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-attributes.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache-reasons-stay.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-fetch.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-lock.https.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-redirect-on-history.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-reload.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-replace.tentative.window.js create mode 100644 test/fixtures/wpt/performance-timeline/not-restored-reasons/test-helper.js create mode 100644 test/fixtures/wpt/performance-timeline/resources/child-frame.html create mode 100644 test/fixtures/wpt/performance-timeline/resources/empty.html create mode 100644 test/fixtures/wpt/performance-timeline/resources/going-back.html create mode 100644 test/fixtures/wpt/performance-timeline/resources/include-frames-helper.js create mode 100644 test/fixtures/wpt/performance-timeline/resources/include-frames-subframe.html create mode 100644 test/fixtures/wpt/performance-timeline/resources/json_resource.json create mode 100644 test/fixtures/wpt/performance-timeline/resources/make_long_task.js create mode 100644 test/fixtures/wpt/performance-timeline/resources/navigation-id-detached-frame-page.html create mode 100644 test/fixtures/wpt/performance-timeline/resources/worker-navigation-id.js create mode 100644 test/fixtures/wpt/performance-timeline/supportedEntryTypes-cross-realm-access.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/detached-frame.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A-A.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AA.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AB.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-A.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-B.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/performance-entry-source.html create mode 100644 test/fixtures/wpt/performance-timeline/tentative/with-filter-options-originA.html create mode 100644 test/fixtures/wpt/performance-timeline/timing-removed-iframe.html create mode 100644 test/fixtures/wpt/resource-timing/body-size-cross-origin.https.html create mode 100644 test/fixtures/wpt/resource-timing/content-type-minimization.html create mode 100644 test/fixtures/wpt/resource-timing/content-type.html create mode 100644 test/fixtures/wpt/resource-timing/delivery-type.tentative.any.js create mode 100644 test/fixtures/wpt/resource-timing/interim-response-times.h2.html create mode 100644 test/fixtures/wpt/resource-timing/interim-response-times.html create mode 100644 test/fixtures/wpt/resource-timing/internal-resources-not-counted.html create mode 100644 test/fixtures/wpt/resource-timing/nested-nav-fallback-timing.html create mode 100644 test/fixtures/wpt/resource-timing/no-entries-for-cross-origin-css-fetched-memory-cache.sub.html create mode 100644 test/fixtures/wpt/resource-timing/queue-entry-regardless-buffer-size.html create mode 100644 test/fixtures/wpt/resource-timing/resource-timing-failed-fetch-web-bundle.tentative.html create mode 100644 test/fixtures/wpt/resource-timing/resource-timing-failed-fetch.html create mode 100644 test/fixtures/wpt/resource-timing/resources/child_script.js create mode 100644 test/fixtures/wpt/resource-timing/resources/delay-load.html create mode 100644 test/fixtures/wpt/resource-timing/resources/get-resourceID.js create mode 100644 test/fixtures/wpt/resource-timing/resources/loadingResources.js create mode 100644 test/fixtures/wpt/resource-timing/resources/no-entries-for-cross-origin-css-fetched-memory-cache-iframe.sub.html create mode 100644 test/fixtures/wpt/resource-timing/resources/parent_script.js create mode 100644 test/fixtures/wpt/resource-timing/resources/test-initiator.js create mode 100644 test/fixtures/wpt/resource-timing/response-status-code.html create mode 100644 test/fixtures/wpt/resource-timing/tentative/document-initiated.html create mode 100644 test/fixtures/wpt/resource-timing/tentative/script-initiated.html create mode 100644 test/fixtures/wpt/resource-timing/tentative/stylesheet-initiated.html delete mode 100644 test/fixtures/wpt/resources/declarative-shadow-dom-polyfill.js create mode 100644 test/fixtures/wpt/streams/readable-streams/crashtests/from-cross-realm.https.html create mode 100644 test/fixtures/wpt/url/resources/urltestdata-javascript-only.json create mode 100644 test/fixtures/wpt/wasm/jsapi/gc/casts.tentative.any.js create mode 100644 test/fixtures/wpt/wasm/jsapi/gc/exported-object.tentative.any.js create mode 100644 test/fixtures/wpt/wasm/jsapi/gc/i31.tentative.any.js create mode 100644 test/fixtures/wpt/wasm/jsapi/js-string/basic.tentative.any.js create mode 100644 test/fixtures/wpt/wasm/jsapi/js-string/constants.tentative.any.js create mode 100644 test/fixtures/wpt/wasm/jsapi/js-string/imports.tentative.any.js create mode 100644 test/fixtures/wpt/wasm/jsapi/js-string/polyfill.js create mode 100644 test/fixtures/wpt/wasm/jsapi/module/moduleSource.tentative.any.js create mode 100644 test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-source-phase.js create mode 100644 test/fixtures/wpt/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html create mode 100644 test/fixtures/wpt/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html create mode 100644 test/fixtures/wpt/wasm/webapi/esm-integration/source-phase.tentative.html create mode 100644 test/fixtures/wpt/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html create mode 100644 test/fixtures/wpt/webmessaging/broadcastchannel/WEB_FEATURES.yml rename test/fixtures/wpt/webstorage/{localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.tentative.html => localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html} (100%) rename test/fixtures/wpt/webstorage/{localstorage-basic-partitioned.tentative.sub.html => localstorage-basic-partitioned.sub.html} (97%) rename test/fixtures/wpt/webstorage/{localstorage-cross-origin-iframe.tentative.https.window.js => localstorage-cross-origin-iframe.https.window.js} (100%) rename test/fixtures/wpt/webstorage/{sessionStorage-basic-partitioned.tentative.sub.html => sessionStorage-basic-partitioned.sub.html} (97%) create mode 100644 test/wpt/status/WebCryptoAPI.json diff --git a/test/fixtures/wpt/FileAPI/BlobURL/cross-partition-navigation.tentative.https.html b/test/fixtures/wpt/FileAPI/BlobURL/cross-partition-navigation.tentative.https.html new file mode 100644 index 00000000000000..cdfa145a838c15 --- /dev/null +++ b/test/fixtures/wpt/FileAPI/BlobURL/cross-partition-navigation.tentative.https.html @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/FileAPI/BlobURL/cross-partition.tentative.https.html b/test/fixtures/wpt/FileAPI/BlobURL/cross-partition.tentative.https.html index c75ce07d054eb7..5cdaad5f0af2d5 100644 --- a/test/fixtures/wpt/FileAPI/BlobURL/cross-partition.tentative.https.html +++ b/test/fixtures/wpt/FileAPI/BlobURL/cross-partition.tentative.https.html @@ -13,31 +13,54 @@ - + \ No newline at end of file diff --git a/test/fixtures/wpt/FileAPI/blob/Blob-constructor.any.js b/test/fixtures/wpt/FileAPI/blob/Blob-constructor.any.js index 6dc44e8e156cce..57a85624340e56 100644 --- a/test/fixtures/wpt/FileAPI/blob/Blob-constructor.any.js +++ b/test/fixtures/wpt/FileAPI/blob/Blob-constructor.any.js @@ -290,14 +290,22 @@ test_blob(function() { new Int16Array([0x4150, 0x5353]), new Uint32Array([0x53534150]), new Int32Array([0x53534150]), - new Float16Array([2.65625, 58.59375]), new Float32Array([0xD341500000]) ]); }, { - expected: "PASSPASSPASSPASSPASSPASSPASSPASS", + expected: "PASSPASSPASSPASSPASSPASSPASS", type: "", desc: "Passing typed arrays as elements of the blobParts array should work." }); +test_blob(function() { + return new Blob([ + new Float16Array([2.65625, 58.59375]) + ]); +}, { + expected: "PASS", + type: "", + desc: "Passing a Float16Array as element of the blobParts array should work." +}); test_blob(function() { return new Blob([ // 0x535 3415053534150 diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 118bb456a8eecd..cbcf0edbeffa56 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -10,32 +10,32 @@ See [test/wpt](../../wpt/README.md) for information on how these tests are run. Last update: -- common: https://github.com/web-platform-tests/wpt/tree/dbd648158d/common -- compression: https://github.com/web-platform-tests/wpt/tree/5aa50dd415/compression -- console: https://github.com/web-platform-tests/wpt/tree/767ae35464/console -- dom/abort: https://github.com/web-platform-tests/wpt/tree/d1f1ecbd52/dom/abort -- dom/events: https://github.com/web-platform-tests/wpt/tree/ab8999891c/dom/events +- common: https://github.com/web-platform-tests/wpt/tree/d8da9d4d1d/common +- compression: https://github.com/web-platform-tests/wpt/tree/da8d6860b2/compression +- console: https://github.com/web-platform-tests/wpt/tree/e48251b778/console +- dom/abort: https://github.com/web-platform-tests/wpt/tree/0143fe244b/dom/abort +- dom/events: https://github.com/web-platform-tests/wpt/tree/0a811c5161/dom/events - encoding: https://github.com/web-platform-tests/wpt/tree/5aa50dd415/encoding - fetch/data-urls/resources: https://github.com/web-platform-tests/wpt/tree/7c79d998ff/fetch/data-urls/resources -- FileAPI: https://github.com/web-platform-tests/wpt/tree/cceaf3628d/FileAPI -- hr-time: https://github.com/web-platform-tests/wpt/tree/34cafd797e/hr-time +- FileAPI: https://github.com/web-platform-tests/wpt/tree/40a4e28f25/FileAPI +- hr-time: https://github.com/web-platform-tests/wpt/tree/614e81711c/hr-time - html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob -- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing -- html/webappapis/structured-clone: https://github.com/web-platform-tests/wpt/tree/47d3fb280c/html/webappapis/structured-clone -- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/5873f2d8f1/html/webappapis/timers -- interfaces: https://github.com/web-platform-tests/wpt/tree/e90ece61d6/interfaces -- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline -- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing -- resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources -- streams: https://github.com/web-platform-tests/wpt/tree/2bd26e124c/streams -- url: https://github.com/web-platform-tests/wpt/tree/6a39784534/url +- html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/22ecfc9bac/html/webappapis/microtask-queuing +- html/webappapis/structured-clone: https://github.com/web-platform-tests/wpt/tree/c29dcddf37/html/webappapis/structured-clone +- html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/7a0548ac47/html/webappapis/timers +- interfaces: https://github.com/web-platform-tests/wpt/tree/4a48b52da3/interfaces +- performance-timeline: https://github.com/web-platform-tests/wpt/tree/94caab7038/performance-timeline +- resource-timing: https://github.com/web-platform-tests/wpt/tree/7bb012885c/resource-timing +- resources: https://github.com/web-platform-tests/wpt/tree/d9f17001dc/resources +- streams: https://github.com/web-platform-tests/wpt/tree/699dbdae30/streams +- url: https://github.com/web-platform-tests/wpt/tree/08519e73d1/url - user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing -- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi -- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi +- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/f427091001/wasm/jsapi +- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/28456b73ca/wasm/webapi - WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/6748a0a246/WebCryptoAPI - webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions -- webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/e97fac4791/webmessaging/broadcastchannel -- webstorage: https://github.com/web-platform-tests/wpt/tree/9dafa89214/webstorage +- webmessaging/broadcastchannel: https://github.com/web-platform-tests/wpt/tree/6495c91853/webmessaging/broadcastchannel +- webstorage: https://github.com/web-platform-tests/wpt/tree/1291340aaa/webstorage [Web Platform Tests]: https://github.com/web-platform-tests/wpt [`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt diff --git a/test/fixtures/wpt/common/META.yml b/test/fixtures/wpt/common/META.yml index ca4d2e523c0866..963dff9d1f1ecf 100644 --- a/test/fixtures/wpt/common/META.yml +++ b/test/fixtures/wpt/common/META.yml @@ -1,3 +1,2 @@ suggested_reviewers: - - zqzhang - deniak diff --git a/test/fixtures/wpt/common/dispatcher/dispatcher.js b/test/fixtures/wpt/common/dispatcher/dispatcher.js index a0f9f43e622483..dab0100020b829 100644 --- a/test/fixtures/wpt/common/dispatcher/dispatcher.js +++ b/test/fixtures/wpt/common/dispatcher/dispatcher.js @@ -1,7 +1,32 @@ // Define a universal message passing API. It works cross-origin and across // browsing context groups. const dispatcher_path = "/common/dispatcher/dispatcher.py"; -const dispatcher_url = new URL(dispatcher_path, location.href).href; + +// Finds the nearest ancestor window that has a non srcdoc location. This should +// give us a usable location for constructing further URLs. +function findLocationFromAncestors(w) { + if (w.location.href == 'about:srcdoc') { + return findLocationFromAncestors(w.parent); + } + return w.location; +} + +// Handles differences between workers vs frames (src vs srcdoc). +function findLocation() { + if (location.href == 'about:srcdoc') { + return findLocationFromAncestors(window.parent); + } + if (location.protocol == 'blob:' || location.protocol == 'data:') { + // Allows working around blob and data URLs. + if (self.document && self.document.baseURI) { + return self.document.baseURI; + } + } + return location; +} + +const dispatcherLocation = findLocation(); +const dispatcher_url = new URL(dispatcher_path, dispatcherLocation).href; // Return a promise, limiting the number of concurrent accesses to a shared // resources to |max_concurrent_access|. @@ -138,7 +163,7 @@ const cacheableShowRequestHeaders = function(origin, uuid) { // protocol: (optional) Sets the returned URL's `protocol` property. // } function remoteExecutorUrl(uuid, options) { - const url = new URL("/common/dispatcher/remote-executor.html", location); + const url = new URL("/common/dispatcher/remote-executor.html", dispatcherLocation); url.searchParams.set("uuid", uuid); if (options?.host) { diff --git a/test/fixtures/wpt/common/dummy.json b/test/fixtures/wpt/common/dummy.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/test/fixtures/wpt/common/dummy.json @@ -0,0 +1 @@ +{} diff --git a/test/fixtures/wpt/common/media.js b/test/fixtures/wpt/common/media.js index f2dc8612660495..a5a8e957e9b5be 100644 --- a/test/fixtures/wpt/common/media.js +++ b/test/fixtures/wpt/common/media.js @@ -9,10 +9,12 @@ function getVideoURI(base) var videotag = document.createElement("video"); - if ( videotag.canPlayType && - videotag.canPlayType('video/ogg; codecs="theora, vorbis"') ) + if ( videotag.canPlayType ) { - extension = '.ogv'; + if (videotag.canPlayType('video/webm; codecs="vp9, opus"') ) + { + extension = '.webm'; + } } return base + extension; @@ -46,10 +48,10 @@ function getAudioURI(base) function getMediaContentType(url) { var extension = new URL(url, location).pathname.split(".").pop(); var map = { - "mp4": "video/mp4", - "ogv": "application/ogg", - "mp3": "audio/mp3", - "oga": "application/ogg", + "mp4" : "video/mp4", + "webm": "video/webm", + "mp3" : "audio/mp3", + "oga" : "application/ogg", }; return map[extension]; } diff --git a/test/fixtures/wpt/common/top-layer.js b/test/fixtures/wpt/common/top-layer.js new file mode 100644 index 00000000000000..2dc8ce3893aa72 --- /dev/null +++ b/test/fixtures/wpt/common/top-layer.js @@ -0,0 +1,29 @@ +// This function is a version of test_driver.bless which works while there are +// elements in the top layer: +// https://github.com/web-platform-tests/wpt/issues/41218. +// Pass it the element at the top of the top layer stack. +window.blessTopLayer = async (topLayerElement) => { + const button = document.createElement('button'); + topLayerElement.append(button); + let wait_click = new Promise(resolve => button.addEventListener("click", resolve, {once: true})); + await test_driver.click(button); + await wait_click; + button.remove(); +}; + +window.isTopLayer = (el) => { + // A bit of a hack. Just test a few properties of the ::backdrop pseudo + // element that change when in the top layer. + const properties = ['right','background']; + const testEl = document.createElement('div'); + document.body.appendChild(testEl); + const computedStyle = getComputedStyle(testEl, '::backdrop'); + const nonTopLayerValues = properties.map(p => computedStyle[p]); + testEl.remove(); + for(let i=0;i new Float16Array(new Uint8Array(compressedBytesWithDeflate).buffer) }, { name: 'Float32Array', @@ -100,7 +100,7 @@ const bufferSourceChunksForGzip = [ }, { name: 'Float16Array', - value: new Float16Array(new Uint8Array(compressedBytesWithGzip).buffer) + value: () => new Float16Array(new Uint8Array(compressedBytesWithGzip).buffer) }, { name: 'Float32Array', @@ -151,7 +151,7 @@ const bufferSourceChunksForDeflateRaw = [ }, { name: 'Float16Array', - value: new Float16Array(new Uint8Array(compressedBytesWithDeflateRaw).buffer) + value: () => new Float16Array(new Uint8Array(compressedBytesWithDeflateRaw).buffer) }, { name: 'Float32Array', @@ -172,7 +172,7 @@ for (const chunk of bufferSourceChunksForDeflate) { const ds = new DecompressionStream('deflate'); const reader = ds.readable.getReader(); const writer = ds.writable.getWriter(); - const writePromise = writer.write(chunk.value); + const writePromise = writer.write(typeof chunk.value === 'function' ? chunk.value() : chunk.value); writer.close(); const { value } = await reader.read(); assert_array_equals(Array.from(value), deflateExpectedChunkValue, 'value should match'); @@ -184,7 +184,7 @@ for (const chunk of bufferSourceChunksForGzip) { const ds = new DecompressionStream('gzip'); const reader = ds.readable.getReader(); const writer = ds.writable.getWriter(); - const writePromise = writer.write(chunk.value); + const writePromise = writer.write(typeof chunk.value === 'function' ? chunk.value() : chunk.value); writer.close(); const { value } = await reader.read(); assert_array_equals(Array.from(value), gzipExpectedChunkValue, 'value should match'); @@ -196,7 +196,7 @@ for (const chunk of bufferSourceChunksForDeflateRaw) { const ds = new DecompressionStream('deflate-raw'); const reader = ds.readable.getReader(); const writer = ds.writable.getWriter(); - const writePromise = writer.write(chunk.value); + const writePromise = writer.write(typeof chunk.value === 'function' ? chunk.value() : chunk.value); writer.close(); const { value } = await reader.read(); assert_array_equals(Array.from(value), deflateRawExpectedChunkValue, 'value should match'); diff --git a/test/fixtures/wpt/console/console-countReset-logging-manual.html b/test/fixtures/wpt/console/console-countReset-logging-manual.html index 7fe01f50edbfa1..f0a9358fba5d74 100644 --- a/test/fixtures/wpt/console/console-countReset-logging-manual.html +++ b/test/fixtures/wpt/console/console-countReset-logging-manual.html @@ -24,21 +24,25 @@ console.count(); console.countReset(); console.count(); +console.countReset(); console.count(undefined); console.countReset(undefined); console.count(undefined); +console.countReset(undefined); console.count("default"); console.countReset("default"); console.count("default"); +console.countReset("default"); console.count({toString() {return "default"}}); console.countReset({toString() {return "default"}}); console.count({toString() {return "default"}}); +console.countReset({toString() {return "default"}}); console.count("a label"); -console.countReset(); +console.countReset("a label"); console.count("a label"); console.countReset("b"); // should produce a warning diff --git a/test/fixtures/wpt/console/console-log-large-array.any.js b/test/fixtures/wpt/console/console-log-large-array.any.js new file mode 100644 index 00000000000000..e5cb92d9d36d4a --- /dev/null +++ b/test/fixtures/wpt/console/console-log-large-array.any.js @@ -0,0 +1,8 @@ +// META: global=window,dedicatedworker,shadowrealm +"use strict"; +// https://console.spec.whatwg.org/ + +test(() => { + console.log(new Array(10000000).fill("x")); + console.log(new Uint8Array(10000000)); +}, "Logging large arrays works"); diff --git a/test/fixtures/wpt/console/console-log-symbol.any.js b/test/fixtures/wpt/console/console-log-symbol.any.js new file mode 100644 index 00000000000000..a2facb6c64e864 --- /dev/null +++ b/test/fixtures/wpt/console/console-log-symbol.any.js @@ -0,0 +1,10 @@ +// META: global=window,dedicatedworker,shadowrealm +"use strict"; +// https://console.spec.whatwg.org/ + +test(() => { + console.log(Symbol()); + console.log(Symbol("abc")); + console.log(Symbol.for("def")); + console.log(Symbol.isConcatSpreadable); +}, "Logging a symbol doesn't throw"); diff --git a/test/fixtures/wpt/dom/abort/WEB_FEATURES.yml b/test/fixtures/wpt/dom/abort/WEB_FEATURES.yml new file mode 100644 index 00000000000000..d2fdc1555e2830 --- /dev/null +++ b/test/fixtures/wpt/dom/abort/WEB_FEATURES.yml @@ -0,0 +1,6 @@ +features: +- name: aborting + files: "**" +- name: abortsignal-any + files: + - abort-signal-any.any.js diff --git a/test/fixtures/wpt/dom/abort/abort-signal-any-crash.html b/test/fixtures/wpt/dom/abort/abort-signal-any-crash.html new file mode 100644 index 00000000000000..912c0d0ada7385 --- /dev/null +++ b/test/fixtures/wpt/dom/abort/abort-signal-any-crash.html @@ -0,0 +1,23 @@ + + + + AbortSignal::Any when source signal was garbage collected + + + + + +

Test passes if the browser does not crash.

+ + + diff --git a/test/fixtures/wpt/dom/abort/crashtests/any-on-abort.html b/test/fixtures/wpt/dom/abort/crashtests/any-on-abort.html new file mode 100644 index 00000000000000..07a0f0bd3c2ba6 --- /dev/null +++ b/test/fixtures/wpt/dom/abort/crashtests/any-on-abort.html @@ -0,0 +1,11 @@ + + + diff --git a/test/fixtures/wpt/dom/abort/resources/abort-signal-any-tests.js b/test/fixtures/wpt/dom/abort/resources/abort-signal-any-tests.js index 66e4141eaccb08..8f897c934e87ea 100644 --- a/test/fixtures/wpt/dom/abort/resources/abort-signal-any-tests.js +++ b/test/fixtures/wpt/dom/abort/resources/abort-signal-any-tests.js @@ -182,4 +182,59 @@ function abortSignalAnyTests(signalInterface, controllerInterface) { controller.abort(); assert_equals(result, "01234"); }, `Abort events for ${desc} signals fire in the right order ${suffix}`); + + test(t => { + const controller = new controllerInterface(); + const signal1 = signalInterface.any([controller.signal]); + const signal2 = signalInterface.any([signal1]); + let eventFired = false; + + controller.signal.addEventListener('abort', () => { + const signal3 = signalInterface.any([signal2]); + assert_true(controller.signal.aborted); + assert_true(signal1.aborted); + assert_true(signal2.aborted); + assert_true(signal3.aborted); + eventFired = true; + }); + + controller.abort(); + assert_true(eventFired, "event fired"); + }, `Dependent signals for ${desc} are marked aborted before abort events fire ${suffix}`); + + test(t => { + const controller1 = new controllerInterface(); + const controller2 = new controllerInterface(); + const signal = signalInterface.any([controller1.signal, controller2.signal]); + let count = 0; + + controller1.signal.addEventListener('abort', () => { + controller2.abort("reason 2"); + }); + + signal.addEventListener('abort', () => { + count++; + }); + + controller1.abort("reason 1"); + assert_equals(count, 1); + assert_true(signal.aborted); + assert_equals(signal.reason, "reason 1"); + }, `Dependent signals for ${desc} are aborted correctly for reentrant aborts ${suffix}`); + + test(t => { + const source = signalInterface.abort(); + const dependent = signalInterface.any([source]); + assert_true(source.reason instanceof DOMException); + assert_equals(source.reason, dependent.reason); + }, `Dependent signals for ${desc} should use the same DOMException instance from the already aborted source signal ${suffix}`); + + test(t => { + const controller = new controllerInterface(); + const source = controller.signal; + const dependent = signalInterface.any([source]); + controller.abort(); + assert_true(source.reason instanceof DOMException); + assert_equals(source.reason, dependent.reason); + }, `Dependent signals for ${desc} should use the same DOMException instance from the source signal being aborted later ${suffix}`); } diff --git a/test/fixtures/wpt/dom/events/Event-dispatch-click.html b/test/fixtures/wpt/dom/events/Event-dispatch-click.html index 010305775df7e9..ab4a24a5ad5096 100644 --- a/test/fixtures/wpt/dom/events/Event-dispatch-click.html +++ b/test/fixtures/wpt/dom/events/Event-dispatch-click.html @@ -87,6 +87,22 @@ child.dispatchEvent(new MouseEvent("click", {bubbles:true})) }, "pick the first with activation behavior ") +async_test(function(t) { + var input = document.createElement("input") + input.type = "radio" + dump.appendChild(input) + input.onclick = t.step_func(function() { + assert_false(input.checked, "input pre-click must not be triggered") + }) + var child = input.appendChild(document.createElement("input")) + child.type = "radio" + child.onclick = t.step_func(function() { + assert_true(child.checked, "child pre-click must be triggered") + }) + child.dispatchEvent(new MouseEvent("click", {bubbles:true})) + t.done() +}, "pick the first with activation behavior ") + async_test(function(t) { var input = document.createElement("input") input.type = "checkbox" @@ -173,6 +189,46 @@ t.done() }, "disabled checkbox still has activation behavior, part 2") +async_test(function(t) { + var state = "start" + + var form = document.createElement("form") + form.onsubmit = t.step_func(() => { + if(state == "start" || state == "radio") { + state = "failure" + } else if(state == "form") { + state = "done" + } + return false + }) + dump.appendChild(form) + var button = form.appendChild(document.createElement("button")) + button.type = "submit" + var radio = button.appendChild(document.createElement("input")) + radio.type = "radio" + radio.onclick = t.step_func(() => { + if(state == "start") { + assert_unreached() + } else if(state == "radio") { + assert_true(radio.checked) + } + }) + radio.disabled = true + radio.click() + assert_equals(state, "start") + + state = "radio" + radio.disabled = false + radio.click() + assert_equals(state, "radio") + + state = "form" + button.click() + assert_equals(state, "done") + + t.done() +}, "disabled radio still has activation behavior") + async_test(function(t) { var input = document.createElement("input") input.type = "checkbox" diff --git a/test/fixtures/wpt/dom/events/Event-dispatch-on-disabled-elements.html b/test/fixtures/wpt/dom/events/Event-dispatch-on-disabled-elements.html index 361006a7240496..e7d6b455bbcaab 100644 --- a/test/fixtures/wpt/dom/events/Event-dispatch-on-disabled-elements.html +++ b/test/fixtures/wpt/dom/events/Event-dispatch-on-disabled-elements.html @@ -19,7 +19,7 @@ + +
+ +
+ + + + + diff --git a/test/fixtures/wpt/dom/events/Event-dispatch-throwing-multiple-globals.html b/test/fixtures/wpt/dom/events/Event-dispatch-throwing-multiple-globals.html new file mode 100644 index 00000000000000..a56fdf6af86581 --- /dev/null +++ b/test/fixtures/wpt/dom/events/Event-dispatch-throwing-multiple-globals.html @@ -0,0 +1,69 @@ + + + + + + diff --git a/test/fixtures/wpt/dom/events/EventTarget-constructible.any.js b/test/fixtures/wpt/dom/events/EventTarget-constructible.any.js index b0e7614e625b3d..4125d23f0c9650 100644 --- a/test/fixtures/wpt/dom/events/EventTarget-constructible.any.js +++ b/test/fixtures/wpt/dom/events/EventTarget-constructible.any.js @@ -23,6 +23,23 @@ test(() => { assert_equals(callCount, 2); }, "A constructed EventTarget can be used as expected"); +test(() => { + const target = new EventTarget(); + const event = new Event("foo"); + + function listener(e) { + assert_equals(e, event); + assert_equals(e.target, target); + assert_equals(e.currentTarget, target); + assert_array_equals(e.composedPath(), [target]); + } + target.addEventListener("foo", listener, { once: true }); + target.dispatchEvent(event); + assert_equals(event.target, target); + assert_equals(event.currentTarget, null); + assert_array_equals(event.composedPath(), []); +}, "A constructed EventTarget implements dispatch correctly"); + test(() => { class NicerEventTarget extends EventTarget { on(...args) { diff --git a/test/fixtures/wpt/dom/events/event-global.html b/test/fixtures/wpt/dom/events/event-global.html index 3e8d25ecb5dd9d..f70606fb65496a 100644 --- a/test/fixtures/wpt/dom/events/event-global.html +++ b/test/fixtures/wpt/dom/events/event-global.html @@ -114,4 +114,14 @@ target.dispatchEvent(new Event("click")); }, "window.event is set to the current event, which is the event passed to dispatch"); + +async_test(t => { + let target = new XMLHttpRequest(); + + target.onload = t.step_func_done(e => { + assert_equals(e, window.event); + }); + + target.dispatchEvent(new Event("load")); +}, "window.event is set to the current event, which is the event passed to dispatch (2)"); diff --git a/test/fixtures/wpt/dom/events/pointer-event-document-move.html b/test/fixtures/wpt/dom/events/pointer-event-document-move.html new file mode 100644 index 00000000000000..91e7c36860572a --- /dev/null +++ b/test/fixtures/wpt/dom/events/pointer-event-document-move.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/dom/events/preventDefault-during-activation-behavior.html b/test/fixtures/wpt/dom/events/preventDefault-during-activation-behavior.html new file mode 100644 index 00000000000000..92874031347165 --- /dev/null +++ b/test/fixtures/wpt/dom/events/preventDefault-during-activation-behavior.html @@ -0,0 +1,53 @@ + +preventDefault during activation behavior + + + + + + + + + +
+ +
+ + diff --git a/test/fixtures/wpt/dom/events/remove-all-listeners.html b/test/fixtures/wpt/dom/events/remove-all-listeners.html new file mode 100644 index 00000000000000..3a2a751a146bca --- /dev/null +++ b/test/fixtures/wpt/dom/events/remove-all-listeners.html @@ -0,0 +1,95 @@ + +Various edge cases where listeners are removed during iteration + + +
+ diff --git a/test/fixtures/wpt/dom/events/scrolling/WEB_FEATURES.yml b/test/fixtures/wpt/dom/events/scrolling/WEB_FEATURES.yml new file mode 100644 index 00000000000000..e4fba841b62ac9 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: scrollend + files: + - scrollend-* diff --git a/test/fixtures/wpt/dom/events/scrolling/overscroll-deltas.html b/test/fixtures/wpt/dom/events/scrolling/overscroll-deltas.html index 6f0b77f22eda2a..e13e9f1cce5949 100644 --- a/test/fixtures/wpt/dom/events/scrolling/overscroll-deltas.html +++ b/test/fixtures/wpt/dom/events/scrolling/overscroll-deltas.html @@ -6,80 +6,152 @@ - -
-
+ +
+
+
+
+
diff --git a/test/fixtures/wpt/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html b/test/fixtures/wpt/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html index cfc782a809a7e7..be4176df59d6a1 100644 --- a/test/fixtures/wpt/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html +++ b/test/fixtures/wpt/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html @@ -10,11 +10,14 @@ width: 200px; height: 200px; overflow: scroll; + position: absolute; + left: 150px; + top: 150px; } #innerDiv { - width: 400px; - height: 400px; + width: 250px; + height: 250px; } @@ -45,7 +48,7 @@ await waitForCompositorCommit(); // Do a horizontal scroll and wait for overscroll event. - await touchScrollInTarget(300, scrolling_div , 'right'); + await touchScrollInTarget(100, scrolling_div , 'right'); await waitFor(() => { return overscrolled_x_delta > 0; }, 'Scroller did not receive overscroll event after horizontal scroll.'); assert_equals(scrolling_div.scrollWidth - scrolling_div.scrollLeft, @@ -55,7 +58,7 @@ overscrolled_y_delta = 0; // Do a vertical scroll and wait for overscroll event. - await touchScrollInTarget(300, scrolling_div, 'down'); + await touchScrollInTarget(100, scrolling_div, 'down'); await waitFor(() => { return overscrolled_y_delta > 0; }, 'Scroller did not receive overscroll event after vertical scroll.'); assert_equals(scrolling_div.scrollHeight - scrolling_div.scrollTop, diff --git a/test/fixtures/wpt/dom/events/scrolling/scroll_support.js b/test/fixtures/wpt/dom/events/scrolling/scroll_support.js index 169393e4c3e419..a708364df07cad 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scroll_support.js +++ b/test/fixtures/wpt/dom/events/scrolling/scroll_support.js @@ -1,15 +1,83 @@ -async function waitForScrollendEvent(test, target, timeoutMs = 500) { +async function waitForEvent(eventName, test, target, timeoutMs = 500) { return new Promise((resolve, reject) => { const timeoutCallback = test.step_timeout(() => { - reject(`No Scrollend event received for target ${target}`); + reject(`No ${eventName} event received for target ${target}`); }, timeoutMs); - target.addEventListener('scrollend', (evt) => { + target.addEventListener(eventName, (evt) => { clearTimeout(timeoutCallback); resolve(evt); }, { once: true }); }); } +async function waitForScrollendEvent(test, target, timeoutMs = 500) { + return waitForEvent("scrollend", test, target, timeoutMs); +} + +async function waitForScrollendEventNoTimeout(target) { + return new Promise((resolve) => { + target.addEventListener("scrollend", resolve); + }); +} + +async function waitForPointercancelEvent(test, target, timeoutMs = 500) { + return waitForEvent("pointercancel", test, target, timeoutMs); +} + +// Resets the scroll position to (0,0). If a scroll is required, then the +// promise is not resolved until the scrollend event is received. +async function waitForScrollReset(test, scroller, x = 0, y = 0) { + return new Promise(resolve => { + if (scroller.scrollTop == x && scroller.scrollLeft == y) { + resolve(); + } else { + const eventTarget = + scroller == document.scrollingElement ? document : scroller; + scroller.scrollTo(x, y); + waitForScrollendEventNoTimeout(eventTarget).then(resolve); + } + }); +} + +async function createScrollendPromiseForTarget(test, + target_div, + timeoutMs = 500) { + return waitForScrollendEvent(test, target_div, timeoutMs).then(evt => { + assert_false(evt.cancelable, 'Event is not cancelable'); + assert_false(evt.bubbles, 'Event targeting element does not bubble'); + }); +} + +function verifyNoScrollendOnDocument(test) { + const callback = + test.unreached_func("window got unexpected scrollend event."); + window.addEventListener('scrollend', callback); + test.add_cleanup(() => { + window.removeEventListener('scrollend', callback); + }); +} + +async function verifyScrollStopped(test, target_div) { + const unscaled_pause_time_in_ms = 100; + const x = target_div.scrollLeft; + const y = target_div.scrollTop; + return new Promise(resolve => { + test.step_timeout(() => { + assert_equals(target_div.scrollLeft, x); + assert_equals(target_div.scrollTop, y); + resolve(); + }, unscaled_pause_time_in_ms); + }); +} + +async function resetTargetScrollState(test, target_div) { + if (target_div.scrollTop != 0 || target_div.scrollLeft != 0) { + target_div.scrollTop = 0; + target_div.scrollLeft = 0; + return waitForScrollendEvent(test, target_div); + } +} + const MAX_FRAME = 700; const MAX_UNCHANGED_FRAMES = 20; @@ -45,6 +113,29 @@ function waitForCompositorCommit() { }); } +// Please don't remove this. This is necessary for chromium-based browsers. It +// can be a no-op on user-agents that do not have a separate compositor thread. +// TODO(crbug.com/1509054): This shouldn't be necessary if the test harness +// deferred running the tests until after paint holding. +async function waitForCompositorReady() { + const animation = + document.body.animate({ opacity: [ 0, 1 ] }, {duration: 1 }); + return animation.finished; +} + +function waitForNextFrame() { + const startTime = performance.now(); + return new Promise(resolve => { + window.requestAnimationFrame((frameTime) => { + if (frameTime < startTime) { + window.requestAnimationFrame(resolve); + } else { + resolve(); + } + }); + }); +} + // TODO(crbug.com/1400399): Deprecate as frame rates may vary greatly in // different test environments. function waitForAnimationEnd(getValue) { @@ -70,6 +161,10 @@ function waitForAnimationEnd(getValue) { } // Scrolls in target according to move_path with pauses in between +// The move_path should contains coordinates that are within target boundaries. +// Keep in mind that 0,0 is the center of the target element and is also +// the pointerDown position. +// pointerUp() is fired after sequence of moves. function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_in_ms = 100) { const test_driver_actions = new test_driver.Actions() .addPointer("pointer1", "touch") @@ -88,7 +183,7 @@ function touchScrollInTargetSequentiallyWithPause(target, move_path, pause_time_ y += step_y; test_driver_actions.pointerMove(x, y, {origin: target}); } - test_driver_actions.pause(pause_time_in_ms); + test_driver_actions.pause(pause_time_in_ms); // To prevent inertial scroll } return test_driver_actions.pointerUp().send(); @@ -125,7 +220,7 @@ function touchScrollInTarget(pixels_to_scroll, target, direction, pause_time_in_ // Trigger fling by doing pointerUp right after pointerMoves. function touchFlingInTarget(pixels_to_scroll, target, direction) { - touchScrollInTarget(pixels_to_scroll, target, direction, 0 /* pause_time */); + return touchScrollInTarget(pixels_to_scroll, target, direction, 0 /* pause_time */); } function mouseActionsInTarget(target, origin, delta, pause_time_in_ms = 100) { @@ -161,3 +256,23 @@ function conditionHolds(condition, error_message = 'Condition is not true anymor tick(0); }); } + +function scrollElementDown(element, scroll_amount) { + let x = 0; + let y = 0; + let delta_x = 0; + let delta_y = scroll_amount; + let actions = new test_driver.Actions() + .scroll(x, y, delta_x, delta_y, {origin: element}); + return actions.send(); +} + +function scrollElementLeft(element, scroll_amount) { + let x = 0; + let y = 0; + let delta_x = scroll_amount; + let delta_y = 0; + let actions = new test_driver.Actions() + .scroll(x, y, delta_x, delta_y, {origin: element}); + return actions.send(); +} diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-after-sequence-of-scrolls.tentative.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-after-sequence-of-scrolls.tentative.html index 77bf029ced58c5..dab6dcc9bd8d67 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-after-sequence-of-scrolls.tentative.html +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-after-sequence-of-scrolls.tentative.html @@ -14,7 +14,7 @@ } #innerDiv { - width: 500px; + width: 4000px; height: 4000px; } @@ -28,36 +28,64 @@ diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-mandatory-snap-point-after-load.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-mandatory-snap-point-after-load.html new file mode 100644 index 00000000000000..f3791134204497 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-mandatory-snap-point-after-load.html @@ -0,0 +1,87 @@ + + + + + + + + + + + + scrollend + mandatory scroll snap test + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html index c6569e0bebbd9f..449aea05351244 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html @@ -1,11 +1,19 @@ - + + + + + + + + + + + +
+
+
+
+ + diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html index 8782b1dfee6237..40aa77f4764b6c 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html @@ -108,10 +108,12 @@ document.body.appendChild(out_div); await waitForCompositorCommit(); - element_scrollend_arrived = false; - document_scrollend_arrived = false; inner_div.scrollIntoView({ inline: "end", block: "end", behavior: "auto" }); - await waitFor(() => { return element_scrollend_arrived || document_scrollend_arrived; }, "Nested scrollIntoView did not receive scrollend event."); + const scrollend_events = [ + waitForScrollendEventNoTimeout(out_div), + waitForScrollendEventNoTimeout(target_div) + ]; + await Promise.all(scrollend_events); assert_equals(root_element.scrollLeft, 0, "Nested scrollIntoView root_element scrollLeft"); assert_equals(root_element.scrollTop, 0, "Nested scrollIntoView root_element scrollTop"); assert_equals(out_div.scrollLeft, 100, "Nested scrollIntoView out_div scrollLeft"); diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-document.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-document.html index 30904553883435..797c2eb53da0e3 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-document.html +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-document.html @@ -7,64 +7,100 @@ -
-
+
+
-
+
+
diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-element-with-overscroll-behavior.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-element-with-overscroll-behavior.html index acad168e56c995..edda88e7cb2064 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-element-with-overscroll-behavior.html +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-element-with-overscroll-behavior.html @@ -7,96 +7,167 @@ -
-
-
-
-
-
-
-
+
+
+
+
-
+
+
+ +
+
+
+
+
+
diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-scrolled-element.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-scrolled-element.html deleted file mode 100644 index 734339694220cc..00000000000000 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-scrolled-element.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - -
-
-
-
- - - diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-window.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-window.html index ef72f56d2ba9d6..d2fd6f4d3158a5 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-window.html +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fired-to-window.html @@ -7,49 +7,63 @@ -
-
+
+
-
+
diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html new file mode 100644 index 00000000000000..99a281480ff11f --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + +
+ + + diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-inner-frame.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-inner-frame.html new file mode 100644 index 00000000000000..115e583c067ebd --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-inner-frame.html @@ -0,0 +1,30 @@ + + + + +
+
+
+
+ + diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-window.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-window.html new file mode 100644 index 00000000000000..9cd3b421fe9036 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-fires-to-iframe-window.html @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html index 5146c5f719a1e4..a06843a35e7ec9 100644 --- a/test/fixtures/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-event-for-user-scroll.html @@ -8,6 +8,7 @@ + + + +
+ + +
+
+ +
+ + + diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-fires-to-text-input.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-fires-to-text-input.html new file mode 100644 index 00000000000000..edc75d9121776e --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-fires-to-text-input.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-user-scroll-common.js b/test/fixtures/wpt/dom/events/scrolling/scrollend-user-scroll-common.js new file mode 100644 index 00000000000000..1b637233882843 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-user-scroll-common.js @@ -0,0 +1,150 @@ + +async function test_scrollend_on_touch_drag(t, target_div) { + // Skip the test on a Mac as they do not support touch screens. + const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + if (isMac) + return; + + await resetTargetScrollState(t, target_div); + await waitForCompositorReady(); + + const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div); + verifyNoScrollendOnDocument(t); + + let scrollend_count = 0; + const scrollend_listener = () => { + scrollend_count += 1; + }; + target_div.addEventListener("scrollend", scrollend_listener); + t.add_cleanup(() => { + target_div.removeEventListener('scrollend', scrollend_listener); + }); + + // Perform a touch drag on target div and wait for target_div to get + // a scrollend event. + await new test_driver.Actions() + .addPointer('TestPointer', 'touch') + .pointerMove(0, 0, { origin: target_div }) // 0, 0 is center of element. + .pointerDown() + .addTick() + .pointerMove(0, -40, { origin: target_div }) // Drag up to move down. + .addTick() + .pause(200) // Prevent inertial scroll. + .pointerMove(0, -60, { origin: target_div }) + .addTick() + .pause(200) // Prevent inertial scroll. + .pointerUp() + .send(); + + await targetScrollendPromise; + + assert_true(target_div.scrollTop > 0); + await verifyScrollStopped(t, target_div); + assert_equals(scrollend_count, 1); +} + +async function test_scrollend_on_scrollbar_gutter_click(t, target_div) { + // Skip test on platforms that do not have a visible scrollbar (e.g. + // overlay scrollbar). + const scrollbar_width = target_div.offsetWidth - target_div.clientWidth; + if (scrollbar_width == 0) + return; + + await resetTargetScrollState(t, target_div); + await waitForCompositorReady(); + + const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div); + verifyNoScrollendOnDocument(t); + + const bounds = target_div.getBoundingClientRect(); + // Some versions of webdriver have been known to frown at non-int arguments + // to pointerMove. + const x = Math.round(bounds.right - scrollbar_width / 2); + const y = Math.round(bounds.bottom - 20); + await new test_driver.Actions() + .addPointer('TestPointer', 'mouse') + .pointerMove(x, y, { origin: 'viewport' }) + .pointerDown() + .addTick() + .pointerUp() + .send(); + + await targetScrollendPromise; + assert_true(target_div.scrollTop > 0); + await verifyScrollStopped(t, target_div); +} + +// Same issue as previous test. +async function test_scrollend_on_scrollbar_thumb_drag(t, target_div) { + // Skip test on platforms that do not have a visible scrollbar (e.g. + // overlay scrollbar). + const scrollbar_width = target_div.offsetWidth - target_div.clientWidth; + if (scrollbar_width == 0) + return; + + await resetTargetScrollState(t, target_div); + await waitForCompositorReady(); + + const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div); + verifyNoScrollendOnDocument(t); + + const bounds = target_div.getBoundingClientRect(); + // Some versions of webdriver have been known to frown at non-int arguments + // to pointerMove. + const x = Math.round(bounds.right - scrollbar_width / 2); + const y = Math.round(bounds.top + 30); + const dy = 30; + await new test_driver.Actions() + .addPointer('TestPointer', 'mouse') + .pointerMove(x, y, { origin: 'viewport' }) + .pointerDown() + .pointerMove(x, y + dy, { origin: 'viewport' }) + .addTick() + .pointerUp() + .send(); + + await targetScrollendPromise; + assert_true(target_div.scrollTop > 0); + await verifyScrollStopped(t, target_div); +} + +async function test_scrollend_on_mousewheel_scroll(t, target_div, frame) { + await resetTargetScrollState(t, target_div); + await waitForCompositorReady(); + + const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div); + verifyNoScrollendOnDocument(t); + + let scroll_origin = target_div; + if (frame) { + // chromedriver doesn't support passing { origin: element } + // for an element within a subframe. Use the frame element itself. + scroll_origin = frame; + } + const x = 0; + const y = 0; + const dx = 0; + const dy = 40; + await new test_driver.Actions() + .scroll(x, y, dx, dy, { origin: scroll_origin }) + .send(); + + await targetScrollendPromise; + assert_true(target_div.scrollTop > 0); + await verifyScrollStopped(t, target_div); +} + +async function test_scrollend_on_keyboard_scroll(t, target_div) { + await resetTargetScrollState(t, target_div); + await waitForCompositorReady(); + + verifyNoScrollendOnDocument(t); + const targetScrollendPromise = waitForScrollendEventNoTimeout(target_div); + + target_div.focus(); + window.test_driver.send_keys(target_div, '\ue015'); + + await targetScrollendPromise; + assert_true(target_div.scrollTop > 0); + await verifyScrollStopped(t, target_div); +} diff --git a/test/fixtures/wpt/dom/events/scrolling/scrollend-with-snap-on-fractional-offset.html b/test/fixtures/wpt/dom/events/scrolling/scrollend-with-snap-on-fractional-offset.html new file mode 100644 index 00000000000000..d1f50304add2ac --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/scrollend-with-snap-on-fractional-offset.html @@ -0,0 +1,85 @@ + + + + + + + +
+
1
+
2
+
3
+
+ + + \ No newline at end of file diff --git a/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-basic.html b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-basic.html new file mode 100644 index 00000000000000..1b9df69ec77f88 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-basic.html @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ + + + diff --git a/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-multiple-action-chains.html b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-multiple-action-chains.html new file mode 100644 index 00000000000000..3b46b2f99a9c99 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-multiple-action-chains.html @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + +
+
+ + + + diff --git a/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-display-change.html b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-display-change.html new file mode 100644 index 00000000000000..6ca3c78b74cefe --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-display-change.html @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + +
+
+
+ + + + diff --git a/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-elements.html b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-elements.html new file mode 100644 index 00000000000000..0109b7b6b18b92 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-elements.html @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + +
X
+
+ + + + diff --git a/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-move.html b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-move.html new file mode 100644 index 00000000000000..a739d1cd5479ad --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-move.html @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + +
+
+
+ + + + diff --git a/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-removal.html b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-removal.html new file mode 100644 index 00000000000000..f81efd22e22b92 --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-removal.html @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + +
+
+
+ + + + diff --git a/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-resize.html b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-resize.html new file mode 100644 index 00000000000000..eb7431fca475db --- /dev/null +++ b/test/fixtures/wpt/dom/events/scrolling/wheel-event-transactions-target-resize.html @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + +
+
+
+ + + + diff --git a/test/fixtures/wpt/hr-time/raf-coarsened-time.https.html b/test/fixtures/wpt/hr-time/raf-coarsened-time.https.html new file mode 100644 index 00000000000000..182c707dc3b783 --- /dev/null +++ b/test/fixtures/wpt/hr-time/raf-coarsened-time.https.html @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/test/fixtures/wpt/hr-time/raf-coarsened-time.https.html.headers b/test/fixtures/wpt/hr-time/raf-coarsened-time.https.html.headers new file mode 100644 index 00000000000000..5f8621ef83660c --- /dev/null +++ b/test/fixtures/wpt/hr-time/raf-coarsened-time.https.html.headers @@ -0,0 +1,2 @@ +Cross-Origin-Embedder-Policy: require-corp +Cross-Origin-Opener-Policy: same-origin diff --git a/test/fixtures/wpt/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html b/test/fixtures/wpt/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html new file mode 100644 index 00000000000000..fa153f8f965d2b --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/microtask-queuing/queue-microtask-cross-realm-callback-report-exception.html @@ -0,0 +1,28 @@ + + +queueMicrotask() reports the exception from its callback in the callback's global object + + + + + + diff --git a/test/fixtures/wpt/html/webappapis/structured-clone/WEB_FEATURES.yml b/test/fixtures/wpt/html/webappapis/structured-clone/WEB_FEATURES.yml new file mode 100644 index 00000000000000..423a469bf17283 --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/structured-clone/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: structured-clone + files: "**" diff --git a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js index 660477dca4c621..00a86fa74b9a96 100644 --- a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js +++ b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js @@ -9,6 +9,7 @@ * - `postTest()`: An optional, async function run after a test is done * - `structuredClone(obj, transferList)`: Required function that somehow * structurally clones an object. + * Must return a promise. * - `hasDocument`: When true, disables tests that require a document. True by default. */ @@ -30,11 +31,11 @@ function runStructuredCloneBatteryOfTests(runner) { } return new Promise(resolve => { - promise_test(async _ => { + promise_test(async t => { test = await test; await setupPromise; await runner.preTest(test); - await test.f(runner) + await test.f(runner, t) await runner.postTest(test); resolve(); }, test.description); diff --git a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js index 744f1168196001..23cf4f651ac70a 100644 --- a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js +++ b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js @@ -20,3 +20,150 @@ structuredCloneBatteryOfTests.push({ }); // TODO: ImageBitmap + +structuredCloneBatteryOfTests.push({ + description: 'A detached ArrayBuffer cannot be transferred', + async f(runner, t) { + const buffer = new ArrayBuffer(); + await runner.structuredClone(buffer, [buffer]); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(buffer, [buffer]) + ); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'A detached platform object cannot be transferred', + async f(runner, t) { + const {port1} = new MessageChannel(); + await runner.structuredClone(port1, [port1]); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(port1, [port1]) + ); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'Transferring a non-transferable platform object fails', + async f(runner, t) { + const blob = new Blob(); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(blob, [blob]) + ); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'An object whose interface is deleted from the global object must still be received', + async f(runner) { + const {port1} = new MessageChannel(); + const messagePortInterface = globalThis.MessagePort; + delete globalThis.MessagePort; + try { + const transfer = await runner.structuredClone(port1, [port1]); + assert_true(transfer instanceof messagePortInterface); + } finally { + globalThis.MessagePort = messagePortInterface; + } + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'A subclass instance will be received as its closest transferable superclass', + async f(runner) { + // MessagePort doesn't have a constructor, so we must use something else. + + // Make sure that ReadableStream is transferable before we test its subclasses. + try { + const stream = new ReadableStream(); + await runner.structuredClone(stream, [stream]); + } catch(err) { + if (err instanceof DOMException && err.code === DOMException.DATA_CLONE_ERR) { + throw new OptionalFeatureUnsupportedError("ReadableStream isn't transferable"); + } else { + throw err; + } + } + + class ReadableStreamSubclass extends ReadableStream {} + const original = new ReadableStreamSubclass(); + const transfer = await runner.structuredClone(original, [original]); + assert_equals(Object.getPrototypeOf(transfer), ReadableStream.prototype); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'Resizable ArrayBuffer is transferable', + async f(runner) { + const buffer = new ArrayBuffer(16, { maxByteLength: 1024 }); + const copy = await runner.structuredClone(buffer, [buffer]); + assert_equals(buffer.byteLength, 0); + assert_equals(copy.byteLength, 16); + assert_equals(copy.maxByteLength, 1024); + assert_true(copy.resizable); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'Length-tracking TypedArray is transferable', + async f(runner) { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + const ta = new Uint8Array(ab); + const copy = await runner.structuredClone(ta, [ab]); + assert_equals(ab.byteLength, 0); + assert_equals(copy.buffer.byteLength, 16); + assert_equals(copy.buffer.maxByteLength, 1024); + assert_true(copy.buffer.resizable); + copy.buffer.resize(32); + assert_equals(copy.byteLength, 32); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'Length-tracking DataView is transferable', + async f(runner) { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + const dv = new DataView(ab); + const copy = await runner.structuredClone(dv, [ab]); + assert_equals(ab.byteLength, 0); + assert_equals(copy.buffer.byteLength, 16); + assert_equals(copy.buffer.maxByteLength, 1024); + assert_true(copy.buffer.resizable); + copy.buffer.resize(32); + assert_equals(copy.byteLength, 32); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'Transferring OOB TypedArray throws', + async f(runner, t) { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + const ta = new Uint8Array(ab, 8); + ab.resize(0); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(ta, [ab]) + ); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'Transferring OOB DataView throws', + async f(runner, t) { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + const dv = new DataView(ab, 8); + ab.resize(0); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(dv, [ab]) + ); + } +}); diff --git a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests.js b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests.js index 0c96ded2088357..923ac9dc164918 100644 --- a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests.js +++ b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-battery-of-tests.js @@ -3,11 +3,6 @@ structuredCloneBatteryOfTests = []; function check(description, input, callback, requiresDocument = false) { - testObjMock = { - done() {}, - step_func(f) {return _ => f()}, - }; - structuredCloneBatteryOfTests.push({ description, async f(runner) { @@ -16,47 +11,41 @@ function check(description, input, callback, requiresDocument = false) { newInput = input(); } const copy = await runner.structuredClone(newInput); - await callback(copy, newInput, testObjMock); + await callback(copy, newInput); }, requiresDocument }); } -function compare_primitive(actual, input, test_obj) { +function compare_primitive(actual, input) { assert_equals(actual, input); - if (test_obj) - test_obj.done(); } -function compare_Array(callback, callback_is_async) { - return function(actual, input, test_obj) { +function compare_Array(callback) { + return async function(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof Array, 'instanceof Array'); assert_not_equals(actual, input); assert_equals(actual.length, input.length, 'length'); - callback(actual, input); - if (test_obj && !callback_is_async) - test_obj.done(); + await callback(actual, input); } } -function compare_Object(callback, callback_is_async) { - return function(actual, input, test_obj) { +function compare_Object(callback) { + return async function(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof Object, 'instanceof Object'); assert_false(actual instanceof Array, 'instanceof Array'); assert_not_equals(actual, input); - callback(actual, input); - if (test_obj && !callback_is_async) - test_obj.done(); + await callback(actual, input); } } -function enumerate_props(compare_func, test_obj) { - return function(actual, input) { +function enumerate_props(compare_func) { + return async function(actual, input) { for (const x in input) { - compare_func(actual[x], input[x], test_obj); + await compare_func(actual[x], input[x]); } }; } @@ -127,14 +116,12 @@ check('Object primitives', {'undefined':undefined, '9007199254740994':9007199254740994, '-9007199254740994':-9007199254740994}, compare_Object(enumerate_props(compare_primitive))); -function compare_Boolean(actual, input, test_obj) { +function compare_Boolean(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof Boolean, 'instanceof Boolean'); assert_equals(String(actual), String(input), 'converted to primitive'); assert_not_equals(actual, input); - if (test_obj) - test_obj.done(); } check('Boolean true', new Boolean(true), compare_Boolean); check('Boolean false', new Boolean(false), compare_Boolean); @@ -143,14 +130,12 @@ check('Object Boolean objects', {'true':new Boolean(true), 'false':new Boolean(f function compare_obj(what) { const Type = self[what]; - return function(actual, input, test_obj) { + return function(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof Type, 'instanceof '+what); assert_equals(Type(actual), Type(input), 'converted to primitive'); assert_not_equals(actual, input); - if (test_obj) - test_obj.done(); }; } check('String empty string', new String(''), compare_obj('String')); @@ -203,14 +188,12 @@ check('Object Number objects', {'0.2':new Number(0.2), '9007199254740994':new Number(9007199254740994), '-9007199254740994':new Number(-9007199254740994)}, compare_Object(enumerate_props(compare_obj('Number')))); -function compare_Date(actual, input, test_obj) { +function compare_Date(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof Date, 'instanceof Date'); assert_equals(Number(actual), Number(input), 'converted to primitive'); assert_not_equals(actual, input); - if (test_obj) - test_obj.done(); } check('Date 0', new Date(0), compare_Date); check('Date -0', new Date(-0), compare_Date); @@ -227,7 +210,7 @@ check('Object Date objects', {'0':new Date(0), function compare_RegExp(expected_source) { // XXX ES6 spec doesn't define exact serialization for `source` (it allows several ways to escape) - return function(actual, input, test_obj) { + return function(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof RegExp, 'instanceof RegExp'); @@ -239,8 +222,6 @@ function compare_RegExp(expected_source) { assert_equals(actual.unicode, input.unicode, 'unicode'); assert_equals(actual.lastIndex, 0, 'lastIndex'); assert_not_equals(actual, input); - if (test_obj) - test_obj.done(); } } function func_RegExp_flags_lastIndex() { @@ -273,7 +254,28 @@ check('Object RegExp object, RegExp empty', {'x':new RegExp('')}, compare_Object check('Object RegExp object, RegExp slash', {'x':new RegExp('/')}, compare_Object(enumerate_props(compare_RegExp('\\/')))); check('Object RegExp object, RegExp new line', {'x':new RegExp('\n')}, compare_Object(enumerate_props(compare_RegExp('\\n')))); -async function compare_Blob(actual, input, test_obj, expect_File) { +function compare_Error(actual, input) { + assert_true(actual instanceof Error, "Checking instanceof"); + assert_equals(actual.constructor, input.constructor, "Checking constructor"); + assert_equals(actual.name, input.name, "Checking name"); + assert_equals(actual.hasOwnProperty("message"), input.hasOwnProperty("message"), "Checking message existence"); + assert_equals(actual.message, input.message, "Checking message"); + assert_equals(actual.foo, undefined, "Checking for absence of custom property"); +} + +check('Empty Error object', new Error, compare_Error); + +const errorConstructors = [Error, EvalError, RangeError, ReferenceError, + SyntaxError, TypeError, URIError]; +for (const constructor of errorConstructors) { + check(`${constructor.name} object`, () => { + let error = new constructor("Error message here"); + error.foo = "testing"; + return error; + }, compare_Error); +} + +async function compare_Blob(actual, input, expect_File) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof Blob, 'instanceof Blob'); @@ -333,33 +335,33 @@ function func_Blob_NUL() { } check('Blob NUL', func_Blob_NUL, compare_Blob); -check('Array Blob object, Blob basic', [func_Blob_basic()], compare_Array(enumerate_props(compare_Blob), true)); -check('Array Blob object, Blob unpaired high surrogate (invalid utf-8)', [func_Blob_bytes([0xD800])()], compare_Array(enumerate_props(compare_Blob), true)); -check('Array Blob object, Blob unpaired low surrogate (invalid utf-8)', [func_Blob_bytes([0xDC00])()], compare_Array(enumerate_props(compare_Blob), true)); -check('Array Blob object, Blob paired surrogates (invalid utf-8)', [func_Blob_bytes([0xD800, 0xDC00])()], compare_Array(enumerate_props(compare_Blob), true)); -check('Array Blob object, Blob empty', [func_Blob_empty()], compare_Array(enumerate_props(compare_Blob), true)); -check('Array Blob object, Blob NUL', [func_Blob_NUL()], compare_Array(enumerate_props(compare_Blob), true)); -check('Array Blob object, two Blobs', [func_Blob_basic(), func_Blob_empty()], compare_Array(enumerate_props(compare_Blob), true)); - -check('Object Blob object, Blob basic', {'x':func_Blob_basic()}, compare_Object(enumerate_props(compare_Blob), true)); -check('Object Blob object, Blob unpaired high surrogate (invalid utf-8)', {'x':func_Blob_bytes([0xD800])()}, compare_Object(enumerate_props(compare_Blob), true)); -check('Object Blob object, Blob unpaired low surrogate (invalid utf-8)', {'x':func_Blob_bytes([0xDC00])()}, compare_Object(enumerate_props(compare_Blob), true)); -check('Object Blob object, Blob paired surrogates (invalid utf-8)', {'x':func_Blob_bytes([0xD800, 0xDC00])() }, compare_Object(enumerate_props(compare_Blob), true)); -check('Object Blob object, Blob empty', {'x':func_Blob_empty()}, compare_Object(enumerate_props(compare_Blob), true)); -check('Object Blob object, Blob NUL', {'x':func_Blob_NUL()}, compare_Object(enumerate_props(compare_Blob), true)); - -function compare_File(actual, input, test_obj) { +check('Array Blob object, Blob basic', [func_Blob_basic()], compare_Array(enumerate_props(compare_Blob))); +check('Array Blob object, Blob unpaired high surrogate (invalid utf-8)', [func_Blob_bytes([0xD800])()], compare_Array(enumerate_props(compare_Blob))); +check('Array Blob object, Blob unpaired low surrogate (invalid utf-8)', [func_Blob_bytes([0xDC00])()], compare_Array(enumerate_props(compare_Blob))); +check('Array Blob object, Blob paired surrogates (invalid utf-8)', [func_Blob_bytes([0xD800, 0xDC00])()], compare_Array(enumerate_props(compare_Blob))); +check('Array Blob object, Blob empty', [func_Blob_empty()], compare_Array(enumerate_props(compare_Blob))); +check('Array Blob object, Blob NUL', [func_Blob_NUL()], compare_Array(enumerate_props(compare_Blob))); +check('Array Blob object, two Blobs', [func_Blob_basic(), func_Blob_empty()], compare_Array(enumerate_props(compare_Blob))); + +check('Object Blob object, Blob basic', {'x':func_Blob_basic()}, compare_Object(enumerate_props(compare_Blob))); +check('Object Blob object, Blob unpaired high surrogate (invalid utf-8)', {'x':func_Blob_bytes([0xD800])()}, compare_Object(enumerate_props(compare_Blob))); +check('Object Blob object, Blob unpaired low surrogate (invalid utf-8)', {'x':func_Blob_bytes([0xDC00])()}, compare_Object(enumerate_props(compare_Blob))); +check('Object Blob object, Blob paired surrogates (invalid utf-8)', {'x':func_Blob_bytes([0xD800, 0xDC00])() }, compare_Object(enumerate_props(compare_Blob))); +check('Object Blob object, Blob empty', {'x':func_Blob_empty()}, compare_Object(enumerate_props(compare_Blob))); +check('Object Blob object, Blob NUL', {'x':func_Blob_NUL()}, compare_Object(enumerate_props(compare_Blob))); + +async function compare_File(actual, input) { assert_true(actual instanceof File, 'instanceof File'); assert_equals(actual.name, input.name, 'name'); assert_equals(actual.lastModified, input.lastModified, 'lastModified'); - compare_Blob(actual, input, test_obj, true); + await compare_Blob(actual, input, true); } function func_File_basic() { return new File(['foo'], 'bar', {type:'text/x-bar', lastModified:42}); } check('File basic', func_File_basic, compare_File); -function compare_FileList(actual, input, test_obj) { +function compare_FileList(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof FileList, 'instanceof FileList'); @@ -367,8 +369,6 @@ function compare_FileList(actual, input, test_obj) { assert_not_equals(actual, input); // XXX when there's a way to populate or construct a FileList, // check the items in the FileList - if (test_obj) - test_obj.done(); } function func_FileList_empty() { const input = document.createElement('input'); @@ -379,30 +379,36 @@ check('FileList empty', func_FileList_empty, compare_FileList, true); check('Array FileList object, FileList empty', () => ([func_FileList_empty()]), compare_Array(enumerate_props(compare_FileList)), true); check('Object FileList object, FileList empty', () => ({'x':func_FileList_empty()}), compare_Object(enumerate_props(compare_FileList)), true); +function compare_ArrayBuffer(actual, input) { + assert_true(actual instanceof ArrayBuffer, 'instanceof ArrayBuffer'); + assert_equals(actual.byteLength, input.byteLength, 'byteLength'); + assert_equals(actual.maxByteLength, input.maxByteLength, 'maxByteLength'); + assert_equals(actual.resizable, input.resizable, 'resizable'); + assert_equals(actual.growable, input.growable, 'growable'); +} + function compare_ArrayBufferView(view) { const Type = self[view]; - return function(actual, input, test_obj) { + return function(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_true(actual instanceof Type, 'instanceof '+view); assert_equals(actual.length, input.length, 'length'); + assert_equals(actual.byteLength, input.byteLength, 'byteLength'); + assert_equals(actual.byteOffset, input.byteOffset, 'byteOffset'); assert_not_equals(actual.buffer, input.buffer, 'buffer'); for (let i = 0; i < actual.length; ++i) { assert_equals(actual[i], input[i], 'actual['+i+']'); } - if (test_obj) - test_obj.done(); }; } -function compare_ImageData(actual, input, test_obj) { +function compare_ImageData(actual, input) { if (typeof actual === 'string') assert_unreached(actual); assert_equals(actual.width, input.width, 'width'); assert_equals(actual.height, input.height, 'height'); assert_not_equals(actual.data, input.data, 'data'); compare_ArrayBufferView('Uint8ClampedArray')(actual.data, input.data, null); - if (test_obj) - test_obj.done(); } function func_ImageData_1x1_transparent_black() { const canvas = document.createElement('canvas'); @@ -506,6 +512,23 @@ check('Object with non-configurable property', function() { return rv; }, compare_Object(check_configurable_property('foo'))); +structuredCloneBatteryOfTests.push({ + description: 'Object with a getter that throws', + async f(runner, t) { + const exception = new Error(); + const testObject = { + get testProperty() { + throw exception; + } + }; + await promise_rejects_exactly( + t, + exception, + runner.structuredClone(testObject) + ); + } +}); + /* The tests below are inspired by @zcorpan’s work but got some more substantial changed due to their previous async setup */ @@ -616,3 +639,115 @@ check('ObjectPrototype must lose its exotic-ness when cloned', assert_equals(Object.getPrototypeOf(copy), newProto); } ); + +structuredCloneBatteryOfTests.push({ + description: 'Serializing a non-serializable platform object fails', + async f(runner, t) { + const request = new Response(); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(request) + ); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'An object whose interface is deleted from the global must still deserialize', + async f(runner) { + const blob = new Blob(); + const blobInterface = globalThis.Blob; + delete globalThis.Blob; + try { + const copy = await runner.structuredClone(blob); + assert_true(copy instanceof blobInterface); + } finally { + globalThis.Blob = blobInterface; + } + } +}); + +check( + 'A subclass instance will deserialize as its closest serializable superclass', + () => { + class FileSubclass extends File {} + return new FileSubclass([], ""); + }, + (copy) => { + assert_equals(Object.getPrototypeOf(copy), File.prototype); + } +); + +check( + 'Resizable ArrayBuffer', + () => { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + assert_true(ab.resizable); + return ab; + }, + compare_ArrayBuffer); + +structuredCloneBatteryOfTests.push({ + description: 'Growable SharedArrayBuffer', + async f(runner) { + const sab = createBuffer('SharedArrayBuffer', 16, { maxByteLength: 1024 }); + assert_true(sab.growable); + try { + const copy = await runner.structuredClone(sab); + compare_ArrayBuffer(sab, copy); + } catch (e) { + // If we're cross-origin isolated, cloning SABs should not fail. + if (e instanceof DOMException && e.code === DOMException.DATA_CLONE_ERR) { + assert_false(self.crossOriginIsolated); + } else { + throw e; + } + } + } +}); + +check( + 'Length-tracking TypedArray', + () => { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + assert_true(ab.resizable); + return new Uint8Array(ab); + }, + compare_ArrayBufferView('Uint8Array')); + +check( + 'Length-tracking DataView', + () => { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + assert_true(ab.resizable); + return new DataView(ab); + }, + compare_ArrayBufferView('DataView')); + +structuredCloneBatteryOfTests.push({ + description: 'Serializing OOB TypedArray throws', + async f(runner, t) { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + const ta = new Uint8Array(ab, 8); + ab.resize(0); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(ta) + ); + } +}); + +structuredCloneBatteryOfTests.push({ + description: 'Serializing OOB DataView throws', + async f(runner, t) { + const ab = new ArrayBuffer(16, { maxByteLength: 1024 }); + const dv = new DataView(ab, 8); + ab.resize(0); + await promise_rejects_dom( + t, + "DataCloneError", + runner.structuredClone(dv) + ); + } +}); diff --git a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method.html b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method.html new file mode 100644 index 00000000000000..d80010e0df78ad --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-cross-realm-method.html @@ -0,0 +1,20 @@ + +self.structuredClone() uses this's relevant Realm for deserialization + + + + + + diff --git a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-detached-window-crash.html b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-detached-window-crash.html new file mode 100644 index 00000000000000..75971023d2d79f --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone-detached-window-crash.html @@ -0,0 +1,11 @@ + +window.structuredClone() doesn't crash when window is detached + + + diff --git a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone.any.js b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone.any.js index 34f96f33fdf881..1358a71fc03d4e 100644 --- a/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone.any.js +++ b/test/fixtures/wpt/html/webappapis/structured-clone/structured-clone.any.js @@ -1,9 +1,14 @@ // META: title=structuredClone() tests +// META: script=/common/sab.js // META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests.js // META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-with-transferables.js // META: script=/html/webappapis/structured-clone/structured-clone-battery-of-tests-harness.js runStructuredCloneBatteryOfTests({ - structuredClone: (obj, transfer) => self.structuredClone(obj, { transfer }), + structuredClone: (obj, transfer) => { + return new Promise(resolve => { + resolve(self.structuredClone(obj, { transfer })); + }); + }, hasDocument: typeof document !== "undefined", }); diff --git a/test/fixtures/wpt/html/webappapis/timers/clearinterval-from-callback.any.js b/test/fixtures/wpt/html/webappapis/timers/clearinterval-from-callback.any.js new file mode 100644 index 00000000000000..bf4eb7cf5ac897 --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/timers/clearinterval-from-callback.any.js @@ -0,0 +1,19 @@ +async_test((t) => { + let wasPreviouslyCalled = false; + + const handle = setInterval( + t.step_func(() => { + if (!wasPreviouslyCalled) { + wasPreviouslyCalled = true; + + clearInterval(handle); + + // Make the test succeed after the callback would've run next. + setInterval(t.step_func_done(), 750); + } else { + assert_unreached(); + } + }), + 500 + ); +}, "Clearing an interval from the callback should still clear it."); diff --git a/test/fixtures/wpt/html/webappapis/timers/evil-spec-example.any.js b/test/fixtures/wpt/html/webappapis/timers/evil-spec-example.any.js new file mode 100644 index 00000000000000..17215e218a9e26 --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/timers/evil-spec-example.any.js @@ -0,0 +1,12 @@ +var t = async_test("Interaction of setTimeout and WebIDL") +function finishTest() { + assert_equals(log, "ONE TWO ") + t.done() +} +var log = ''; +function logger(s) { log += s + ' '; } + +setTimeout({ toString: function () { + setTimeout("logger('ONE')", 100); + return "logger('TWO'); t.step(finishTest)"; +} }, 100); diff --git a/test/fixtures/wpt/html/webappapis/timers/evil-spec-example.html b/test/fixtures/wpt/html/webappapis/timers/evil-spec-example.html deleted file mode 100644 index 77a8746908d742..00000000000000 --- a/test/fixtures/wpt/html/webappapis/timers/evil-spec-example.html +++ /dev/null @@ -1,23 +0,0 @@ - -Interaction of setTimeout and WebIDL - - - - - - -
- diff --git a/test/fixtures/wpt/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html b/test/fixtures/wpt/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html new file mode 100644 index 00000000000000..4a780fc93291b1 --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/timers/setinterval-cross-realm-callback-report-exception.html @@ -0,0 +1,32 @@ + + +window.setInterval() reports the exception from its callback in the callback's global object + + + + + + diff --git a/test/fixtures/wpt/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html b/test/fixtures/wpt/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html new file mode 100644 index 00000000000000..b4860151a6c39f --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/timers/settimeout-cross-realm-callback-report-exception.html @@ -0,0 +1,28 @@ + + +window.setTimeout() reports the exception from its callback in the callback's global object + + + + + + diff --git a/test/fixtures/wpt/interfaces/dom.idl b/test/fixtures/wpt/interfaces/dom.idl index 72d61f5cfd80ad..99192924f4f46f 100644 --- a/test/fixtures/wpt/interfaces/dom.idl +++ b/test/fixtures/wpt/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 TrustedScript or DOMString)... nodes); - [CEReactions, Unscopable] undefined append((Node or TrustedScript or DOMString)... nodes); - [CEReactions, Unscopable] undefined replaceChildren((Node or TrustedScript or DOMString)... nodes); + [CEReactions, Unscopable] undefined prepend((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined append((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined replaceChildren((Node 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 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 before((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined after((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined replaceWith((Node or DOMString)... nodes); [CEReactions, Unscopable] undefined remove(); }; DocumentType includes ChildNode; diff --git a/test/fixtures/wpt/interfaces/html.idl b/test/fixtures/wpt/interfaces/html.idl index 4d6c0229bc1392..25e1f8a4ca058e 100644 --- a/test/fixtures/wpt/interfaces/html.idl +++ b/test/fixtures/wpt/interfaces/html.idl @@ -125,6 +125,7 @@ interface HTMLElement : Element { [CEReactions] attribute boolean spellcheck; [CEReactions] attribute DOMString writingSuggestions; [CEReactions] attribute DOMString autocapitalize; + [CEReactions] attribute boolean autocorrect; [CEReactions] attribute [LegacyNullToEmptyString] DOMString innerText; [CEReactions] attribute [LegacyNullToEmptyString] DOMString outerText; @@ -1411,8 +1412,6 @@ interface mixin CanvasDrawPath { interface mixin CanvasUserInterface { undefined drawFocusIfNeeded(Element element); undefined drawFocusIfNeeded(Path2D path, Element element); - undefined scrollPathIntoView(); - undefined scrollPathIntoView(Path2D path); }; interface mixin CanvasText { diff --git a/test/fixtures/wpt/performance-timeline/back-forward-cache-restoration.tentative.html b/test/fixtures/wpt/performance-timeline/back-forward-cache-restoration.tentative.html new file mode 100644 index 00000000000000..c673e09cb461e0 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/back-forward-cache-restoration.tentative.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/droppedentriescount.any.js b/test/fixtures/wpt/performance-timeline/droppedentriescount.any.js new file mode 100644 index 00000000000000..4de816bdc42bd8 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/droppedentriescount.any.js @@ -0,0 +1,81 @@ +promise_test(t => { + // This setup is required for later tests as well. + // Await for a dropped entry. + return new Promise(res => { + // Set a buffer size of 0 so that new resource entries count as dropped. + performance.setResourceTimingBufferSize(0); + // Use an observer to make sure the promise is resolved only when the + // new entry has been created. + new PerformanceObserver(res).observe({type: 'resource'}); + fetch('resources/square.png?id=1'); + }).then(() => { + return new Promise(resolve => { + new PerformanceObserver(t.step_func((entries, obs, options) => { + assert_equals(options['droppedEntriesCount'], 0); + resolve(); + })).observe({type: 'mark'}); + performance.mark('test'); + })}); +}, 'Dropped entries count is 0 when there are no dropped entries of relevant type.'); + +promise_test(async t => { + return new Promise(resolve => { + new PerformanceObserver(t.step_func((entries, obs, options) => { + assert_equals(options['droppedEntriesCount'], 1); + resolve(); + })).observe({entryTypes: ['mark', 'resource']}); + performance.mark('meow'); + }); +}, 'Dropped entries correctly counted with multiple types.'); + +promise_test(t => { + return new Promise(resolve => { + new PerformanceObserver(t.step_func((entries, obs, options) => { + assert_equals(options['droppedEntriesCount'], 1, + 'There should have been some dropped resource timing entries at this point'); + resolve(); + })).observe({type: 'resource', buffered: true}); + }); +}, 'Dropped entries counted even if observer was not registered at the time.'); + +promise_test(t => { + return new Promise(resolve => { + let callback_ran = false; + new PerformanceObserver(t.step_func((entries, obs, options) => { + if (!callback_ran) { + assert_equals(options['droppedEntriesCount'], 2, + 'There should be two dropped entries right now.'); + fetch('resources/square.png?id=3'); + callback_ran = true; + } else { + assert_equals(options['droppedEntriesCount'], undefined, + 'droppedEntriesCount should be unset after the first callback!'); + resolve(); + } + })).observe({type: 'resource'}); + fetch('resources/square.png?id=2'); + }); +}, 'Dropped entries only surfaced on the first callback.'); + + +promise_test(t => { + return new Promise(resolve => { + let callback_ran = false; + let droppedEntriesCount = -1; + new PerformanceObserver(t.step_func((entries, obs, options) => { + if (!callback_ran) { + assert_greater_than(options['droppedEntriesCount'], 0, + 'There should be several dropped entries right now.'); + droppedEntriesCount = options['droppedEntriesCount']; + callback_ran = true; + obs.observe({type: 'mark'}); + performance.mark('woof'); + } else { + assert_equals(options['droppedEntriesCount'], droppedEntriesCount, + 'There should be droppedEntriesCount due to the new observe().'); + resolve(); + } + })).observe({type: 'resource'}); + fetch('resources/square.png?id=4'); + }); +}, 'Dropped entries surfaced after an observe() call!'); diff --git a/test/fixtures/wpt/performance-timeline/idlharness-shadowrealm.window.js b/test/fixtures/wpt/performance-timeline/idlharness-shadowrealm.window.js new file mode 100644 index 00000000000000..6caaa3306132bd --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/idlharness-shadowrealm.window.js @@ -0,0 +1,2 @@ +// META: script=/resources/idlharness-shadowrealm.js +idl_test_shadowrealm(["performance-timeline"], ["hr-time", "dom"]); diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-detached-frame.tentative.html b/test/fixtures/wpt/performance-timeline/navigation-id-detached-frame.tentative.html new file mode 100644 index 00000000000000..add11255af45c0 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-detached-frame.tentative.html @@ -0,0 +1,30 @@ + + + + + + The navigation_id Detached iframe Parent Page. + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-element-timing.tentative.html b/test/fixtures/wpt/performance-timeline/navigation-id-element-timing.tentative.html new file mode 100644 index 00000000000000..7ff415530bc69d --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-element-timing.tentative.html @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-initial-load.tentative.html b/test/fixtures/wpt/performance-timeline/navigation-id-initial-load.tentative.html new file mode 100644 index 00000000000000..b996f0f117922d --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-initial-load.tentative.html @@ -0,0 +1,49 @@ + + + + + + + +

This text is to trigger a LCP entry emission.

+ + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-long-task-task-attribution.tentative.html b/test/fixtures/wpt/performance-timeline/navigation-id-long-task-task-attribution.tentative.html new file mode 100644 index 00000000000000..e1da9100aeee55 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-long-task-task-attribution.tentative.html @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-mark-measure.tentative.html b/test/fixtures/wpt/performance-timeline/navigation-id-mark-measure.tentative.html new file mode 100644 index 00000000000000..30613ebb980eb0 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-mark-measure.tentative.html @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-reset.tentative.html b/test/fixtures/wpt/performance-timeline/navigation-id-reset.tentative.html new file mode 100644 index 00000000000000..f5a2428e5f568a --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-reset.tentative.html @@ -0,0 +1,53 @@ + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-resource-timing.tentative.html b/test/fixtures/wpt/performance-timeline/navigation-id-resource-timing.tentative.html new file mode 100644 index 00000000000000..6d0614a6e23940 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-resource-timing.tentative.html @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/navigation-id-worker-created-entries.html b/test/fixtures/wpt/performance-timeline/navigation-id-worker-created-entries.html new file mode 100644 index 00000000000000..96fc57be1d426c --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id-worker-created-entries.html @@ -0,0 +1,27 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/navigation-id.helper.js b/test/fixtures/wpt/performance-timeline/navigation-id.helper.js new file mode 100644 index 00000000000000..1b72fe9908d573 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/navigation-id.helper.js @@ -0,0 +1,144 @@ +// The test functions called in the navigation-counter test. They rely on +// artifacts defined in +// '/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js' +// which should be included before this file to use these functions. + +// This function is to obtain navigation ids of all performance entries to +// verify. +let testInitial = () => { + return window.performance.getEntries().map(e => e.navigationId); +} + +let testMarkMeasure = (markId, markName, MeasureName) => { + const markName1 = 'test-mark'; + const markName2 = 'test-mark' + markId; + const measureName = 'test-measure' + markId; + + window.performance.mark(markName1); + window.performance.mark(markName2); + window.performance.measure(measureName, markName1, markName2); + return window.performance.getEntriesByName(markName2).concat( + window.performance.getEntriesByName(measureName)).map(e => e.navigationId); +} + +let testResourceTiming = async (resourceTimingEntryId) => { + let navigationId; + + let p = new Promise(resolve => { + new PerformanceObserver((list) => { + const entry = list.getEntries().find( + e => e.name.includes('json_resource' + resourceTimingEntryId)); + if (entry) { + navigationId = entry.navigationId; + resolve(); + } + }).observe({ type: 'resource' }); + }); + + const resp = await fetch( + '/performance-timeline/resources/json_resource' + resourceTimingEntryId + '.json'); + await p; + return [navigationId]; +} + +let testElementTiming = async (elementTimingEntryId) => { + let navigationId; + let p = new Promise(resolve => { + new PerformanceObserver((list) => { + const entry = list.getEntries().find( + e => e.entryType === 'element' && e.identifier === 'test-element-timing' + elementTimingEntryId); + if (entry) { + navigationId = entry.navigationId; + resolve(); + } + }).observe({ type: 'element' }); + }); + + let el = document.createElement('p'); + el.setAttribute('elementtiming', 'test-element-timing' + elementTimingEntryId); + el.textContent = 'test element timing text'; + document.body.appendChild(el); + await p; + return [navigationId]; +} + +let testLongTask = async () => { + let navigationIds = []; + + let p = new Promise(resolve => { + new PerformanceObserver((list) => { + const entry = list.getEntries().find(e => e.entryType === 'longtask') + if (entry) { + navigationIds.push(entry.navigationId); + navigationIds = navigationIds.concat( + entry.attribution.map(a => a.navigationId)); + resolve(); + } + }).observe({ type: 'longtask' }); + }); + + const script = document.createElement('script'); + script.src = '/performance-timeline/resources/make_long_task.js'; + document.body.appendChild(script); + await p; + document.body.removeChild(script); + return navigationIds; +} + +const testFunctionMap = { + 'mark_measure': testMarkMeasure, + 'resource_timing': testResourceTiming, + 'element_timing': testElementTiming, + 'long_task_task_attribution': testLongTask, +}; + +function runNavigationIdTest(params, description) { + const defaultParams = { + openFunc: url => window.open(url, '_blank', 'noopener'), + scripts: [], + funcBeforeNavigation: () => { }, + targetOrigin: originCrossSite, + navigationTimes: 4, + funcAfterAssertion: () => { }, + } // Apply defaults. + params = { ...defaultParams, ...params }; + + promise_test(async t => { + const pageA = new RemoteContext(token()); + const pageB = new RemoteContext(token()); + + const urlA = executorPath + pageA.context_id; + const urlB = params.targetOrigin + executorPath + pageB.context_id; + // Open url A. + params.openFunc(urlA); + await pageA.execute_script(waitForPageShow); + + // Assert navigation ids of all performance entries are the same. + let navigationIds = await pageA.execute_script(testInitial); + assert_true( + navigationIds.every(t => t === navigationIds[0]), + 'Navigation Ids should be the same as the initial load.'); + + for (i = 1; i <= params.navigationTimes; i++) { + // Navigate away to url B and back. + await navigateAndThenBack(pageA, pageB, urlB); + + // Assert new navigation ids are generated when the document is load from bfcache. + let nextNavigationIds = await pageA.execute_script( + testFunctionMap[params.testName], [i + 1]); + + // Assert navigation ids of all performance entries are the same. + assert_true( + nextNavigationIds.every(t => t === nextNavigationIds[0]), + 'All Navigation Ids should be same after bfcache navigation.'); + + // Assert navigation ids after bfcache navigation are different from those before. + assert_true( + navigationIds[0] !== nextNavigationIds[0], + params.testName + + ' Navigation Ids should be re-generated and different from the previous ones.'); + + navigationIds = nextNavigationIds; + } + }, description); +} \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/abort-block-bfcache.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/abort-block-bfcache.window.js new file mode 100644 index 00000000000000..e5dbb0f43c8dd3 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/abort-block-bfcache.window.js @@ -0,0 +1,21 @@ +// META: title=Aborting a parser should block bfcache +// META: script=./test-helper.js +// META: timeout=long + + +async_test(t => { + if (!sessionStorage.getItem("pageVisited")) { + // This is the first time loading the page. + sessionStorage.setItem("pageVisited", 1); + t.step_timeout(() => { + // Go to another page and instantly come back to this page. + location.href = new URL("../resources/going-back.html", window.location); + }, 0); + // Abort parsing in the middle of loading the page. + window.stop(); + } else { + const nrr = performance.getEntriesByType('navigation')[0].notRestoredReasons; + assert_true(ReasonsInclude(nrr.reasons, "parser-aborted")); + t.done(); + } +}, "aborting a parser should block bfcache."); diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-attributes.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-attributes.tentative.window.js new file mode 100644 index 00000000000000..44495fa981b752 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-attributes.tentative.window.js @@ -0,0 +1,55 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; + +// Ensure that empty attributes are reported as empty strings and missing +// attributes are reported as null. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + // Add a cross-origin iframe. + const rc1_child = await rc1.addIframe( + /*extraConfig=*/ { + origin: 'HTTP_REMOTE_ORIGIN', + scripts: [], + headers: [], + }, + /*attributes=*/ {id: '', name: ''}, + ); + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + const rc1_child_url = await rc1_child.executeScript(() => { + return location.href; + }); + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/[{ + 'url': null, + 'src': rc1_child_url, + // Id and name should be empty. + 'id': '', + 'name': '', + 'reasons': null, + 'children': null + }]); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache-reasons-stay.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache-reasons-stay.tentative.window.js new file mode 100644 index 00000000000000..28dbc38575a3b2 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache-reasons-stay.tentative.window.js @@ -0,0 +1,47 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; + +// Ensure that notRestoredReasons are only updated after non BFCache navigation. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/ []); + + // This time no blocking feature is used, so the page is restored + // from BFCache. Ensure that the previous reasons stay there. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ true); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/ []); +}); diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache.tentative.window.js new file mode 100644 index 00000000000000..bfb685451c36d6 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-bfcache.tentative.window.js @@ -0,0 +1,28 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: timeout=long + +'use strict'; + +// Ensure that notRestoredReasons is empty for successful BFCache restore. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + + // Check the BFCache result and verify that no reasons are recorded + // for successful restore. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ true); + assert_true(await rc1.executeScript(() => { + let reasons = + performance.getEntriesByType('navigation')[0].notRestoredReasons; + return reasons === null; + })); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js new file mode 100644 index 00000000000000..2a313fe7b14334 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js @@ -0,0 +1,60 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; + +// Ensure that cross-origin subtree's reasons are not exposed to +// notRestoredReasons. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + // Add a cross-origin iframe. + const rc1_child = await rc1.addIframe( + /*extraConfig=*/ { + origin: 'HTTP_REMOTE_ORIGIN', + scripts: [], + headers: [], + }, + /*attributes=*/ {id: 'test-id'}, + ); + // Use WebSocket to block BFCache. + await useWebSocket(rc1_child); + const rc1_child_url = await rc1_child.executeScript(() => { + return location.href; + }); + // Add a child to the iframe. + const rc1_grand_child = await rc1_child.addIframe(); + const rc1_grand_child_url = await rc1_grand_child.executeScript(() => { + return location.href; + }); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': "masked"}], + /*children=*/[{ + 'url': null, + 'src': rc1_child_url, + 'id': 'test-id', + 'name': null, + 'reasons': null, + 'children': null + }]); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-fetch.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-fetch.tentative.window.js new file mode 100644 index 00000000000000..c8f53a660fa9a1 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-fetch.tentative.window.js @@ -0,0 +1,40 @@ +// META: title=Ensure that ongoing fetch upon entering bfcache blocks bfcache and recorded. +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: timeout=long + +'use strict'; + +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + const wavURL = new URL(get_host_info().HTTP_REMOTE_ORIGIN + '/fetch/range/resources/long-wav.py'); + await rc1.executeScript((wavURL) => { + // Register pagehide handler to create a fetch request. + addEventListener('pagehide', (wavURL) => { + fetch(wavURL, { + keepalive: true + }); + }) + }); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'fetch'}], + /*children=*/[]); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js new file mode 100644 index 00000000000000..cda0ac43944742 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js @@ -0,0 +1,103 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; + +// Ensure that empty attributes are reported as empty strings and missing +// attributes are reported as null. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + // Add a cross-origin iframe. + const rc1_child = await rc1.addIframe( + /*extraConfig=*/ { + origin: 'HTTP_REMOTE_ORIGIN', + scripts: [], + headers: [], + }, + /*attributes=*/ {id: '', name: ''}, + ); + const rc2_child = await rc1.addIframe( + /*extraConfig=*/ { + origin: 'HTTP_REMOTE_ORIGIN', + scripts: [], + headers: [], + }, + /*attributes=*/ {}, + ); + const rc3_child = await rc1.addIframe( + /*extraConfig=*/ {}, + /*attributes=*/ {}, + ); + const rc4_child = await rc1.addIframe( + /*extraConfig=*/ {}, + /*attributes=*/ {id: '', name: ''}, + ); + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + const rc1_child_url = await rc1_child.executeScript(() => { + return location.href; + }); + const rc2_child_url = await rc2_child.executeScript(() => { + return location.href; + }); + const rc3_child_url = await rc3_child.executeScript(() => { + return location.href; + }); + const rc4_child_url = await rc4_child.executeScript(() => { + return location.href; + }); + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/[{ + 'url': null, + 'src': rc1_child_url, + // Id and name should be empty. + 'id': '', + 'name': '', + 'reasons': null, + 'children': null + }, { + 'url': null, + 'src': rc2_child_url, + // Id and name should be null. + 'id': null, + 'name': null, + 'reasons': null, + 'children': null + },{ + 'url': rc3_child_url, + 'src': rc3_child_url, + // Id and name should be null. + 'id': null, + 'name': null, + 'reasons': [], + 'children': [] + }, { + 'url': rc4_child_url, + 'src': rc4_child_url, + 'id': '', + 'name': '', + 'reasons': [], + 'children': [] + }]); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-lock.https.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-lock.https.tentative.window.js new file mode 100644 index 00000000000000..46d8752f20d967 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-lock.https.tentative.window.js @@ -0,0 +1,32 @@ +// META: title=Ensure that if WebLock is held upon entering bfcache, it cannot enter bfcache and gets reported. +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: timeout=long + +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + + // Request a WebLock. + let return_value = await rc1.executeScript(() => { + return new Promise((resolve) => { + navigator.locks.request('resource', () => { + resolve(42); + }); + }) + }); + assert_equals(return_value, 42); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredFromBFCache(rc1, ['lock']); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js new file mode 100644 index 00000000000000..faa7649bc33c3f --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js @@ -0,0 +1,26 @@ +// META: title=Ensure that navigation failure blocks bfcache and gets recorded. +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/404.py +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: timeout=long + +'use strict'; +const {ORIGIN} = get_host_info(); + +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ {status: 404}, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredFromBFCache(rc1, ['response-status-not-ok']); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.tentative.window.js new file mode 100644 index 00000000000000..1cf1d55d90f78a --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-not-bfcached.tentative.window.js @@ -0,0 +1,36 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; + +// Ensure that notRestoredReasons is populated when not restored. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/ []); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-redirect-on-history.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-redirect-on-history.tentative.window.js new file mode 100644 index 00000000000000..7191456cc845bd --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-redirect-on-history.tentative.window.js @@ -0,0 +1,53 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; +const {ORIGIN, REMOTE_ORIGIN} = get_host_info(); + +// Ensure that notRestoredReasons reset after the server redirect. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + + // Create a remote context with the redirected URL. + let rc1_redirected = + await rcHelper.createContext(/*extraConfig=*/ { + origin: 'HTTP_ORIGIN', + scripts: [], + headers: [], + }); + + const redirectUrl = + `${ORIGIN}/common/redirect.py?location=${encodeURIComponent(rc1_redirected.url)}`; + // Replace the history state. + await rc1.executeScript((url) => { + window.history.replaceState(null, '', url); + }, [redirectUrl]); + + // Navigate away. + const newRemoteContextHelper = await rc1.navigateToNew(); + + // Go back. + await newRemoteContextHelper.historyBack(); + + const navigation_entry = await rc1_redirected.executeScript(() => { + return performance.getEntriesByType('navigation')[0]; + }); + assert_equals( + navigation_entry.redirectCount, 1, 'Expected redirectCount is 1.'); + // Becauase of the redirect, notRestoredReasons is reset. + assert_equals( + navigation_entry.notRestoredReasons, null, + 'Expected notRestoredReasons is null.'); +}); diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-reload.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-reload.tentative.window.js new file mode 100644 index 00000000000000..1a8972778d287c --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-reload.tentative.window.js @@ -0,0 +1,50 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; +const {ORIGIN, REMOTE_ORIGIN} = get_host_info(); + +// Ensure that notRestoredReasons reset after the server redirect. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/ []); + + // Reload. + await rc1.navigate(() => { + location.reload(); + }, []); + + // Becauase of the reload, notRestoredReasons is reset. + const navigation_entry = await rc1.executeScript(() => { + return performance.getEntriesByType('navigation')[0]; + }); + + assert_equals( + navigation_entry.notRestoredReasons, null, + 'Expected notRestoredReasons is null.'); +}); diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js new file mode 100644 index 00000000000000..89d66b070ed8c4 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js @@ -0,0 +1,61 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + + +'use strict'; + +// Ensure that same-origin subtree's reasons are exposed to notRestoredReasons. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + // Add a same-origin iframe and use WebSocket. + const rc1_child = await rc1.addIframe( + /*extra_config=*/ {}, /*attributes=*/ {id: 'test-id'}); + await useWebSocket(rc1_child); + + const rc1_child_url = await rc1_child.executeScript(() => { + return location.href; + }); + // Add a child to the iframe. + const rc1_grand_child = await rc1_child.addIframe(); + const rc1_grand_child_url = await rc1_grand_child.executeScript(() => { + return location.href; + }); + + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[], + /*children=*/[{ + 'url': rc1_child_url, + 'src': rc1_child_url, + 'id': 'test-id', + 'name': '', + 'reasons': [{'reason': 'websocket'}], + 'children': [{ + 'url': rc1_grand_child_url, + 'src': rc1_grand_child_url, + 'id': '', + 'name': '', + 'reasons': [], + 'children': [] + }] + }]); +}); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-replace.tentative.window.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-replace.tentative.window.js new file mode 100644 index 00000000000000..1162bfaf7600fd --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/performance-navigation-timing-same-origin-replace.tentative.window.js @@ -0,0 +1,43 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.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/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; + +// Ensure that notRestoredReasons are accessible after history replace. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + // Navigate away. + const newRemoteContextHelper = await rc1.navigateToNew(); + // Replace the history state to a same-origin site. + await newRemoteContextHelper.executeScript((destUrl) => { + window.history.replaceState(null, '', '#'); + }); + // Go back. + await newRemoteContextHelper.historyBack(); + + // Reasons are not reset for same-origin replace. + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/ []); +}); diff --git a/test/fixtures/wpt/performance-timeline/not-restored-reasons/test-helper.js b/test/fixtures/wpt/performance-timeline/not-restored-reasons/test-helper.js new file mode 100644 index 00000000000000..ba9a4c0342fcb5 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/not-restored-reasons/test-helper.js @@ -0,0 +1,57 @@ +// META: script=../../html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js + +async function assertNotRestoredReasonsEquals( + remoteContextHelper, url, src, id, name, reasons, children) { + let result = await remoteContextHelper.executeScript(() => { + return performance.getEntriesByType('navigation')[0].notRestoredReasons; + }); + assertReasonsStructEquals( + result, url, src, id, name, reasons, children); +} + +function assertReasonsStructEquals( + result, url, src, id, name, reasons, children) { + assert_equals(result.url, url); + assert_equals(result.src, src); + assert_equals(result.id, id); + assert_equals(result.name, name); + + // Reasons should match. + let expected = new Set(reasons); + let actual = new Set(result.reasons); + matchReasons(extractReason(expected), extractReason(actual)); + + // Children should match. + if (children == null) { + assert_equals(result.children, children); + } else { + for (let j = 0; j < children.length; j++) { + assertReasonsStructEquals( + result.children[j], children[j].url, + children[j].src, children[j].id, children[j].name, children[j].reasons, + children[j].children); + } + } +} + +function ReasonsInclude(reasons, targetReason) { + for (const reason of reasons) { + if (reason.reason == targetReason) { + return true; + } + } + return false; +} + +// Requires: +// - /websockets/constants.sub.js in the test file and pass the domainPort +// constant here. +async function useWebSocket(remoteContextHelper) { + let return_value = await remoteContextHelper.executeScript((domain) => { + return new Promise((resolve) => { + var webSocketInNotRestoredReasonsTests = new WebSocket(domain + '/echo'); + webSocketInNotRestoredReasonsTests.onopen = () => { resolve(42); }; + }); + }, [SCHEME_DOMAIN_PORT]); + assert_equals(return_value, 42); +} \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/resources/child-frame.html b/test/fixtures/wpt/performance-timeline/resources/child-frame.html new file mode 100644 index 00000000000000..40c8f7268857d0 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/child-frame.html @@ -0,0 +1,7 @@ + + + + + diff --git a/test/fixtures/wpt/performance-timeline/resources/empty.html b/test/fixtures/wpt/performance-timeline/resources/empty.html new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/test/fixtures/wpt/performance-timeline/resources/going-back.html b/test/fixtures/wpt/performance-timeline/resources/going-back.html new file mode 100644 index 00000000000000..f4a26669baa163 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/going-back.html @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/resources/include-frames-helper.js b/test/fixtures/wpt/performance-timeline/resources/include-frames-helper.js new file mode 100644 index 00000000000000..a56489baaf4d06 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/include-frames-helper.js @@ -0,0 +1,60 @@ +const verifyEntries = (entries, filterOptions) => { + for (const filterOption of filterOptions) { + let countBeforeFiltering = entries.length; + + // Using negate of the condition so that the next filtering is applied on less entries. + entries = entries.filter( + e => !(e.entryType == filterOption['entryType'] && e.name.includes(filterOption['name']))); + + assert_equals( + countBeforeFiltering - entries.length, filterOption['expectedCount'], filterOption['failureMsg']); + } +} + +const createFilterOption = (name, entryType, expectedCount, msgPrefix, description = '') => { + if (description) { + description = ' ' + description; + } + + let failureMsg = + `${msgPrefix} should have ${expectedCount} ${entryType} entries for name ${name}` + description; + + return { + name: name, + entryType: entryType, + expectedCount: expectedCount, + failureMsg: failureMsg + }; +} + +const loadChildFrame = (src) => { + return new Promise(resolve => { + + const childFrame = document.createElement('iframe'); + + childFrame.addEventListener("load", resolve); + + childFrame.src = src; + + document.body.appendChild(childFrame); + }); +} + +const loadChildFrameAndGrandchildFrame = (src) => { + return new Promise(resolve => { + + const crossOriginChildFrame = document.createElement('iframe'); + + // Wait for the child frame to send a message. The child frame would send a message + // when it loads its child frame. + window.addEventListener('message', e => { + if (e.data == 'Load completed') { + resolve(); + } + }); + + crossOriginChildFrame.src = src; + + document.body.appendChild(crossOriginChildFrame) + }); +} diff --git a/test/fixtures/wpt/performance-timeline/resources/include-frames-subframe.html b/test/fixtures/wpt/performance-timeline/resources/include-frames-subframe.html new file mode 100644 index 00000000000000..0d43f418a096a0 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/include-frames-subframe.html @@ -0,0 +1,43 @@ + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/resources/json_resource.json b/test/fixtures/wpt/performance-timeline/resources/json_resource.json new file mode 100644 index 00000000000000..68b6ac1d56f7c2 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/json_resource.json @@ -0,0 +1,4 @@ +{ + "name": "nav_id_test", + "target": "resource_timing" +} \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/resources/make_long_task.js b/test/fixtures/wpt/performance-timeline/resources/make_long_task.js new file mode 100644 index 00000000000000..a52d6d839298cc --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/make_long_task.js @@ -0,0 +1,4 @@ +(function () { + let now = window.performance.now(); + while (window.performance.now() < now + 60); +}()); \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/resources/navigation-id-detached-frame-page.html b/test/fixtures/wpt/performance-timeline/resources/navigation-id-detached-frame-page.html new file mode 100644 index 00000000000000..02aafbb5c78368 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/navigation-id-detached-frame-page.html @@ -0,0 +1,21 @@ + + + + + + The navigation_id Detached iframe Page. + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/performance-timeline/resources/worker-navigation-id.js b/test/fixtures/wpt/performance-timeline/resources/worker-navigation-id.js new file mode 100644 index 00000000000000..3a2740d0675c84 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/resources/worker-navigation-id.js @@ -0,0 +1,6 @@ +self.onmessage = () => { + const mark_name = 'user_timig_mark'; + performance.mark(mark_name); + postMessage(performance.getEntriesByName(mark_name)[0].navigationId); + self.close(); +} diff --git a/test/fixtures/wpt/performance-timeline/supportedEntryTypes-cross-realm-access.html b/test/fixtures/wpt/performance-timeline/supportedEntryTypes-cross-realm-access.html new file mode 100644 index 00000000000000..8b86a6398bfd49 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/supportedEntryTypes-cross-realm-access.html @@ -0,0 +1,18 @@ + + +Cross-realm access of supportedEntryTypes returns Array of another realm + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/detached-frame.html b/test/fixtures/wpt/performance-timeline/tentative/detached-frame.html new file mode 100644 index 00000000000000..70019223a648d9 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/detached-frame.html @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A-A.html b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A-A.html new file mode 100644 index 00000000000000..57623e5b33bfb0 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A-A.html @@ -0,0 +1,93 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A.html b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A.html new file mode 100644 index 00000000000000..bcb9a81657f227 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-A.html @@ -0,0 +1,76 @@ + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AA.html b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AA.html new file mode 100644 index 00000000000000..cccbf4100dd2ae --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AA.html @@ -0,0 +1,51 @@ + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AB.html b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AB.html new file mode 100644 index 00000000000000..d630665b652cb1 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-AB.html @@ -0,0 +1,53 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-A.html b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-A.html new file mode 100644 index 00000000000000..58cad40faba658 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-A.html @@ -0,0 +1,91 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-B.html b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-B.html new file mode 100644 index 00000000000000..2368a8e881aa3a --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B-B.html @@ -0,0 +1,57 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B.html b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B.html new file mode 100644 index 00000000000000..b823d6edaa3262 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/include-frames-originA-B.html @@ -0,0 +1,50 @@ + + + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/performance-entry-source.html b/test/fixtures/wpt/performance-timeline/tentative/performance-entry-source.html new file mode 100644 index 00000000000000..d10d3c5ed512b7 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/performance-entry-source.html @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/tentative/with-filter-options-originA.html b/test/fixtures/wpt/performance-timeline/tentative/with-filter-options-originA.html new file mode 100644 index 00000000000000..6c6643df75cf09 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/tentative/with-filter-options-originA.html @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/test/fixtures/wpt/performance-timeline/timing-removed-iframe.html b/test/fixtures/wpt/performance-timeline/timing-removed-iframe.html new file mode 100644 index 00000000000000..43988b21fbbe02 --- /dev/null +++ b/test/fixtures/wpt/performance-timeline/timing-removed-iframe.html @@ -0,0 +1,16 @@ + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/META.yml b/test/fixtures/wpt/resource-timing/META.yml index 662c42cb664219..153654eae8b1a1 100644 --- a/test/fixtures/wpt/resource-timing/META.yml +++ b/test/fixtures/wpt/resource-timing/META.yml @@ -1,6 +1,5 @@ spec: https://w3c.github.io/resource-timing/ suggested_reviewers: - plehegar - - zqzhang - igrigorik - yoavweiss diff --git a/test/fixtures/wpt/resource-timing/body-size-cross-origin.https.html b/test/fixtures/wpt/resource-timing/body-size-cross-origin.https.html new file mode 100644 index 00000000000000..b0340139bf7f40 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/body-size-cross-origin.https.html @@ -0,0 +1,63 @@ + + + + +Verify that encodedBodySize/decodedBodySize are CORS-protected rather than TAO-protected + + + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/content-type-minimization.html b/test/fixtures/wpt/resource-timing/content-type-minimization.html new file mode 100644 index 00000000000000..d7dbc2cc9f3b6a --- /dev/null +++ b/test/fixtures/wpt/resource-timing/content-type-minimization.html @@ -0,0 +1,79 @@ + + + + +This test validates the parsing of content-type of resources. + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/resource-timing/content-type.html b/test/fixtures/wpt/resource-timing/content-type.html new file mode 100644 index 00000000000000..654e083e14aa1f --- /dev/null +++ b/test/fixtures/wpt/resource-timing/content-type.html @@ -0,0 +1,126 @@ + + + +This test validates the content-type of resources. + + + + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html b/test/fixtures/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html index 1b107d3aef7764..8e368d13807745 100644 --- a/test/fixtures/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html +++ b/test/fixtures/wpt/resource-timing/cross-origin-start-end-time-with-redirects.html @@ -19,13 +19,19 @@ const blank_page = `/resource-timing/resources/blank_page_green.htm`; const destUrl = `/common/slow-redirect.py?delay=${delay}&location=${REMOTE_ORIGIN}/${blank_page}`; -const timeBefore = performance.now() -attribute_test(load.iframe, destUrl, entry => { - assert_equals(entry.startTime, entry.fetchStart, 'startTime and fetchStart should be equal'); - assert_greater_than(entry.startTime, timeBefore, 'startTime and fetchStart should be greater than the time before fetching'); - // See https://github.com/w3c/resource-timing/issues/264 - assert_less_than(Math.round(entry.startTime - timeBefore), delay * 1000, 'startTime should not expose redirect delays'); -}, "Verify that cross-origin resources don't implicitly expose their redirect timings") +const timeBefore = performance.now(); +(async () => { + // Wait 10 ms, to ensure the difference between startTime and timeBefore is + // larger than 1 ms, to avoid flakiness in browsers that clamp timestamps to + // 1 ms. + await new Promise(r => step_timeout(r, 10)); + attribute_test(load.iframe, destUrl, entry => { + assert_equals(entry.startTime, entry.fetchStart, 'startTime and fetchStart should be equal'); + assert_greater_than(entry.startTime, timeBefore, 'startTime and fetchStart should be greater than the time before fetching'); + // See https://github.com/w3c/resource-timing/issues/264 + assert_less_than(Math.round(entry.startTime - timeBefore), delay * 1000, 'startTime should not expose redirect delays'); + }, "Verify that cross-origin resources don't implicitly expose their redirect timings") +})(); diff --git a/test/fixtures/wpt/resource-timing/delivery-type.tentative.any.js b/test/fixtures/wpt/resource-timing/delivery-type.tentative.any.js new file mode 100644 index 00000000000000..e2b408fdd74f29 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/delivery-type.tentative.any.js @@ -0,0 +1,90 @@ +// META: global=window,worker +// META: script=/common/get-host-info.sub.js +// META: script=/resource-timing/resources/resource-loaders.js + +// TODO(crbug/1358591): Rename this file from "tentative" once +// `w3c/resource-timing#343` is merged. + +const {REMOTE_ORIGIN, ORIGIN} = get_host_info(); + +const redirectBase = new URL( + '/resource-timing/resources/redirect-cors.py', REMOTE_ORIGIN).href; +const cacheAndValidatedBase = new URL( + '/resource-timing/resources/cacheable-and-validated.py?content=content', + ORIGIN).href; + +const mustRevalidate = {headers: {'Cache-Control': 'max-age=0'}}; + +const fetchAndEatBody = (url, fetchOption) => { + return fetch(url, fetchOption).then(response => response.arrayBuffer()); +}; + +const accumulateEntries = () => { + return new Promise(resolve => { + const po = new PerformanceObserver(list => { + resolve(list); + }); + po.observe({type: "resource", buffered: true}); + }); +}; + +const checkDeliveryTypeBase = + (list, lookupURL, deliveryTypeForCachedResources) => { + const entries = list.getEntriesByName(lookupURL); + assert_equals(entries.length, 3, 'Wrong number of entries'); + + // 200 response (`cacheMode` is an empty string) + assert_equals(entries[0].deliveryType, "", + "Expect empty deliveryType for 200 response."); + // Cached response (`cacheMode` is "local") or 304 response (`cacheMode` is + // "validated"). + assert_equals(entries[1].deliveryType, deliveryTypeForCachedResources, + `Expect "${deliveryTypeForCachedResources}" deliveryType for a + cached response.`); + assert_equals(entries[2].deliveryType, deliveryTypeForCachedResources, + `Expect "${deliveryTypeForCachedResources}" deliveryType for a + revalidated response.`); +}; + +promise_test(() => { + // Use a different URL every time so that the cache behaviour does not depend + // on execution order. + const initialURL = load.cache_bust(cacheAndValidatedBase); + const checkDeliveryType = + list => checkDeliveryTypeBase(list, initialURL, "cache"); + return fetchAndEatBody(initialURL, {}) // 200. + .then(() => fetchAndEatBody(initialURL, {})) // Cached. + .then(() => fetchAndEatBody(initialURL, mustRevalidate)) // 304. + .then(accumulateEntries) + .then(checkDeliveryType); +}, 'PerformanceResourceTiming deliveryType test, same origin.'); + +promise_test(() => { + const cacheAndValidatedURL = load.cache_bust( + cacheAndValidatedBase + '&timing_allow_origin=*'); + const redirectURL = redirectBase + + "?timing_allow_origin=*" + + `&allow_origin=${encodeURIComponent(ORIGIN)}` + + `&location=${encodeURIComponent(cacheAndValidatedURL)}`; + const checkDeliveryType = + list => checkDeliveryTypeBase(list, redirectURL, "cache"); + return fetchAndEatBody(redirectURL, {}) // 200. + .then(() => fetchAndEatBody(redirectURL, {})) // Cached. + .then(() => fetchAndEatBody(redirectURL, mustRevalidate)) // 304. + .then(accumulateEntries) + .then(checkDeliveryType); +}, 'PerformanceResourceTiming deliveryType test, cross origin, TAO passes.'); + +promise_test(() => { + const cacheAndValidatedURL = load.cache_bust(cacheAndValidatedBase); + const redirectURL = redirectBase + + `?allow_origin=${encodeURIComponent(ORIGIN)}` + + `&location=${encodeURIComponent(cacheAndValidatedURL)}`; + const checkDeliveryType = + list => checkDeliveryTypeBase(list, redirectURL, ""); + return fetchAndEatBody(redirectURL, {}) // 200. + .then(() => fetchAndEatBody(redirectURL, {})) // Cached. + .then(() => fetchAndEatBody(redirectURL, mustRevalidate)) // 304. + .then(accumulateEntries) + .then(checkDeliveryType); +}, 'PerformanceResourceTiming deliveryType test, cross origin, TAO fails.'); diff --git a/test/fixtures/wpt/resource-timing/entries-for-network-errors.sub.https.html b/test/fixtures/wpt/resource-timing/entries-for-network-errors.sub.https.html index 95849d282621ff..ebc2247babcd4a 100644 --- a/test/fixtures/wpt/resource-timing/entries-for-network-errors.sub.https.html +++ b/test/fixtures/wpt/resource-timing/entries-for-network-errors.sub.https.html @@ -30,6 +30,22 @@ network_error_entry_test( `/element-timing/resources/multiple-redirects.py?redirect_count=22&final_resource=${validXmlUrl}`, null, "too many redirects"); + +// ORB (https://github.com/whatwg/fetch/pull/1442) will return network errors +// for certain cross-origin fetches. This tests that the same rules apply to +// these fetches. Since ORB (at least as presently implemented) doesn't return +// network errors for fetches, we have to load this case using an element. +// +// This emulates a case previously tested in service-workers/service-worker/resource-timing.sub.https.html +const orb_loader = (url, _) => new Promise(resolve => { + const img = document.createElement("img"); + img.src = url; + img.onerror = resolve; + document.body.appendChild(img); +} ); +network_error_entry_test( + '//{{hosts[alt][]}}:{{ports[https][0]}}/service-workers/service-worker/resources/missing.jpg', + null, "network error for ORB-blocked response", orb_loader); diff --git a/test/fixtures/wpt/resource-timing/fetch-cross-origin-redirect.https.html b/test/fixtures/wpt/resource-timing/fetch-cross-origin-redirect.https.html index 4193422653a595..1605e224ab43b2 100644 --- a/test/fixtures/wpt/resource-timing/fetch-cross-origin-redirect.https.html +++ b/test/fixtures/wpt/resource-timing/fetch-cross-origin-redirect.https.html @@ -12,7 +12,7 @@ const {REMOTE_ORIGIN, ORIGIN} = get_host_info(); const redirect = "/common/redirect.py?" + - "location=/resource-timing/resources/green.html"; + "location=/resource-timing/resources/empty_script.js"; const cross_origin_redirect = REMOTE_ORIGIN + redirect; const same_origin_redirect = ORIGIN + redirect; diff --git a/test/fixtures/wpt/resource-timing/iframe-failed-commit.html b/test/fixtures/wpt/resource-timing/iframe-failed-commit.html index 1da207d2fbe05e..d3b5cce59ec21e 100644 --- a/test/fixtures/wpt/resource-timing/iframe-failed-commit.html +++ b/test/fixtures/wpt/resource-timing/iframe-failed-commit.html @@ -3,6 +3,7 @@ Resource Timing - test that unsuccessful iframes create entries + @@ -20,6 +21,10 @@ return load.iframe_with_attrs(path, {"csp": "default-src 'none'"}); }; +const load_iframe_with_csp_no_navigation = async path => { + return load.iframe_with_attrs(path, {"csp": "default-src 'none'"}, () => {}, true); +} + // Runs a test (labeled by the given label) to verify that loading an iframe // with the given URL generates a PerformanceResourceTiming entry and that the // entry does not expose sensitive timing attributes. @@ -46,12 +51,12 @@ }; // Runs a test (labeled by the given label) to verify that loading an iframe -// with the given URL, an empty response body and under a "default-src 'none' -// Content-Security-Policy generates a PerformanceResourceTiming entry and that -// the entry does expose sensitive timing attributes. -const empty_unmasked_entry_with_csp_test = (url, label) => { - return attribute_test(load_iframe_with_csp, url, - invariants.assert_tao_pass_no_redirect_http_empty, label); +// with the given URL under a "default-src 'none' Content-Security-Policy +// generates a PerformanceResourceTiming entry and that the entry does not +// expose sensitive timing attributes. +const non_navigating_masked_entry_with_csp_test = (url, label) => { + return attribute_test(load_iframe_with_csp_no_navigation, url, + invariants.assert_tao_failure_resource, label); }; const {REMOTE_ORIGIN, ORIGINAL_HOST, HTTPS_PORT} = get_host_info(); @@ -68,7 +73,8 @@ unmasked_entry_with_csp_test("/resource-timing/resources/csp-default-none.html", "Same-origin iframe that complies with CSP attribute gets reported"); -unmasked_entry_with_csp_test("/resource-timing/resources/green-frame.html", +// masked because this will load an error page which is cross-origin. +masked_entry_with_csp_test("/resource-timing/resources/green-frame.html", "Same-origin iframe that doesn't comply with CSP attribute gets reported"); masked_entry_with_csp_test( @@ -79,7 +85,7 @@ new URL("/resource-timing/resources/green-frame.html", REMOTE_ORIGIN), "Cross-origin iframe that doesn't comply with CSP attribute gets reported"); -empty_unmasked_entry_with_csp_test( +masked_entry_with_csp_test( "/resource-timing/resources/200_empty.asis", "Same-origin empty iframe with a 200 status gets reported"); @@ -87,19 +93,19 @@ new URL("/resource-timing/resources/200_empty.asis", REMOTE_ORIGIN), "Cross-origin empty iframe with a 200 status gets reported"); -unmasked_entry_with_csp_test( - new URL("/resource-timing/resources/204_empty.asis"), +non_navigating_masked_entry_with_csp_test( + new URL("/resource-timing/resources/204_empty.asis", location.origin), "Same-origin empty iframe with a 204 status gets reported"); -unmasked_entry_with_csp_test( - new URL("/resource-timing/resources/205_empty.asis"), +non_navigating_masked_entry_with_csp_test( + new URL("/resource-timing/resources/205_empty.asis", location.origin), "Same-origin empty iframe with a 205 status gets reported"); -masked_entry_with_csp_test( +non_navigating_masked_entry_with_csp_test( new URL("/resource-timing/resources/204_empty.asis", REMOTE_ORIGIN), "Cross-origin empty iframe with a 204 status gets reported"); -masked_entry_with_csp_test( +non_navigating_masked_entry_with_csp_test( new URL("/resource-timing/resources/205_empty.asis", REMOTE_ORIGIN), "Cross-origin empty iframe with a 205 status gets reported"); diff --git a/test/fixtures/wpt/resource-timing/iframe-sequence-of-events.html b/test/fixtures/wpt/resource-timing/iframe-sequence-of-events.html index 5f99a5cab2de6b..02d1c362c9df49 100644 --- a/test/fixtures/wpt/resource-timing/iframe-sequence-of-events.html +++ b/test/fixtures/wpt/resource-timing/iframe-sequence-of-events.html @@ -4,9 +4,21 @@ + + - \ No newline at end of file + diff --git a/test/fixtures/wpt/resource-timing/initiator-type/link.html b/test/fixtures/wpt/resource-timing/initiator-type/link.html index c49576a8e650bc..43367ac3d501e2 100644 --- a/test/fixtures/wpt/resource-timing/initiator-type/link.html +++ b/test/fixtures/wpt/resource-timing/initiator-type/link.html @@ -1,38 +1,35 @@ + - -Resource Timing initiator type: link - - - - - - + + Resource Timing initiator type: link + + + + + + + + + + + + - - - - - - - -
    This content forces a font to get fetched
+ // Verify there are enries for each of nested.css' nested resources. + initiator_type_test("resource_timing_test0.css?id=n1", "css", "css resources embedded in css"); + initiator_type_test("fonts/Ahem.ttf?id=n1", "css", "font resources embedded in css"); + initiator_type_test("blue.png?id=n1", "css", "image resources embedded in css"); + initiator_type_test("resource_timing_test0.css?id=prefetch", "link", ""); + initiator_type_test("resource_timing_test0.css?id=preload", "link", ""); + initiator_type_test("manifest.json", "link", ""); + initiator_type_test("resources/empty.js?id=modulePreload", "other", "module preload"); + +
    This content forces a font to get fetched
diff --git a/test/fixtures/wpt/resource-timing/initiator-type/script.html b/test/fixtures/wpt/resource-timing/initiator-type/script.html index dbd6a131decae1..6e9e3ae7eef361 100644 --- a/test/fixtures/wpt/resource-timing/initiator-type/script.html +++ b/test/fixtures/wpt/resource-timing/initiator-type/script.html @@ -12,6 +12,7 @@ + diff --git a/test/fixtures/wpt/resource-timing/initiator-type/video.html b/test/fixtures/wpt/resource-timing/initiator-type/video.html index 16f3b3dea5f1e8..2d8c9dcc474c2c 100644 --- a/test/fixtures/wpt/resource-timing/initiator-type/video.html +++ b/test/fixtures/wpt/resource-timing/initiator-type/video.html @@ -19,14 +19,14 @@ src="/resource-timing/resources/empty.py?id=track"> diff --git a/test/fixtures/wpt/resource-timing/initiator-type/workers.html b/test/fixtures/wpt/resource-timing/initiator-type/workers.html index 3a23ad71a31555..a3da99356db5e2 100644 --- a/test/fixtures/wpt/resource-timing/initiator-type/workers.html +++ b/test/fixtures/wpt/resource-timing/initiator-type/workers.html @@ -17,7 +17,7 @@ new Worker(moduleWorkerURL, {type: "module"}); new Worker(workerURL, {type: "classic"}); initiator_type_test(workerURL, "other", "classic worker"); - initiator_type_test(moduleWorkerURL, "other", "module worker"); + initiator_type_test(moduleWorkerURL, "script", "module worker"); diff --git a/test/fixtures/wpt/resource-timing/interim-response-times.h2.html b/test/fixtures/wpt/resource-timing/interim-response-times.h2.html new file mode 100644 index 00000000000000..4b1ca93ff7bd62 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/interim-response-times.h2.html @@ -0,0 +1,74 @@ + + + + + +Resource Timing: PerformanceResourceTiming interim resource times + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/interim-response-times.html b/test/fixtures/wpt/resource-timing/interim-response-times.html new file mode 100644 index 00000000000000..a4d03f599ee5a7 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/interim-response-times.html @@ -0,0 +1,72 @@ + + + + + +Resource Timing: PerformanceResourceTiming interim resource times + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/internal-resources-not-counted.html b/test/fixtures/wpt/resource-timing/internal-resources-not-counted.html new file mode 100644 index 00000000000000..a746e2adfa32be --- /dev/null +++ b/test/fixtures/wpt/resource-timing/internal-resources-not-counted.html @@ -0,0 +1,39 @@ + + +Resource Timing should not include internal resources + + + + + +

This test validates that image resources which are part of an element's +UA-defined interface are not exposed to the performance timeline. This uses an +<audio> element as an example of an element with internal resources used +for the playback controls, and verifies that resource timing entries are not +created. +

+ + + + + diff --git a/test/fixtures/wpt/resource-timing/nested-nav-fallback-timing.html b/test/fixtures/wpt/resource-timing/nested-nav-fallback-timing.html new file mode 100644 index 00000000000000..b8bba5614d0d12 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/nested-nav-fallback-timing.html @@ -0,0 +1,33 @@ + + +Test ResourceTiming reporting for cross-origin iframe. + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/no-entries-for-cross-origin-css-fetched-memory-cache.sub.html b/test/fixtures/wpt/resource-timing/no-entries-for-cross-origin-css-fetched-memory-cache.sub.html new file mode 100644 index 00000000000000..6b60305ded2e99 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/no-entries-for-cross-origin-css-fetched-memory-cache.sub.html @@ -0,0 +1,53 @@ + + +Make sure that resources fetched by cross origin CSS are not in the timeline. + + + + + + + +
    Some content
+ diff --git a/test/fixtures/wpt/resource-timing/object-not-found-after-cross-origin-redirect.html b/test/fixtures/wpt/resource-timing/object-not-found-after-cross-origin-redirect.html index 278c78e320e98c..6990c6c06082e5 100644 --- a/test/fixtures/wpt/resource-timing/object-not-found-after-cross-origin-redirect.html +++ b/test/fixtures/wpt/resource-timing/object-not-found-after-cross-origin-redirect.html @@ -4,6 +4,7 @@ This test validates the values in resource timing for cross-origin redirects. + @@ -15,20 +16,26 @@ diff --git a/test/fixtures/wpt/resource-timing/queue-entry-regardless-buffer-size.html b/test/fixtures/wpt/resource-timing/queue-entry-regardless-buffer-size.html new file mode 100644 index 00000000000000..ea47ae3a7950a2 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/queue-entry-regardless-buffer-size.html @@ -0,0 +1,38 @@ + + + + + +This test validates that resource timing entires should always be queued regardless the size of the buffer. + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/resource-timing-failed-fetch-web-bundle.tentative.html b/test/fixtures/wpt/resource-timing/resource-timing-failed-fetch-web-bundle.tentative.html new file mode 100644 index 00000000000000..b99183a49cd84c --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resource-timing-failed-fetch-web-bundle.tentative.html @@ -0,0 +1,53 @@ + + + + Resource timing attributes are consistent for the same-origin subresources. + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/resource-timing/resource-timing-failed-fetch.html b/test/fixtures/wpt/resource-timing/resource-timing-failed-fetch.html new file mode 100644 index 00000000000000..5bab39e2760728 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resource-timing-failed-fetch.html @@ -0,0 +1,34 @@ + + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/wpt/resource-timing/resource-timing-level1.js b/test/fixtures/wpt/resource-timing/resource-timing-level1.js index 95b5cdfb1ed0ca..6167777fe68fa3 100644 --- a/test/fixtures/wpt/resource-timing/resource-timing-level1.js +++ b/test/fixtures/wpt/resource-timing/resource-timing-level1.js @@ -235,47 +235,6 @@ window.onload = }); }); - // Test that responseStart uses the timing of 1XX responses by - // synthesizing a delay between a 100 and 200 status, and verifying that - // this delay is included before responseEnd. If the delay is not - // included, this implies that the 200 status line was (incorrectly) used - // for responseStart timing, despite the 100 response arriving earlier. - // - // Source: "In the case where more than one response is available for a - // request, due to an Informational 1xx response, the reported - // responseStart value is that of the first response to the last - // request." - [ - { initiator: "iframe", response: "(done)", mime: mimeHtml }, - { initiator: "xmlhttprequest", response: "(done)", mime: mimeText }, - { initiator: "script", response: '"";', mime: mimeScript }, - { initiator: "link", response: ".unused{}", mime: mimeCss }, - ] - .forEach(function (template) { - testCases.push({ - description: "'" + template.initiator + " responseStart uses 1XX (first) response timings'", - test: function (test) { - initiateFetch( - test, - template.initiator, - getSyntheticUrl("status:100" - + "&flush" - + "&" + serverStepDelay + "ms" - + "&status:200" - + "&mime:" + template.mime - + "&send:" + encodeURIComponent(template.response)), - function (initiator, entry) { - assert_greater_than_equal( - entry.responseEnd, - entry.responseStart + serverStepDelay, - "HTTP/1.1 1XX (first) response should determine 'responseStart' timing."); - - test.done(); - }); - } - }); - }); - // Function to run the next case in the queue. var currentTestIndex = -1; function runNextCase() { diff --git a/test/fixtures/wpt/resource-timing/resources/child_script.js b/test/fixtures/wpt/resource-timing/resources/child_script.js new file mode 100644 index 00000000000000..8b137891791fe9 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resources/child_script.js @@ -0,0 +1 @@ + diff --git a/test/fixtures/wpt/resource-timing/resources/delay-load.html b/test/fixtures/wpt/resource-timing/resources/delay-load.html new file mode 100644 index 00000000000000..4898c1be8ebfff --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resources/delay-load.html @@ -0,0 +1,4 @@ + + + + diff --git a/test/fixtures/wpt/resource-timing/resources/entry-invariants.js b/test/fixtures/wpt/resource-timing/resources/entry-invariants.js index 4bef9496103ca6..78d43d1b01e451 100644 --- a/test/fixtures/wpt/resource-timing/resources/entry-invariants.js +++ b/test/fixtures/wpt/resource-timing/resources/entry-invariants.js @@ -1,3 +1,19 @@ +const await_with_timeout = async (delay, message, promise, cleanup = ()=>{}) => { + let timeout_id; + const timeout = new Promise((_, reject) => { + timeout_id = step_timeout(() => + reject(new DOMException(message, "TimeoutError")), delay) + }); + let result = null; + try { + result = await Promise.race([promise, timeout]); + clearTimeout(timeout_id); + } finally { + cleanup(); + } + return result; +}; + // Asserts that the given attributes are present in 'entry' and hold equal // values. const assert_all_equal_ = (entry, attributes) => { @@ -75,8 +91,6 @@ const invariants = { assert_positive_(entry, [ "fetchStart", "transferSize", - "encodedBodySize", - "decodedBodySize", ]); }, @@ -98,8 +112,6 @@ const invariants = { "secureConnectionStart", "redirectStart", "redirectEnd", - "encodedBodySize", - "decodedBodySize", ]); assert_not_negative_(entry, [ @@ -139,8 +151,6 @@ const invariants = { assert_positive_(entry, [ "fetchStart", "transferSize", - "encodedBodySize", - "decodedBodySize", ]); }, @@ -172,8 +182,6 @@ const invariants = { assert_positive_(entry, [ "fetchStart", "transferSize", - "encodedBodySize", - "decodedBodySize", ]); }, @@ -196,8 +204,6 @@ const invariants = { "secureConnectionStart", "redirectStart", "redirectEnd", - "encodedBodySize", - "decodedBodySize", ]); assert_not_negative_(entry, [ @@ -229,8 +235,6 @@ const invariants = { "workerStart", "redirectStart", "redirectEnd", - "encodedBodySize", - "decodedBodySize", ]); assert_not_negative_(entry, [ @@ -405,8 +409,6 @@ const invariants = { "requestStart", "responseStart", "transferSize", - "encodedBodySize", - "decodedBodySize", ]); assert_ordered_(entry, [ @@ -440,8 +442,6 @@ const invariants = { "requestStart", "responseStart", "transferSize", - "encodedBodySize", - "decodedBodySize", ]); } @@ -467,7 +467,10 @@ const attribute_test_internal = (loader, path, validator, run_test, test_label) }); await loader(path, validator); - const entry = await(loaded_entry); + const entry = await await_with_timeout(2000, + "Timeout was reached before entry fired", + loaded_entry); + assert_not_equals(entry, null, 'No entry was received'); run_test(entry); }, test_label); }; @@ -485,11 +488,13 @@ const attribute_test_with_validator = (loader, path, validator, run_test, test_l attribute_test_internal(loader, path, validator, run_test, test_label); }; -const network_error_entry_test = (originalURL, args, label) => { +const network_error_entry_test = (originalURL, args, label, loader) => { const url = new URL(originalURL, location.href); const search = new URLSearchParams(url.search.substr(1)); const timeBefore = performance.now(); - loader = () => new Promise(resolve => fetch(url, args).catch(resolve)); + + // Load using `fetch()`, unless we're given a specific loader for this test. + loader ??= () => new Promise(resolve => fetch(url, args).catch(resolve)); attribute_test( loader, url, diff --git a/test/fixtures/wpt/resource-timing/resources/frame-timing.js b/test/fixtures/wpt/resource-timing/resources/frame-timing.js index e0c364e9b2c3e2..019bd424b55065 100644 --- a/test/fixtures/wpt/resource-timing/resources/frame-timing.js +++ b/test/fixtures/wpt/resource-timing/resources/frame-timing.js @@ -1,48 +1,63 @@ function test_frame_timing_before_load_event(type) { - promise_test(async t => { - const {document, performance} = type === 'frame' ? window.parent : window; - const delay = 500; - const frame = document.createElement(type); - t.add_cleanup(() => frame.remove()); - await new Promise(resolve => { - frame.addEventListener('load', resolve); - frame.src = `resources/iframe-with-delay.sub.html?delay=${delay}`; - (type === 'frame' ? document.querySelector('frameset') : document.body).appendChild(frame); - }); + promise_test(async t => { + const {document, performance} = type === 'frame' ? window.parent : window; + const delay = 500; + const frame = document.createElement(type); + t.add_cleanup(() => frame.remove()); + await new Promise(resolve => { + frame.addEventListener('load', resolve); + frame.src = `/resource-timing/resources/iframe-with-delay.sub.html?delay=${delay}`; + (type === 'frame' ? document.querySelector('frameset') : document.body).appendChild(frame); + }); - const entries = performance.getEntriesByName(frame.src); - const navigationEntry = frame.contentWindow.performance.getEntriesByType('navigation')[0]; - assert_equals(entries.length, 1); - assert_equals(entries[0].initiatorType, type); - assert_greater_than(performance.now(), entries[0].responseEnd + delay); - const domContentLoadedEventAbsoluteTime = navigationEntry.domContentLoadedEventStart + frame.contentWindow.performance.timeOrigin; - const frameResponseEndAbsoluteTime = entries[0].responseEnd + performance.timeOrigin; - assert_greater_than(domContentLoadedEventAbsoluteTime, frameResponseEndAbsoluteTime); - }, `A ${type} should report its RT entry when the response is done and before it is completely loaded`); + const entries = performance.getEntriesByName(frame.src); + const navigationEntry = frame.contentWindow.performance.getEntriesByType('navigation')[0]; + assert_equals(entries.length, 1); + assert_equals(entries[0].initiatorType, type); + assert_greater_than(performance.now(), entries[0].responseEnd + delay); + const domContentLoadedEventAbsoluteTime = + navigationEntry.domContentLoadedEventStart + + frame.contentWindow.performance.timeOrigin; + const frameResponseEndAbsoluteTime = entries[0].responseEnd + performance.timeOrigin; + assert_greater_than(domContentLoadedEventAbsoluteTime, frameResponseEndAbsoluteTime); + }, `A ${type} should report its RT entry when the response is done and before it is completely loaded`); } -function test_frame_timing_change_src(type) { - promise_test(async t => { - const {document, performance} = type === 'frame' ? window.parent : window; - const frame = document.createElement(type); - t.add_cleanup(() => frame.remove()); - await new Promise(resolve => { - const done = () => { - resolve(); - frame.removeEventListener('load', done); - } - frame.addEventListener('load', done); - frame.src = 'resources/green.html?1'; - (type === 'frame' ? document.querySelector('frameset') : document.body).appendChild(frame); - }); +function test_frame_timing_change_src(type, + origin1 = document.origin, + origin2 = document.origin, + tao = false, label = '') { + const uid = token(); + promise_test(async t => { + const {document, performance} = type === 'frame' ? window.parent : window; + const frame = document.createElement(type); + t.add_cleanup(() => frame.remove()); + function createURL(origin) { + const url = new URL(`${origin}/resource-timing/resources/green.html`, location.href); + url.searchParams.set("uid", uid); + if (tao) + url.searchParams.set("pipe", "header(Timing-Allow-Origin, *)"); + return url.toString(); + } - await new Promise(resolve => { - frame.addEventListener('load', resolve); - frame.src = 'resources/green.html?2'; - }); + await new Promise(resolve => { + const done = () => { + resolve(); + frame.removeEventListener('load', done); + } + frame.addEventListener('load', done); + frame.src = createURL(origin1); + const root = type === 'frame' ? document.querySelector('frameset') : document.body; + root.appendChild(frame); + }); - const entries = performance.getEntries().filter(e => e.name.includes('green.html')); - assert_equals(entries.length, 2); - }, `A ${type} should report separate RT entries if its src changed from the outside`); -} \ No newline at end of file + await new Promise(resolve => { + frame.addEventListener('load', resolve); + frame.src = createURL(origin2); + }); + + const entries = performance.getEntries().filter(e => e.name.includes(uid)); + assert_equals(entries.length, 2); + }, label || `A ${type} should report separate RT entries if its src changed from the outside`); +} diff --git a/test/fixtures/wpt/resource-timing/resources/get-resourceID.js b/test/fixtures/wpt/resource-timing/resources/get-resourceID.js new file mode 100644 index 00000000000000..3fe499226a405d --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resources/get-resourceID.js @@ -0,0 +1,30 @@ +function getResourceID(resourceName) { + return new Promise((resolve) => { + const observer = new PerformanceObserver((list) => { + const entries = list.getEntriesByType("resource"); + for (const entry of entries) { + if (entry.name.endsWith(resourceName)) { + observer.disconnect(); + resolve(`${entry.name}/${entry.startTime}`); + return; + } + } + }); + observer.observe({ entryTypes: ["resource"] }); + }); +} + +function getDocumentResourceID() { + return new Promise((resolve) => { + const observer = new PerformanceObserver((list) => { + const entries = list.getEntriesByType("navigation"); + if (entries.length > 0) { + observer.disconnect(); + const [entry] = entries; + const { name, startTime } = entry; + resolve(`${name}/${startTime}`); + } + }); + observer.observe({ entryTypes: ["navigation"] }); + }); +} diff --git a/test/fixtures/wpt/resource-timing/resources/loadingResources.js b/test/fixtures/wpt/resource-timing/resources/loadingResources.js new file mode 100644 index 00000000000000..e5d5d71982af05 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resources/loadingResources.js @@ -0,0 +1,21 @@ +//Fetching the Stylesheet +var link = document.createElement("link"); +link.rel = "stylesheet"; +link.href = "../resources/empty_style.css"; +document.head.appendChild(link); + +// Fetching an image +var img = document.createElement("img"); +img.src = "/images/blue.png"; +img.alt = "Sample Image for testing initiator Attribute"; +document.body.appendChild(img); + +//Inserting a html document in an iframe +var iframe = document.createElement("iframe"); +iframe.src = "../resources/green.html"; +document.body.appendChild(iframe); + +// Inserting a script element +var script = document.createElement("script"); +script.src = "../resources/empty.js"; +document.body.appendChild(script); diff --git a/test/fixtures/wpt/resource-timing/resources/nested-contexts.js b/test/fixtures/wpt/resource-timing/resources/nested-contexts.js index c0822943e86a68..31337ae5da2e18 100644 --- a/test/fixtures/wpt/resource-timing/resources/nested-contexts.js +++ b/test/fixtures/wpt/resource-timing/resources/nested-contexts.js @@ -22,22 +22,12 @@ const post_refresh_url = const setup_navigate_or_refresh = (type, pre, post) => { const verify_document_navigate_not_observable = () => { - const entries = performance.getEntriesByType("resource"); - let found_first_document = false; - for (entry of entries) { - if (entry.name == pre) { - found_first_document = true; - } - if (entry.name == post) { - opener.postMessage(`FAIL - ${type} document should not be observable`, - `*`); - return; - } - } - if (!found_first_document) { - opener.postMessage("FAIL - initial document should be observable", "*"); - return; + if (performance.getEntriesByName(post).length) { + opener.postMessage(`FAIL - ${type} document should not be observable`, + `*`); + } + opener.postMessage("PASS", "*"); } window.addEventListener("message", e => { @@ -57,21 +47,8 @@ const setup_refresh_test = () => { const setup_back_navigation = pushed_url => { const verify_document_navigate_not_observable = navigated_back => { - const entries = performance.getEntriesByType("resource"); - let found_first_document = false; - for (entry of entries) { - if (entry.name == pre_navigate_url) { - found_first_document = true; - } - if (entry.name == post_navigate_url) { - opener.postMessage("FAIL - navigated document exposed", "*"); - return; - } - } - if (!found_first_document) { - opener.postMessage(`FAIL - first document not exposed. navigated_back ` + - `is ${navigated_back}`, "*"); - return; + if (performance.getEntriesByName(post_navigate_url).length) { + opener.postMessage("FAIL - navigated document exposed", "*"); } if (navigated_back) { opener.postMessage("PASS", "*"); diff --git a/test/fixtures/wpt/resource-timing/resources/no-entries-for-cross-origin-css-fetched-memory-cache-iframe.sub.html b/test/fixtures/wpt/resource-timing/resources/no-entries-for-cross-origin-css-fetched-memory-cache-iframe.sub.html new file mode 100644 index 00000000000000..f47913468b68eb --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resources/no-entries-for-cross-origin-css-fetched-memory-cache-iframe.sub.html @@ -0,0 +1,8 @@ + + + + + + +
    Some content
+ diff --git a/test/fixtures/wpt/resource-timing/resources/parent_script.js b/test/fixtures/wpt/resource-timing/resources/parent_script.js new file mode 100644 index 00000000000000..01ec0c5823de97 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resources/parent_script.js @@ -0,0 +1 @@ +import './child.js'; diff --git a/test/fixtures/wpt/resource-timing/resources/resource-loaders.js b/test/fixtures/wpt/resource-timing/resources/resource-loaders.js index 99a2c28d2b3b27..37fea16b1750fa 100644 --- a/test/fixtures/wpt/resource-timing/resources/resource-loaders.js +++ b/test/fixtures/wpt/resource-timing/resources/resource-loaders.js @@ -1,23 +1,38 @@ const load = { - _cache_bust_value: Math.random().toString().substr(2), cache_bust: path => { let url = new URL(path, location.origin); url.href += (url.href.includes("?")) ? '&' : '?'; - url.href += "unique=" + load._cache_bust_value++ + // The `Number` type in Javascript, when interpreted as an integer, can only + // safely represent [-2^53 + 1, 2^53 - 1] without the loss of precision [1]. + // We do not generate a global value and increment from it, as the increment + // might not have enough precision to be reflected. + // + // [1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number + url.href += "unique=" + Math.random().toString().substring(2); return url.href; }, - // Returns a promise that settles once the given path has been fetched as an - // image resource. - image: path => { + image_with_attrs: async (path, attribute_map) => { return new Promise(resolve => { const img = new Image(); + for (const key in attribute_map) + img[key] = attribute_map[key]; img.onload = img.onerror = resolve; img.src = load.cache_bust(path); }); }, + // Returns a promise that settles once the given path has been fetched as an + // image resource. + image: path => { + return load.image_with_attrs(path, undefined); + }, + + // Returns a promise that settles once the given path has been fetched as an + // image resource. + image_cors: path => load.image_with_attrs(path, {crossOrigin: "anonymous"}), + // Returns a promise that settles once the given path has been fetched as a // font resource. font: path => { @@ -37,10 +52,13 @@ const load = { }); }, - // Returns a promise that settles once the given path has been fetched as a - // stylesheet resource. - stylesheet: async path => { + stylesheet_with_attrs: async (path, attribute_map) => { const link = document.createElement("link"); + if (attribute_map instanceof Object) { + for (const [key, value] of Object.entries(attribute_map)) { + link[key] = value; + } + } link.rel = "stylesheet"; link.type = "text/css"; link.href = load.cache_bust(path); @@ -54,7 +72,13 @@ const load = { document.head.removeChild(link); }, - iframe_with_attrs: async (path, attribute_map, validator) => { + // Returns a promise that settles once the given path has been fetched as a + // stylesheet resource. + stylesheet: async path => { + return load.stylesheet_with_attrs(path, undefined); + }, + + iframe_with_attrs: async (path, attribute_map, validator, skip_wait_for_navigation) => { const frame = document.createElement("iframe"); if (attribute_map instanceof Object) { for (const [key, value] of Object.entries(attribute_map)) { @@ -66,11 +90,17 @@ const load = { }); frame.src = load.cache_bust(path); document.body.appendChild(frame); - await loaded; + if ( !skip_wait_for_navigation ) { + await loaded; + } if (validator instanceof Function) { validator(frame); } - document.body.removeChild(frame); + // since we skipped the wait for load animation, we cannot + // remove the iframe here since the request could get cancelled + if ( !skip_wait_for_navigation ) { + document.body.removeChild(frame); + } }, // Returns a promise that settles once the given path has been fetched as an @@ -79,10 +109,13 @@ const load = { return load.iframe_with_attrs(path, undefined, validator); }, - // Returns a promise that settles once the given path has been fetched as a - // script. - script: async path => { + script_with_attrs: async (path, attribute_map) => { const script = document.createElement("script"); + if (attribute_map instanceof Object) { + for (const [key, value] of Object.entries(attribute_map)) { + script[key] = value; + } + } const loaded = new Promise(resolve => { script.onload = script.onerror = resolve; }); @@ -92,21 +125,29 @@ const load = { document.body.removeChild(script); }, + // Returns a promise that settles once the given path has been fetched as a + // script. + script: async path => { + return load.script_with_attrs(path, undefined); + }, + // Returns a promise that settles once the given path has been fetched as an // object. object: async (path, type) => { const object = document.createElement("object"); - const loaded = new Promise(resolve => { + const object_load_settled = new Promise(resolve => { object.onload = object.onerror = resolve; }); object.data = load.cache_bust(path); if (type) { object.type = type; } - object.style = "width: 0px; height: 0px"; document.body.appendChild(object); - await loaded; - document.body.removeChild(object); + await await_with_timeout(2000, + "Timeout was reached before load or error events fired", + object_load_settled, + () => { document.body.removeChild(object) } + ); }, // Returns a promise that settles once the given path has been fetched diff --git a/test/fixtures/wpt/resource-timing/resources/test-initiator.js b/test/fixtures/wpt/resource-timing/resources/test-initiator.js new file mode 100644 index 00000000000000..f839b463c56d8f --- /dev/null +++ b/test/fixtures/wpt/resource-timing/resources/test-initiator.js @@ -0,0 +1,16 @@ +function testResourceInitiator(resourceName, expectedInitiator) { + return new Promise(resolve => { + const observer = new PerformanceObserver(list => { + const entries = list.getEntriesByType('resource'); + for (const entry of entries) { + if (entry.name.endsWith(resourceName)) { + observer.disconnect(); + assert_equals(entry.initiator, expectedInitiator, `Test ${resourceName} initiator`); + resolve(); + return; + } + } + }); + observer.observe({entryTypes: ['resource']}); + }); +} \ No newline at end of file diff --git a/test/fixtures/wpt/resource-timing/response-status-code.html b/test/fixtures/wpt/resource-timing/response-status-code.html new file mode 100644 index 00000000000000..3a184c6f016b28 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/response-status-code.html @@ -0,0 +1,165 @@ + + + + +This test validates the response status of resources. + + + + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/sizes-redirect-img.html b/test/fixtures/wpt/resource-timing/sizes-redirect-img.html index 786018d0c4634c..e440029782b5d5 100644 --- a/test/fixtures/wpt/resource-timing/sizes-redirect-img.html +++ b/test/fixtures/wpt/resource-timing/sizes-redirect-img.html @@ -18,7 +18,7 @@ const redirectUrl = (redirectSourceOrigin, targetUrl) => { return redirectSourceOrigin + - '/resource-timing/resources/redirect-cors.py?timing_allow_origin=*' + + '/resource-timing/resources/redirect-cors.py?allow_origin=*&timing_allow_origin=*' + '&location=' + encodeURIComponent(targetUrl); }; @@ -35,18 +35,18 @@ verify_entry, "PerformanceResourceTiming sizes redirect image - same origin redirect"); -attribute_test(load.image, +attribute_test(load.image_cors, redirectUrl(hostInfo.HTTP_REMOTE_ORIGIN, baseUrl), verify_entry, "PerformanceResourceTiming sizes redirect image - cross origin redirect"); -attribute_test(load.image, +attribute_test(load.image_cors, redirectUrl(hostInfo.HTTP_REMOTE_ORIGIN, redirectUrl(hostInfo.HTTP_ORIGIN, baseUrl)), verify_entry, "PerformanceResourceTiming sizes redirect image - cross origin to same origin redirect"); -attribute_test(load.image, +attribute_test(load.image_cors, redirectUrl(hostInfo.HTTP_ORIGIN, redirectUrl(hostInfo.HTTP_REMOTE_ORIGIN, redirectUrl(hostInfo.HTTP_ORIGIN, diff --git a/test/fixtures/wpt/resource-timing/tentative/document-initiated.html b/test/fixtures/wpt/resource-timing/tentative/document-initiated.html new file mode 100644 index 00000000000000..eea2bb2761d79c --- /dev/null +++ b/test/fixtures/wpt/resource-timing/tentative/document-initiated.html @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sample Image for testing initiator Attribute + + + + + diff --git a/test/fixtures/wpt/resource-timing/tentative/script-initiated.html b/test/fixtures/wpt/resource-timing/tentative/script-initiated.html new file mode 100644 index 00000000000000..d6f3d1a32034a7 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/tentative/script-initiated.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/tentative/stylesheet-initiated.html b/test/fixtures/wpt/resource-timing/tentative/stylesheet-initiated.html new file mode 100644 index 00000000000000..d12e3d193d0837 --- /dev/null +++ b/test/fixtures/wpt/resource-timing/tentative/stylesheet-initiated.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + diff --git a/test/fixtures/wpt/resource-timing/tojson.html b/test/fixtures/wpt/resource-timing/tojson.html index 7a6187d3d55bcd..2564b855dffb8e 100644 --- a/test/fixtures/wpt/resource-timing/tojson.html +++ b/test/fixtures/wpt/resource-timing/tojson.html @@ -47,7 +47,9 @@ 'transferSize', 'encodedBodySize', 'decodedBodySize', - 'renderBlockingStatus' + 'renderBlockingStatus', + 'responseStatus', + 'contentType', ]; for (const key of performanceResourceTimingKeys) { try { diff --git a/test/fixtures/wpt/resources/check-layout-th.js b/test/fixtures/wpt/resources/check-layout-th.js index f14ca3246b8ea2..54ddb35f311200 100644 --- a/test/fixtures/wpt/resources/check-layout-th.js +++ b/test/fixtures/wpt/resources/check-layout-th.js @@ -218,6 +218,7 @@ window.checkLayout = function(selectorList, callDone = true) nodes = Array.prototype.slice.call(nodes); var checkedLayout = false; Array.prototype.forEach.call(nodes, function(node) { + const title = node.title == '' ? '' : `: ${node.title}`; test(function(t) { var container = node.parentNode.className == 'container' ? node.parentNode : node; var prefix = @@ -240,7 +241,7 @@ window.checkLayout = function(selectorList, callDone = true) } checkedLayout |= !passed; } - }, selectorList + ' ' + String(++testNumber)); + }, `${selectorList} ${++testNumber}${title}`); }); if (!checkedLayout) { console.error("No valid data-* attributes found in selector list : " + selectorList); diff --git a/test/fixtures/wpt/resources/declarative-shadow-dom-polyfill.js b/test/fixtures/wpt/resources/declarative-shadow-dom-polyfill.js deleted file mode 100644 index 99a3e911eb6336..00000000000000 --- a/test/fixtures/wpt/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/fixtures/wpt/resources/idlharness.js b/test/fixtures/wpt/resources/idlharness.js index 8f741b09b26bcb..4cf19234af20eb 100644 --- a/test/fixtures/wpt/resources/idlharness.js +++ b/test/fixtures/wpt/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/fixtures/wpt/resources/testdriver.js b/test/fixtures/wpt/resources/testdriver.js index ddf723cb3ee8a5..e737a6ee659f05 100644 --- a/test/fixtures/wpt/resources/testdriver.js +++ b/test/fixtures/wpt/resources/testdriver.js @@ -49,6 +49,58 @@ * @namespace {test_driver} */ window.test_driver = { + /** + Represents `WebDriver BiDi `_ protocol. + */ + bidi: { + /** + * `log `_ module. + */ + log: { + /** + * `log.entryAdded `_ event. + */ + entry_added: { + /** + * Subscribe to the `log.entryAdded` event. This does not + * add actual listeners. To listen to the event, use the + * `on` or `once` methods. + * @param {{contexts?: null | (string | Window)[]}} params - Parameters for the subscription. + * * `contexts`: an array of window proxies or browsing + * context ids to listen to the event. If not provided, the + * event subscription is done for the current window's + * browsing context. `null` for the global subscription. + * @return {Promise} + */ + subscribe: async function (params = {}) { + return window.test_driver_internal.bidi.log.entry_added.subscribe(params); + }, + /** + * Add an event listener for the `log.entryAdded + * `_ event. Make sure `subscribe` is + * called before using this method. + * + * @param callback {function(event): void} - The callback + * to be called when the event is fired. + * @returns {function(): void} - A function to call to + * remove the event listener. + */ + on: function (callback) { + return window.test_driver_internal.bidi.log.entry_added.on(callback); + }, + once: function () { + return new Promise(resolve => { + const remove_handler = window.test_driver_internal.bidi.log.entry_added.on( + data => { + resolve(data); + remove_handler(); + }); + }); + }, + } + } + }, + /** * Set the context in which testharness.js is loaded * @@ -357,6 +409,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 * @@ -647,7 +718,7 @@ * * This function places `Secure Payment * Confirmation `_ into - * an automated 'autoaccept' or 'autoreject' mode, to allow testing + * an automated 'autoAccept' or 'autoReject' mode, to allow testing * without user interaction with the transaction UX prompt. * * Matches the `Set SPC Transaction Mode @@ -667,8 +738,8 @@ * @param {String} mode - The `transaction mode * `_ * to set. Must be one of "``none``", - * "``autoaccept``", or - * "``autoreject``". + * "``autoAccept``", or + * "``autoReject``". * @param {WindowProxy} context - Browsing context in which * to run the call, or null for the current * browsing context. @@ -680,6 +751,42 @@ return window.test_driver_internal.set_spc_transaction_mode(mode, context); }, + /** + * Sets the current registration automation mode for Register Protocol Handlers. + * + * This function places `Register Protocol Handlers + * `_ into + * an automated 'autoAccept' or 'autoReject' mode, to allow testing + * without user interaction with the transaction UX prompt. + * + * Matches the `Set Register Protocol Handler Mode + * `_ + * WebDriver command. + * + * @example + * await test_driver.set_rph_registration_mode("autoAccept"); + * test.add_cleanup(() => { + * return test_driver.set_rph_registration_mode("none"); + * }); + * + * navigator.registerProtocolHandler('web+soup', 'soup?url=%s'); + * + * @param {String} mode - The `registration mode + * `_ + * to set. Must be one of "``none``", + * "``autoAccept``", or + * "``autoReject``". + * @param {WindowProxy} context - Browsing context in which + * to run the call, or null for the current + * browsing context. + * + * @returns {Promise} Fulfilled after the transaction mode has been set, + * or rejected if setting the mode fails. + */ + set_rph_registration_mode: function(mode, context=null) { + return window.test_driver_internal.set_rph_registration_mode(mode, context); + }, + /** * Cancels the Federated Credential Management dialog * @@ -968,6 +1075,72 @@ */ 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); + }, + + /** + * Runs the `bounce tracking timer algorithm + * `_, + * which removes all hosts from the stateful bounce tracking map, without + * regard for the bounce tracking grace period and returns a list of the + * deleted hosts. + * + * Matches the `Run Bounce Tracking Mitigations + * `_ + * 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 bounce tracking timer + * algorithm has finished running. Returns an array + * of all hosts that were in the stateful bounce + * tracking map before deletion occurred. + */ + run_bounce_tracking_mitigations: function (context = null) { + return window.test_driver_internal.run_bounce_tracking_mitigations(context); } }; @@ -980,6 +1153,21 @@ */ in_automation: false, + bidi: { + log: { + entry_added: { + async subscribe() { + throw new Error( + "bidi.log.entry_added.subscribe is not implemented by testdriver-vendor.js"); + }, + on() { + throw new Error( + "bidi.log.entry_added.on is not implemented by testdriver-vendor.js"); + } + } + } + }, + async click(element, coords) { if (this.in_automation) { throw new Error("click() is not implemented by testdriver-vendor.js"); @@ -1002,6 +1190,14 @@ throw new Error("get_named_cookie() is not implemented by testdriver-vendor.js"); }, + async get_computed_role(element) { + throw new Error("get_computed_role is a testdriver.js function which cannot be run in this context."); + }, + + async get_computed_name(element) { + throw new Error("get_computed_name is a testdriver.js function which cannot be run in this context."); + }, + async send_keys(element, keys) { if (this.in_automation) { throw new Error("send_keys() is not implemented by testdriver-vendor.js"); @@ -1046,6 +1242,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"); }, @@ -1094,6 +1294,10 @@ throw new Error("set_spc_transaction_mode() is not implemented by testdriver-vendor.js"); }, + set_rph_registration_mode: function(mode, context=null) { + return Promise.reject(new Error("unimplemented")); + }, + async cancel_fedcm_dialog(context=null) { throw new Error("cancel_fedcm_dialog() is not implemented by testdriver-vendor.js"); }, @@ -1140,6 +1344,18 @@ 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"); + }, + + async run_bounce_tracking_mitigations(context=null) { + throw new Error("run_bounce_tracking_mitigations() is not implemented by testdriver-vendor.js"); } }; })(); diff --git a/test/fixtures/wpt/resources/testharness.js b/test/fixtures/wpt/resources/testharness.js index 497ae23f0e83ee..7fd5336bf34ae9 100644 --- a/test/fixtures/wpt/resources/testharness.js +++ b/test/fixtures/wpt/resources/testharness.js @@ -91,7 +91,12 @@ } on_event(window, 'load', function() { + setTimeout(() => { this_obj.all_loaded = true; + if (tests.all_done()) { + tests.complete(); + } + },0); }); on_event(window, 'message', function(event) { @@ -852,7 +857,7 @@ promise = promiseOrConstructor; description = descriptionOrPromise; assert(maybeDescription === undefined, - "Too many args pased to no-constructor version of promise_rejects_dom"); + "Too many args passed to no-constructor version of promise_rejects_dom, or accidentally explicitly passed undefined"); } return bring_promise_to_current_realm(promise) .then(test.unreached_func("Should have rejected: " + description)) @@ -1196,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. * @@ -1207,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); } @@ -2151,7 +2174,7 @@ func = funcOrConstructor; description = descriptionOrFunc; assert(maybeDescription === undefined, - "Too many args pased to no-constructor version of assert_throws_dom"); + "Too many args passed to no-constructor version of assert_throws_dom, or accidentally explicitly passed undefined"); } assert_throws_dom_impl(type, func, description, "assert_throws_dom", constructor) } @@ -2715,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); }; @@ -2746,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) { @@ -2756,7 +2781,7 @@ "Timed out waiting on condition"); } remaining--; - setTimeout(wait_for_inner, interval); + local_set_timeout(wait_for_inner, interval); } }); @@ -4169,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) { @@ -4205,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) { @@ -4229,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); }; /* @@ -4410,13 +4408,20 @@ { var substitution_re = /\$\{([^ }]*)\}/g; - function do_substitution(input) { + function do_substitution(input) + { var components = input.split(substitution_re); var rv = []; - for (var i = 0; i < components.length; i += 2) { - rv.push(components[i]); - if (components[i + 1]) { - rv.push(String(substitutions[components[i + 1]])); + if (components.length === 1) { + rv = components; + } else if (substitutions) { + for (var i = 0; i < components.length; i += 2) { + if (components[i]) { + rv.push(components[i]); + } + if (substitutions[components[i + 1]]) { + rv.push(String(substitutions[components[i + 1]])); + } } } return rv; @@ -4772,6 +4777,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/fixtures/wpt/resources/testharnessreport.js b/test/fixtures/wpt/resources/testharnessreport.js index e5cb40fe0ef652..405a2d8b06f00f 100644 --- a/test/fixtures/wpt/resources/testharnessreport.js +++ b/test/fixtures/wpt/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/fixtures/wpt/streams/readable-byte-streams/tee.any.js b/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js index 7dd5ba3f3fb013..60d82b9cf6a1fd 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js @@ -934,3 +934,36 @@ promise_test(async () => { assert_typed_array_equals(result4.value, new Uint8Array([0]).subarray(0, 0), 'second chunk from branch2 should be correct'); }, 'ReadableStream teeing with byte source: respond() and close() while both branches are pulling'); + +promise_test(async t => { + let pullCount = 0; + const arrayBuffer = new Uint8Array([0x01, 0x02, 0x03]).buffer; + const enqueuedChunk = new Uint8Array(arrayBuffer, 2); + assert_equals(enqueuedChunk.length, 1); + assert_equals(enqueuedChunk.byteOffset, 2); + const rs = new ReadableStream({ + type: 'bytes', + pull(c) { + ++pullCount; + if (pullCount === 1) { + c.enqueue(enqueuedChunk); + } + } + }); + + const [branch1, branch2] = rs.tee(); + const reader1 = branch1.getReader(); + const reader2 = branch2.getReader(); + + const [result1, result2] = await Promise.all([reader1.read(), reader2.read()]); + assert_equals(result1.done, false, 'reader1 done'); + assert_equals(result2.done, false, 'reader2 done'); + + const view1 = result1.value; + const view2 = result2.value; + // The first stream has the transferred buffer, but the second stream has the + // cloned buffer. + const underlying = new Uint8Array([0x01, 0x02, 0x03]).buffer; + assert_typed_array_equals(view1, new Uint8Array(underlying, 2), 'reader1 value'); + assert_typed_array_equals(view2, new Uint8Array([0x03]), 'reader2 value'); +}, 'ReadableStream teeing with byte source: reading an array with a byte offset should clone correctly'); diff --git a/test/fixtures/wpt/streams/readable-streams/crashtests/from-cross-realm.https.html b/test/fixtures/wpt/streams/readable-streams/crashtests/from-cross-realm.https.html new file mode 100644 index 00000000000000..58a4371186ece7 --- /dev/null +++ b/test/fixtures/wpt/streams/readable-streams/crashtests/from-cross-realm.https.html @@ -0,0 +1,18 @@ + + + diff --git a/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js b/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js index 37f8c9df169607..3bfe634a6e153d 100644 --- a/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js +++ b/test/fixtures/wpt/streams/transferable/transfer-with-messageport.window.js @@ -105,7 +105,7 @@ async function transferMessagePortWith(constructor) { await transferMessagePortWithOrder3(new constructor()); } -async function advancedTransferMesagePortWith(constructor) { +async function advancedTransferMessagePortWith(constructor) { await transferMessagePortWithOrder4(new constructor()); await transferMessagePortWithOrder5(new constructor()); await transferMessagePortWithOrder6(new constructor()); @@ -166,7 +166,7 @@ async function mixedTransferMessagePortWithOrder3() { ); } -async function mixedTransferMesagePortWith() { +async function mixedTransferMessagePortWith() { await mixedTransferMessagePortWithOrder1(); await mixedTransferMessagePortWithOrder2(); await mixedTransferMessagePortWithOrder3(); @@ -185,19 +185,19 @@ promise_test(async t => { }, "Transferring a MessagePort with a TransformStream should set `.ports`"); promise_test(async t => { - await transferMessagePortWith(ReadableStream); + await advancedTransferMessagePortWith(ReadableStream); }, "Transferring a MessagePort with a ReadableStream should set `.ports`, advanced"); promise_test(async t => { - await transferMessagePortWith(WritableStream); + await advancedTransferMessagePortWith(WritableStream); }, "Transferring a MessagePort with a WritableStream should set `.ports`, advanced"); promise_test(async t => { - await transferMessagePortWith(TransformStream); + await advancedTransferMessagePortWith(TransformStream); }, "Transferring a MessagePort with a TransformStream should set `.ports`, advanced"); promise_test(async t => { - await mixedTransferMesagePortWith(); + await mixedTransferMessagePortWith(); }, "Transferring a MessagePort with multiple streams should set `.ports`"); test(() => { diff --git a/test/fixtures/wpt/url/META.yml b/test/fixtures/wpt/url/META.yml index 094b266b64b61b..415bd0f094c6b9 100644 --- a/test/fixtures/wpt/url/META.yml +++ b/test/fixtures/wpt/url/META.yml @@ -3,5 +3,4 @@ suggested_reviewers: - mikewest - domenic - annevk - - GPHemsley - TimothyGu diff --git a/test/fixtures/wpt/url/README.md b/test/fixtures/wpt/url/README.md index fa5e3b0dc72385..50227bc4b330ef 100644 --- a/test/fixtures/wpt/url/README.md +++ b/test/fixtures/wpt/url/README.md @@ -1,11 +1,13 @@ -## urltestdata.json +## urltestdata.json / urltestdata-javascript-only.json -`resources/urltestdata.json` contains URL parsing tests suitable for any URL parser implementation. +[`resources/urltestdata.json`](resources/urltestdata.json) contains URL parsing tests suitable for any URL parser implementation. +[`resources/urltestdata-javascript-only.json`](resources/urltestdata-javascript-only.json) contains URL parsing tests specifically meant +for JavaScript's `URL()` class as well as other languages accepting non-scalar-value strings. -It's used as a source of tests by `a-element.html`, `failure.html`, `url-constructor.any.js`, and -other test files in this directory. +These files are used as a source of tests by `a-element.html`, `failure.html`, `url-constructor.any.js`, +and other test files in this directory. -The format of `resources/urltestdata.json` is a JSON array of comments as strings and test cases as +Both files share the same format. They consist of a JSON array of comments as strings and test cases as objects. The keys for each test case are: * `input`: a string to be parsed as URL. diff --git a/test/fixtures/wpt/url/a-element-origin-xhtml.xhtml b/test/fixtures/wpt/url/a-element-origin-xhtml.xhtml index effcf04bee3fb0..e68e68dda2ad6c 100644 --- a/test/fixtures/wpt/url/a-element-origin-xhtml.xhtml +++ b/test/fixtures/wpt/url/a-element-origin-xhtml.xhtml @@ -12,4 +12,8 @@ - + diff --git a/test/fixtures/wpt/url/a-element-origin.html b/test/fixtures/wpt/url/a-element-origin.html index 9cc8e94cbed060..7015f853f01a1d 100644 --- a/test/fixtures/wpt/url/a-element-origin.html +++ b/test/fixtures/wpt/url/a-element-origin.html @@ -5,4 +5,8 @@
- + diff --git a/test/fixtures/wpt/url/a-element-xhtml.xhtml b/test/fixtures/wpt/url/a-element-xhtml.xhtml index 05bec4ce4b2f1e..610481a7819d62 100644 --- a/test/fixtures/wpt/url/a-element-xhtml.xhtml +++ b/test/fixtures/wpt/url/a-element-xhtml.xhtml @@ -17,4 +17,8 @@ - + diff --git a/test/fixtures/wpt/url/a-element.html b/test/fixtures/wpt/url/a-element.html index 3428fa00574c4d..a7621d2ded76c4 100644 --- a/test/fixtures/wpt/url/a-element.html +++ b/test/fixtures/wpt/url/a-element.html @@ -10,7 +10,11 @@
- +
Link with embedded \n is parsed correctly diff --git a/test/fixtures/wpt/url/failure.html b/test/fixtures/wpt/url/failure.html index e61f462f97456f..d95b1d52d67237 100644 --- a/test/fixtures/wpt/url/failure.html +++ b/test/fixtures/wpt/url/failure.html @@ -6,7 +6,10 @@
+ + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html b/test/fixtures/wpt/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html new file mode 100644 index 00000000000000..0d5c7ca6ed57fd --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html @@ -0,0 +1,21 @@ + +script-src blocks Wasm execution + + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/source-phase.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/source-phase.tentative.html new file mode 100644 index 00000000000000..870b16bd0a0ce8 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/source-phase.tentative.html @@ -0,0 +1,35 @@ + +Source phase imports + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html new file mode 100644 index 00000000000000..e193f3efc69330 --- /dev/null +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html @@ -0,0 +1,13 @@ + +Testing import of WebAssembly source phase from JavaScript worker + + + + diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html index 8002e07ce7f1cf..6145dd04ff8c8d 100644 --- a/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html +++ b/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html @@ -10,4 +10,7 @@ assert_equals(msg, 42); done(); } +worker.onerror = () => { + assert_unreached("worker got an error"); +} diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/WEB_FEATURES.yml b/test/fixtures/wpt/webmessaging/broadcastchannel/WEB_FEATURES.yml new file mode 100644 index 00000000000000..378ed57dc52d07 --- /dev/null +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: broadcast-channel + files: "**" diff --git a/test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js b/test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js index 68b4706028f5aa..eec09d65a3aba2 100644 --- a/test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js +++ b/test/fixtures/wpt/webmessaging/broadcastchannel/basics.any.js @@ -1,3 +1,11 @@ +test(function() { + assert_throws_js( + TypeError, + () => BroadcastChannel(""), + "Calling BroadcastChannel constructor without 'new' must throw" + ); +}, "BroadcastChannel constructor called as normal function"); + async_test(t => { let c1 = new BroadcastChannel('eventType'); let c2 = new BroadcastChannel('eventType'); diff --git a/test/fixtures/wpt/webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.tentative.html b/test/fixtures/wpt/webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html similarity index 100% rename from test/fixtures/wpt/webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.tentative.html rename to test/fixtures/wpt/webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html diff --git a/test/fixtures/wpt/webstorage/localstorage-basic-partitioned.tentative.sub.html b/test/fixtures/wpt/webstorage/localstorage-basic-partitioned.sub.html similarity index 97% rename from test/fixtures/wpt/webstorage/localstorage-basic-partitioned.tentative.sub.html rename to test/fixtures/wpt/webstorage/localstorage-basic-partitioned.sub.html index 7ed49b1e9a0eb5..3d5b1e93ce5ef6 100644 --- a/test/fixtures/wpt/webstorage/localstorage-basic-partitioned.tentative.sub.html +++ b/test/fixtures/wpt/webstorage/localstorage-basic-partitioned.sub.html @@ -38,7 +38,7 @@ assert_true(typeof sameSiteID === "string"); if (location.origin !== altOrigin) { - crossSiteWindow = window.open(`${altOrigin}/webstorage/localstorage-basic-partitioned.tentative.sub.html`, "", "noopener=false"); + crossSiteWindow = window.open(`${altOrigin}/webstorage/localstorage-basic-partitioned.sub.html`, "", "noopener=false"); t.add_cleanup(() => crossSiteWindow.close()); } } diff --git a/test/fixtures/wpt/webstorage/localstorage-cross-origin-iframe.tentative.https.window.js b/test/fixtures/wpt/webstorage/localstorage-cross-origin-iframe.https.window.js similarity index 100% rename from test/fixtures/wpt/webstorage/localstorage-cross-origin-iframe.tentative.https.window.js rename to test/fixtures/wpt/webstorage/localstorage-cross-origin-iframe.https.window.js diff --git a/test/fixtures/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub.html b/test/fixtures/wpt/webstorage/sessionStorage-basic-partitioned.sub.html similarity index 97% rename from test/fixtures/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub.html rename to test/fixtures/wpt/webstorage/sessionStorage-basic-partitioned.sub.html index 30575bfaf1a579..38525b99a23497 100644 --- a/test/fixtures/wpt/webstorage/sessionStorage-basic-partitioned.tentative.sub.html +++ b/test/fixtures/wpt/webstorage/sessionStorage-basic-partitioned.sub.html @@ -47,7 +47,7 @@ // new cross-site window that contains our shared-iframe to repeat // the process in a cross-site environment. if (location.origin !== altOrigin) { - crossSiteWindow = window.open(`${altOrigin}/webstorage/sessionStorage-basic-partitioned.tentative.sub.html`, "", "noopener=false"); + crossSiteWindow = window.open(`${altOrigin}/webstorage/sessionStorage-basic-partitioned.sub.html`, "", "noopener=false"); t.add_cleanup(() => crossSiteWindow.close()); } } diff --git a/test/wpt/status/FileAPI/blob.json b/test/wpt/status/FileAPI/blob.json index 8ea03bbc019992..2cdc267f0af840 100644 --- a/test/wpt/status/FileAPI/blob.json +++ b/test/wpt/status/FileAPI/blob.json @@ -17,6 +17,7 @@ "ArrayBuffer elements of the blobParts array should be supported.", "Passing typed arrays as elements of the blobParts array should work.", "Passing a Float64Array as element of the blobParts array should work.", + "Passing a Float16Array as element of the blobParts array should work.", "Passing BigInt typed arrays as elements of the blobParts array should work.", "Array with two blobs", "Array with two buffers", diff --git a/test/wpt/status/WebCryptoAPI.json b/test/wpt/status/WebCryptoAPI.json new file mode 100644 index 00000000000000..3cc9a4a838a6f8 --- /dev/null +++ b/test/wpt/status/WebCryptoAPI.json @@ -0,0 +1,16 @@ +{ + "algorithm-discards-context.https.window.js": { + "skip": "Not relevant in Node.js context" + }, + "historical.any.js": { + "skip": "Not relevant in Node.js context" + }, + "getRandomValues.any.js": { + "fail": { + "note": "These types do not exist in Node.js", + "expected": [ + "Float16 arrays" + ] + } + } +} diff --git a/test/wpt/status/html/webappapis/timers.json b/test/wpt/status/html/webappapis/timers.json index 21e77a089d5ca7..ee467f5384d03f 100644 --- a/test/wpt/status/html/webappapis/timers.json +++ b/test/wpt/status/html/webappapis/timers.json @@ -1,5 +1,8 @@ { "negative-settimeout.any.js": { "skip": "unreliable in Node.js; Refs: https://github.com/nodejs/node/issues/37672" + }, + "evil-spec-example.any.js": { + "skip": "Node.js does not support a string as a parameter to setTimeout" } } diff --git a/test/wpt/status/performance-timeline.json b/test/wpt/status/performance-timeline.json index 73e1aad56cf707..5ce28d5730767a 100644 --- a/test/wpt/status/performance-timeline.json +++ b/test/wpt/status/performance-timeline.json @@ -7,6 +7,9 @@ ] } }, + "droppedentriescount.any.js": { + "skip": "Node.js cannot open files with query params" + }, "idlharness.any.js": { "fail": { "note": "not implemented", @@ -21,5 +24,55 @@ }, "webtiming-resolution.any.js": { "skip": "flaky" + }, + "not-restored-reasons/performance-navigation-timing-attributes.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-bfcache-reasons-stay.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-bfcache.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-fetch.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-lock.https.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-not-bfcached.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-redirect-on-history.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-reload.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-same-origin-bfcache.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "not-restored-reasons/performance-navigation-timing-same-origin-replace.tentative.window.js": { + "skip": "Depends on HTML WebPlatformTests" + }, + "idlharness-shadowrealm.window.js": { + "skip": "Requires ShadowRealm" + }, + "not-restored-reasons/abort-block-bfcache.window.js": { + "fail": { + "note": "Requires 'window.stop()'", + "expected": [ + "aborting a parser should block bfcache." + ] + } } } diff --git a/test/wpt/status/resource-timing.json b/test/wpt/status/resource-timing.json index f40f8fe51da117..a0dbbb764362af 100644 --- a/test/wpt/status/resource-timing.json +++ b/test/wpt/status/resource-timing.json @@ -20,6 +20,9 @@ "buffered-flag.any.js": { "skip": "Browser-specific test" }, + "delivery-type.tentative.any.js": { + "skip": "Browser-specific test" + }, "idlharness.any.js": { "fail": { "expected": [ diff --git a/test/wpt/status/webstorage.json b/test/wpt/status/webstorage.json index 4ecdb2721bd24b..10171601480aad 100644 --- a/test/wpt/status/webstorage.json +++ b/test/wpt/status/webstorage.json @@ -8,6 +8,9 @@ "localstorage-cross-origin-iframe.tentative.https.window.js": { "skip": "iframes are not supported in Node.js." }, + "localstorage-cross-origin-iframe.https.window.js": { + "skip": "iframes are not supported in Node.js." + }, "storage_local_window_open.window.js": { "skip": "window.open() is not supported in Node.js." },