aboutsummaryrefslogtreecommitdiffstats
path: root/tests/wpt/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/wpt/tests')
-rw-r--r--tests/wpt/tests/WebCryptoAPI/sign_verify/eddsa.js4
-rw-r--r--tests/wpt/tests/audio-output/setSinkId-with-selectAudioOutput.https.html2
-rw-r--r--tests/wpt/tests/bluetooth/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html1
-rw-r--r--tests/wpt/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html1
-rw-r--r--tests/wpt/tests/clipboard-apis/resources/user-activation.js2
-rw-r--r--tests/wpt/tests/common/media.js4
-rw-r--r--tests/wpt/tests/compression/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/content-security-policy/media-src/media-src-7_1.html6
-rw-r--r--tests/wpt/tests/content-security-policy/media-src/media-src-7_1_2.sub.html4
-rw-r--r--tests/wpt/tests/content-security-policy/media-src/media-src-7_2_2.sub.html2
-rw-r--r--tests/wpt/tests/content-security-policy/media-src/media-src-7_3.sub.html4
-rw-r--r--tests/wpt/tests/content-security-policy/media-src/media-src-7_3_2.sub.html4
-rw-r--r--tests/wpt/tests/content-security-policy/media-src/media-src-blocked.sub.html10
-rw-r--r--tests/wpt/tests/content-security-policy/media-src/media-src-redir-bug.sub.html12
-rw-r--r--tests/wpt/tests/css/CSS2/floats/block-in-inline-become-float.html23
-rw-r--r--tests/wpt/tests/css/compositing/mix-blend-mode/support/RGB_Circles.oggtheora.ogvbin66830 -> 0 bytes
-rw-r--r--tests/wpt/tests/css/css-align/parsing/gap-shorthand.html2
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-column-gap-computed.html29
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-column-gap-invalid.html24
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-column-gap-valid.html24
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-gap-computed.html36
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-gap-invalid.html22
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-gap-shorthand.html36
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-gap-valid.html27
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-row-gap-computed.html31
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-row-gap-invalid.html22
-rw-r--r--tests/wpt/tests/css/css-align/parsing/grid-row-gap-valid.html23
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-position-dynamic-005.html45
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012-ref.html39
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012.html62
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-transition-attr.html51
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-transition-default.html54
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-transition-eval.html69
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-transition-name.html56
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-typed-om.html55
-rw-r--r--tests/wpt/tests/css/css-anchor-position/chrome-40286059-crash.html7
-rw-r--r--tests/wpt/tests/css/css-anchor-position/inset-area-basic.html8
-rw-r--r--tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-computed.html9
-rw-r--r--tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-parsing.html15
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-001.html84
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-002.html87
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-003.html95
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-004.html99
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-005.html76
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-006.html65
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-basics.html40
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-try-order-basic.html197
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-try-transition-basic.html58
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-try-transition-flip.html68
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-try-typed-om.html55
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in-ref.html32
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.tentative.html64
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out-ref.html22
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.tentative.html59
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed-ref.html5
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed.tentative.html38
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor-ref.html29
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor.tentative.html68
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility-ref.html16
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.tentative.html50
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility-ref.html3
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility.tentative.html35
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container-ref.html10
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.tentative.html65
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed-ref.html9
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed.tentative.html42
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html21
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-stacked-child.tentative.html60
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-with-position.tentative.html50
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html39
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll-ref.html30
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll.tentative.html44
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible-ref.html25
-rw-r--r--tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible.tentative.html61
-rw-r--r--tests/wpt/tests/css/css-anchor-position/property-interpolations.html11
-rw-r--r--tests/wpt/tests/css/css-anchor-position/try-tactic-wm.html56
-rw-r--r--tests/wpt/tests/css/css-animations/WEB_FEATURES.yml5
-rw-r--r--tests/wpt/tests/css/css-animations/parsing/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/css-backgrounds/WEB_FEATURES.yml9
-rw-r--r--tests/wpt/tests/css/css-backgrounds/animations/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/css-backgrounds/background-clip/WEB_FEATURES.yml10
-rw-r--r--tests/wpt/tests/css/css-backgrounds/background-clip/list.txt12
-rw-r--r--tests/wpt/tests/css/css-backgrounds/background-origin/list.txt12
-rw-r--r--tests/wpt/tests/css/css-backgrounds/parsing/WEB_FEATURES.yml7
-rw-r--r--tests/wpt/tests/css/css-break/transform-022-print-ref.html (renamed from tests/wpt/tests/css/printing/transform-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-break/transform-022-print.html (renamed from tests/wpt/tests/css/printing/transform-001-print.html)2
-rw-r--r--tests/wpt/tests/css/css-break/transform-023-print-ref.html (renamed from tests/wpt/tests/css/printing/transform-002-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-break/transform-023-print.html (renamed from tests/wpt/tests/css/printing/transform-002-print.html)2
-rw-r--r--tests/wpt/tests/css/css-break/transform-024-print-ref.html (renamed from tests/wpt/tests/css/printing/transform-003-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-break/transform-024-print.html (renamed from tests/wpt/tests/css/printing/transform-003-print.html)2
-rw-r--r--tests/wpt/tests/css/css-cascade/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/css-cascade/parsing/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/css-color/WEB_FEATURES.yml22
-rw-r--r--tests/wpt/tests/css/css-color/clip-opacity-out-of-flow-ref.html5
-rw-r--r--tests/wpt/tests/css/css-color/clip-opacity-out-of-flow.html19
-rw-r--r--tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml5
-rw-r--r--tests/wpt/tests/css/css-color/parsing/color-computed-color-mix-function.html2
-rw-r--r--tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html43
-rw-r--r--tests/wpt/tests/css/css-color/parsing/color-invalid-color-mix-function.html2
-rw-r--r--tests/wpt/tests/css/css-color/parsing/color-valid-color-mix-function.html2
-rw-r--r--tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml13
-rw-r--r--tests/wpt/tests/css/css-fonts/animations/WEB_FEATURES.yml6
-rw-r--r--tests/wpt/tests/css/css-fonts/parsing/WEB_FEATURES.yml14
-rw-r--r--tests/wpt/tests/css/css-fonts/variations/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/css-grid/subgrid/placement-invalidation-001.html50
-rw-r--r--tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops-ref.html47
-rw-r--r--tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops.html61
-rw-r--r--tests/wpt/tests/css/css-masking/animations/clip-path-interpolation-shape.html114
-rw-r--r--tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-003.html37
-rw-r--r--tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-004.html37
-rw-r--r--tests/wpt/tests/css/css-masking/clip-path/clip-path-scaled-video.html12
-rw-r--r--tests/wpt/tests/css/css-multicol/crashtests/block-in-inline-become-float.html15
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-001.tentative.html21
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-002.tentative.html20
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-003.tentative.html20
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-004.tentative.html22
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-005.tentative.html23
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-006.tentative.html24
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-007.tentative.html26
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-008.tentative.html42
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-009.tentative.html44
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-010.tentative.html22
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-011.tentative.html23
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-012.tentative.html28
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-013.tentative.html29
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-014.tentative.html22
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-015.tentative.html24
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-016.tentative.html23
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-017.tentative.html28
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-018.tentative.html27
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-019.tentative.html24
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-020.tentative.html20
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-001.tentative.html29
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-002.tentative.html34
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-003.tentative.html29
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-004.tentative.html34
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-005.tentative.html29
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-006.tentative.html34
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-007.tentative.html30
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-008.tentative.html31
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-009.tentative.html31
-rw-r--r--tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-010.tentative.html32
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-010-ref.html14
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-011-ref.html17
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-012-ref.html21
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-013-ref.html22
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-015-ref.html17
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-001-ref.html23
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-005-ref.html23
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-006-ref.html28
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-007-ref.html24
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-008-ref.html25
-rw-r--r--tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-010-ref.html26
-rw-r--r--tests/wpt/tests/css/css-page/crashtests/root-element-remove-print.html (renamed from tests/wpt/tests/css/printing/crashtests/root-element-remove-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/crashtests/tall-inline-block-in-float-in-table-cell-print.html (renamed from tests/wpt/tests/css/printing/crashtests/tall-inline-block-in-float-in-table-cell-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-001-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-001-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-001-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-002-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-002-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-002-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-002-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-003-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-003-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-003-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-003-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-004-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-004-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-004-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-004-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-005-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-005-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-005-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-005-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-006-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-006-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-006-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-006-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-007-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-007-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-007-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-007-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-008-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-008-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-008-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-008-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-with-abspos-with-link-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-with-abspos-with-link-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-with-abspos-with-link-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-with-abspos-with-link-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-with-iframe-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-with-iframe-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-with-iframe-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-with-iframe-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-with-link-with-inline-child-print-ref.html (renamed from tests/wpt/tests/css/printing/fixedpos-with-link-with-inline-child-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/fixedpos-with-link-with-inline-child-print.html (renamed from tests/wpt/tests/css/printing/fixedpos-with-link-with-inline-child-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/media-queries-001-print-ref.html (renamed from tests/wpt/tests/css/printing/media-queries-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/media-queries-001-print.html (renamed from tests/wpt/tests/css/printing/media-queries-001-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/media-queries-002-print.html (renamed from tests/wpt/tests/css/printing/media-queries-002-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/media-queries-003-print-ref.html (renamed from tests/wpt/tests/css/printing/media-queries-003-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/media-queries-003-print.html (renamed from tests/wpt/tests/css/printing/media-queries-003-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-001-print-ref.html (renamed from tests/wpt/tests/css/printing/page-margin-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-001-print.html (renamed from tests/wpt/tests/css/printing/page-margin-001-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-002-print-ref.html (renamed from tests/wpt/tests/css/printing/page-margin-002-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-002-print.html (renamed from tests/wpt/tests/css/printing/page-margin-002-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-003-print-ref.html (renamed from tests/wpt/tests/css/printing/page-margin-003-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-003-print.html (renamed from tests/wpt/tests/css/printing/page-margin-003-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-004-print-ref.html (renamed from tests/wpt/tests/css/printing/page-margin-004-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-004-print.html (renamed from tests/wpt/tests/css/printing/page-margin-004-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-005-print-ref.html (renamed from tests/wpt/tests/css/printing/page-margin-005-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-005-print.html (renamed from tests/wpt/tests/css/printing/page-margin-005-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-006-print-ref.html (renamed from tests/wpt/tests/css/printing/page-margin-006-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-margin-006-print.html (renamed from tests/wpt/tests/css/printing/page-margin-006-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-000-print-ref.html (renamed from tests/wpt/tests/css/printing/page-name-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-000-print.html (renamed from tests/wpt/tests/css/printing/page-name-001-print.html)2
-rw-r--r--tests/wpt/tests/css/css-page/page-name-002-print-ref.html (renamed from tests/wpt/tests/css/printing/page-name-002-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-002-print.html (renamed from tests/wpt/tests/css/printing/page-name-002-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-003-print-ref.html (renamed from tests/wpt/tests/css/printing/page-name-003-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-003-print.html (renamed from tests/wpt/tests/css/printing/page-name-003-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-and-break-001-print.html (renamed from tests/wpt/tests/css/printing/page-name-and-break-001-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-and-break-002-print.html (renamed from tests/wpt/tests/css/printing/page-name-and-break-002-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-and-break-003-print.html (renamed from tests/wpt/tests/css/printing/page-name-and-break-003-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-and-break-004-print.html (renamed from tests/wpt/tests/css/printing/page-name-and-break-004-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-name-and-break-print-ref.html (renamed from tests/wpt/tests/css/printing/page-name-and-break-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-001-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-001-print.html (renamed from tests/wpt/tests/css/printing/page-size-001-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-002-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-002-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-002-print.html (renamed from tests/wpt/tests/css/printing/page-size-002-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-003-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-003-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-003-print.html (renamed from tests/wpt/tests/css/printing/page-size-003-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-004-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-004-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-004-print.html (renamed from tests/wpt/tests/css/printing/page-size-004-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-005-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-005-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-005-print.html (renamed from tests/wpt/tests/css/printing/page-size-005-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-006-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-006-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-006-print.html (renamed from tests/wpt/tests/css/printing/page-size-006-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-007-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-007-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-007-print.html (renamed from tests/wpt/tests/css/printing/page-size-007-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-008-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-008-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-008-print.html (renamed from tests/wpt/tests/css/printing/page-size-008-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-009-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-009-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-009-print.html (renamed from tests/wpt/tests/css/printing/page-size-009-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-010-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-010-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-010-print.html (renamed from tests/wpt/tests/css/printing/page-size-010-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-011-print-ref.html (renamed from tests/wpt/tests/css/printing/page-size-011-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-size-011-print.html (renamed from tests/wpt/tests/css/printing/page-size-011-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-visibility-hidden-001-print-ref.html (renamed from tests/wpt/tests/css/printing/page-visibility-hidden-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/page-visibility-hidden-001-print.html (renamed from tests/wpt/tests/css/printing/page-visibility-hidden-001-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/reference/blank-print-ref.html (renamed from tests/wpt/tests/css/printing/reference/blank-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/reference/filled-green-100px-square-print-ref.html (renamed from tests/wpt/tests/css/printing/reference/filled-green-100px-square-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/remote-origin-iframe-print-ref.html (renamed from tests/wpt/tests/css/printing/remote-origin-iframe-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/remote-origin-iframe-print.html (renamed from tests/wpt/tests/css/printing/remote-origin-iframe-print.html)2
-rw-r--r--tests/wpt/tests/css/css-page/resources/iframe-with-abspos.html (renamed from tests/wpt/tests/css/printing/resources/iframe-with-abspos.html)0
-rw-r--r--tests/wpt/tests/css/css-page/resources/mq-frame-100px.html (renamed from tests/wpt/tests/css/printing/resources/mq-frame-100px.html)0
-rw-r--r--tests/wpt/tests/css/css-page/root-element-display-none-print.html (renamed from tests/wpt/tests/css/printing/root-element-display-none-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/subpixel-page-size-001-print-ref.html (renamed from tests/wpt/tests/css/printing/subpixel-page-size-001-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/subpixel-page-size-001-print.html (renamed from tests/wpt/tests/css/printing/subpixel-page-size-001-print.html)0
-rw-r--r--tests/wpt/tests/css/css-page/subpixel-page-size-002-print-ref.html (renamed from tests/wpt/tests/css/printing/subpixel-page-size-002-print-ref.html)0
-rw-r--r--tests/wpt/tests/css/css-page/subpixel-page-size-002-print.html (renamed from tests/wpt/tests/css/printing/subpixel-page-size-002-print.html)0
-rw-r--r--tests/wpt/tests/css/css-paint-api/parse-input-arguments-018.https.html16
-rw-r--r--tests/wpt/tests/css/css-scoping/host-defined.html24
-rw-r--r--tests/wpt/tests/css/css-scroll-snap-2/resources/common.js26
-rw-r--r--tests/wpt/tests/css/css-scroll-snap-2/resources/programmatic-scroll-common.js26
-rw-r--r--tests/wpt/tests/css/css-scroll-snap-2/resources/user-scroll-common.js31
-rw-r--r--tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html7
-rw-r--r--tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html9
-rw-r--r--tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html5
-rw-r--r--tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html6
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-001.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-001.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-002.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-002.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-003.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-003.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-004.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-004.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-005.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-005.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-006.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-006.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-007.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-007.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-008.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-008.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-009.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-009.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-010.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-010.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-color-dynamic-8.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-color-dynamic-8.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-width-010.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-width-010.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-width-011.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-width-011.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-width-012.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-width-012.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-width-013.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-width-013.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-width-014.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-width-014.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-width-015.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-width-015.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-scrollbars/scrollbar-width-016.html (renamed from tests/wpt/tests/css/css-scrollbars/scrollbar-width-016.tentative.html)2
-rw-r--r--tests/wpt/tests/css/css-sizing/aspect-ratio/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.ogvbin7660 -> 0 bytes
-rw-r--r--tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.webmbin0 -> 555 bytes
-rw-r--r--tests/wpt/tests/css/css-text/line-breaking/line-breaking-029.html46
-rw-r--r--tests/wpt/tests/css/css-text/parsing/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control-ref.html13
-rw-r--r--tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control.html16
-rw-r--r--tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br-ref.html14
-rw-r--r--tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br.html18
-rw-r--r--tests/wpt/tests/css/css-text/text-spacing-trim/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/css/css-text/white-space/text-wrap-balance-right-to-left.html34
-rw-r--r--tests/wpt/tests/css/css-text/white-space/text-wrap-balance-top-to-bottom.html35
-rw-r--r--tests/wpt/tests/css/css-transforms/WEB_FEATURES.yml6
-rw-r--r--tests/wpt/tests/css/css-transforms/animation/WEB_FEATURES.yml5
-rw-r--r--tests/wpt/tests/css/css-transforms/parsing/WEB_FEATURES.yml5
-rw-r--r--tests/wpt/tests/css/css-transitions/parsing/starting-style-parsing.html38
-rw-r--r--tests/wpt/tests/css/css-transitions/starting-style-adjustment.html23
-rw-r--r--tests/wpt/tests/css/css-ui/WEB_FEATURES.yml7
-rw-r--r--tests/wpt/tests/css/css-ui/animation/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/css-ui/resize-014.html3
-rw-r--r--tests/wpt/tests/css/css-ui/support/test.ogvbin287648 -> 0 bytes
-rw-r--r--tests/wpt/tests/css/css-values/WEB_FEATURES.yml17
-rw-r--r--tests/wpt/tests/css/css-values/container-progress-computed.tentative.html80
-rw-r--r--tests/wpt/tests/css/css-values/container-progress-invalid.tentative.html55
-rw-r--r--tests/wpt/tests/css/css-values/container-progress-serialize.tentative.html48
-rw-r--r--tests/wpt/tests/css/css-values/media-progress-computed.tentative.html28
-rw-r--r--tests/wpt/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html4
-rw-r--r--tests/wpt/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html40
-rw-r--r--tests/wpt/tests/css/css-view-transitions/update-callback-timeout.html25
-rw-r--r--tests/wpt/tests/css/cssom-view/WEB_FEATURES.yml5
-rw-r--r--tests/wpt/tests/css/filter-effects/WEB_FEATURES.yml7
-rw-r--r--tests/wpt/tests/css/filter-effects/animation/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/filter-effects/parsing/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/css/motion/animation/offset-path-interpolation-008.html104
-rw-r--r--tests/wpt/tests/css/selectors/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/css/selectors/dir-pseudo-on-input-element.html14
-rw-r--r--tests/wpt/tests/css/selectors/invalidation/WEB_FEATURES.yml6
-rw-r--r--tests/wpt/tests/css/selectors/invalidation/is-where-pseudo-containing-hard-pseudo.html100
-rw-r--r--tests/wpt/tests/css/selectors/parsing/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html2
-rw-r--r--tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html2
-rw-r--r--tests/wpt/tests/device-posture/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/document-policy/experimental-features/resources/video.ogvbin103746 -> 0 bytes
-rw-r--r--tests/wpt/tests/document-policy/experimental-features/resources/video.webmbin0 -> 81605 bytes
-rw-r--r--tests/wpt/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html2
-rw-r--r--tests/wpt/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html46
-rw-r--r--tests/wpt/tests/dom/observable/tentative/observable-first.any.js114
-rw-r--r--tests/wpt/tests/dom/observable/tentative/observable-last.any.js113
-rw-r--r--tests/wpt/tests/dom/observable/tentative/observable-switchMap.any.js252
-rw-r--r--tests/wpt/tests/editing/crashtests/caret-display-list-002.html35
-rw-r--r--tests/wpt/tests/event-timing/interactionid-orphan-pointerup.html31
-rw-r--r--tests/wpt/tests/event-timing/resources/event-timing-test-utils.js12
-rw-r--r--tests/wpt/tests/fenced-frame/notify-event-iframe.https.html24
-rw-r--r--tests/wpt/tests/fetch/api/request/destination/resources/dummy_video.ogvbin94372 -> 0 bytes
-rw-r--r--tests/wpt/tests/fetch/api/request/request-bad-port.any.js2
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data.tentative.https.html54
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html57
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html70
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html51
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html60
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py4
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py28
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js120
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py10
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/resources/empty.html1
-rw-r--r--tests/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py37
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js12
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js55
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/br-body.https.any.js15
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py3
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/resources/big.text.brbin0 -> 49 bytes
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers3
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.brbin0 -> 15 bytes
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers2
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.brbin0 -> 15 bytes
-rw-r--r--tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers2
-rw-r--r--tests/wpt/tests/fledge/tentative/additional-bids.https.window.js267
-rw-r--r--tests/wpt/tests/fledge/tentative/auction-config.https.window.js64
-rw-r--r--tests/wpt/tests/fledge/tentative/component-ads.https.window.js4
-rw-r--r--tests/wpt/tests/fledge/tentative/component-auction.https.window.js4
-rw-r--r--tests/wpt/tests/fledge/tentative/cross-origin.https.window.js2
-rw-r--r--tests/wpt/tests/fledge/tentative/currency.https.window.js2
-rw-r--r--tests/wpt/tests/fledge/tentative/generate-bid-browser-signals.https.window.js2
-rw-r--r--tests/wpt/tests/fledge/tentative/resources/additional-bids.py64
-rw-r--r--tests/wpt/tests/fledge/tentative/resources/ed25519.py289
-rw-r--r--tests/wpt/tests/fledge/tentative/resources/fledge-util.sub.js210
-rw-r--r--tests/wpt/tests/fledge/tentative/resources/worklet-helpers.js4
-rw-r--r--tests/wpt/tests/fledge/tentative/tie.https.window.js2
-rw-r--r--tests/wpt/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html44
-rw-r--r--tests/wpt/tests/fonts/math/stretchy.woffbin1428 -> 1464 bytes
-rw-r--r--tests/wpt/tests/fs/script-tests/FileSystemWritableFileStream-write.js10
-rw-r--r--tests/wpt/tests/html-media-capture/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html3
-rw-r--r--tests/wpt/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html57
-rw-r--r--tests/wpt/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html57
-rw-r--r--tests/wpt/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html7
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html57
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js57
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html57
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js57
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html11
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js11
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js2
-rw-r--r--tests/wpt/tests/html/canvas/tools/gentestutilsunion.py421
-rw-r--r--tests/wpt/tests/html/canvas/tools/templates/reftest.html2
-rw-r--r--tests/wpt/tests/html/canvas/tools/templates/reftest_element.html3
-rw-r--r--tests/wpt/tests/html/canvas/tools/templates/reftest_offscreen.html2
-rw-r--r--tests/wpt/tests/html/canvas/tools/templates/reftest_worker.html2
-rw-r--r--tests/wpt/tests/html/canvas/tools/templates/testharness_element.html24
-rw-r--r--tests/wpt/tests/html/canvas/tools/templates/testharness_offscreen.html18
-rw-r--r--tests/wpt/tests/html/canvas/tools/templates/testharness_worker.js17
-rw-r--r--tests/wpt/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml2
-rw-r--r--tests/wpt/tests/html/canvas/tools/yaml-new/filters.yaml4
-rw-r--r--tests/wpt/tests/html/canvas/tools/yaml-new/layers.yaml1
-rw-r--r--tests/wpt/tests/html/dom/historical.html5
-rw-r--r--tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-manual.tentative.html19
-rw-r--r--tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-same-element-manual.tentative.html15
-rw-r--r--tests/wpt/tests/html/editing/dnd/drop/events-input-manual.tentative.html16
-rw-r--r--tests/wpt/tests/html/editing/dnd/drop/events-input-same-element-manual.tentative.html15
-rw-r--r--tests/wpt/tests/html/editing/dnd/drop/events-textarea-manual.tentative.html16
-rw-r--r--tests/wpt/tests/html/editing/dnd/drop/events-textarea-same-element-manual.tentative.html15
-rw-r--r--tests/wpt/tests/html/editing/dnd/drop/support/events.js31
-rw-r--r--tests/wpt/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js2
-rw-r--r--tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents-ref.html3
-rw-r--r--tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents.html12
-rw-r--r--tests/wpt/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html6
-rw-r--r--tests/wpt/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html4
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html2
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html2
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html2
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html2
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html2
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html2
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html6
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html2
-rw-r--r--tests/wpt/tests/html/semantics/invokers/interestelement-interface.tentative.html1
-rw-r--r--tests/wpt/tests/html/semantics/invokers/interestevent-dispatch-shadow.tentative.html104
-rw-r--r--tests/wpt/tests/html/semantics/invokers/interestevent-interface.tentative.html167
-rw-r--r--tests/wpt/tests/html/semantics/invokers/interesttarget-button-event-dispatch.tentative.html155
-rw-r--r--tests/wpt/tests/html/semantics/invokers/interesttarget-on-popover-behavior.tentative.html113
-rw-r--r--tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html394
-rw-r--r--tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-invalid-behavior.tentative.html120
-rw-r--r--tests/wpt/tests/html/semantics/invokers/invoketarget-on-input-number.tentative.html78
-rw-r--r--tests/wpt/tests/html/semantics/invokers/resources/invoker-utils.js19
-rw-r--r--tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html4
-rw-r--r--tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html4
-rw-r--r--tests/wpt/tests/html/semantics/permission-element/bounded-css-properties.tentative.html47
-rw-r--r--tests/wpt/tests/html/semantics/popovers/popover-light-dismiss.html52
-rw-r--r--tests/wpt/tests/import-maps/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/intersection-observer/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/intersection-observer/svg-container-element.html5
-rw-r--r--tests/wpt/tests/intersection-observer/v2/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/jpegxl/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/largest-contentful-paint/transparent-text.html48
-rw-r--r--tests/wpt/tests/lint.ignore4
-rw-r--r--tests/wpt/tests/long-animation-frame/tentative/loaf-stream-source-location.html3
-rw-r--r--tests/wpt/tests/long-animation-frame/tentative/loaf-stream.html2
-rw-r--r--tests/wpt/tests/mathml/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/mathml/presentation-markup/operators/mo-axis-height-1.html169
-rw-r--r--tests/wpt/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html60
-rw-r--r--tests/wpt/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html3
-rwxr-xr-xtests/wpt/tests/mathml/tools/stretchy.py11
-rw-r--r--tests/wpt/tests/media/A4.ogvbin94372 -> 0 bytes
-rw-r--r--tests/wpt/tests/media/counting.ogvbin187773 -> 0 bytes
-rw-r--r--tests/wpt/tests/media/green-at-15.ogvbin287648 -> 0 bytes
-rw-r--r--tests/wpt/tests/media/movie_300.ogvbin2344665 -> 0 bytes
-rw-r--r--tests/wpt/tests/media/movie_5.ogvbin18645 -> 0 bytes
-rw-r--r--tests/wpt/tests/media/test.ogvbin146510 -> 0 bytes
-rw-r--r--tests/wpt/tests/media/video.ogvbin53189 -> 0 bytes
-rw-r--r--tests/wpt/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html4
-rw-r--r--tests/wpt/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html8
-rw-r--r--tests/wpt/tests/mixed-content/tentative/resources/test.ogvbin2344665 -> 0 bytes
-rw-r--r--tests/wpt/tests/mixed-content/tentative/resources/test.webmbin0 -> 19054 bytes
-rw-r--r--tests/wpt/tests/payment-method-basic-card/billing-address-is-null-manual.https.html6
-rw-r--r--tests/wpt/tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html109
-rw-r--r--tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-call-immediate-manual.https.html206
-rw-r--r--tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-duplicate-shipping-options-manual.https.html106
-rw-r--r--tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html196
-rw-r--r--tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-method-abort-update-manual.https.html286
-rw-r--r--tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html125
-rw-r--r--tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html8
-rw-r--r--tests/wpt/tests/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html103
-rw-r--r--tests/wpt/tests/payment-request/algorithms-manual.https.html176
-rw-r--r--tests/wpt/tests/payment-request/billing-address-changed-manual.https.html115
-rw-r--r--tests/wpt/tests/payment-request/change-shipping-option-manual.https.html104
-rw-r--r--tests/wpt/tests/payment-request/change-shipping-option-select-last-manual.https.html101
-rw-r--r--tests/wpt/tests/payment-request/dynamically-change-shipping-options-manual.https.html142
-rw-r--r--tests/wpt/tests/payment-request/historical.https.html4
-rw-r--r--tests/wpt/tests/payment-request/payment-request-constructor-thcrash.https.html254
-rw-r--r--tests/wpt/tests/payment-request/payment-request-constructor.https.sub.html222
-rw-r--r--tests/wpt/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html57
-rw-r--r--tests/wpt/tests/payment-request/payment-request-onshippingaddresschange-attribute.https.html78
-rw-r--r--tests/wpt/tests/payment-request/payment-request-onshippingoptionchange-attribute.https.html79
-rw-r--r--tests/wpt/tests/payment-request/payment-request-shippingAddress-attribute.https.html28
-rw-r--r--tests/wpt/tests/payment-request/payment-request-shippingOption-attribute.https.html100
-rw-r--r--tests/wpt/tests/payment-request/payment-request-shippingType-attribute.https.html72
-rw-r--r--tests/wpt/tests/payment-request/payment-response/helpers.js40
-rw-r--r--tests/wpt/tests/payment-request/payment-response/retry-method-manual.https.html296
-rw-r--r--tests/wpt/tests/payment-request/payment-response/retry-method-warnings-manual.https.html158
-rw-r--r--tests/wpt/tests/payment-request/payment-response/shippingAddress-attribute-manual.https.html101
-rw-r--r--tests/wpt/tests/payment-request/payment-response/shippingOption-attribute-manual.https.html43
-rw-r--r--tests/wpt/tests/payment-request/shipping-address-changed-manual.https.html99
-rw-r--r--tests/wpt/tests/payment-request/show-method-optional-promise-rejects.https.html40
-rw-r--r--tests/wpt/tests/payment-request/show-method-optional-promise-resolves-manual.https.html339
-rw-r--r--tests/wpt/tests/payment-request/updateWith-method-pmi-handling-manual.https.html140
-rw-r--r--tests/wpt/tests/payment-request/user-accepts-payment-request-algo-manual.https.html230
-rw-r--r--tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js3
-rw-r--r--tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js103
-rw-r--r--tests/wpt/tests/performance-timeline/not-restored-reasons/test-helper.js6
-rw-r--r--tests/wpt/tests/pointerlock/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/preload/resources/A4.ogvbin94372 -> 0 bytes
-rw-r--r--tests/wpt/tests/resource-timing/initiator-type/video.html4
-rw-r--r--tests/wpt/tests/scroll-animations/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/selection/crashtests/selection-modify-line-next-to-input-and-make-it-invisible.html23
-rw-r--r--tests/wpt/tests/selection/selection-nested-video.html25
-rw-r--r--tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6-ref.html12
-rw-r--r--tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6.html44
-rw-r--r--tests/wpt/tests/server-timing/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/shadow-dom/declarative/gethtml.html74
-rw-r--r--tests/wpt/tests/shadow-dom/host-with-namespace.xhtml27
-rw-r--r--tests/wpt/tests/speculation-rules/prerender/resources/request-picture-in-picture.html2
-rw-r--r--tests/wpt/tests/streams/piping/crashtests/cross-piping2.https.html14
-rw-r--r--tests/wpt/tests/svg/linking/reftests/url-processing-invalid-base.svg15
-rw-r--r--tests/wpt/tests/tools/web_features/manifest.py2
-rw-r--r--tests/wpt/tests/tools/web_features/tests/test_manifest.py3
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/base.py157
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py8
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py1
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py1
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/sauce.py1
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/servodriver.py1
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/wktr.py1
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/environment.py43
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/testloader.py5
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py2
-rw-r--r--tests/wpt/tests/tools/wptserve/wptserve/constants.py1
-rw-r--r--tests/wpt/tests/tools/wptserve/wptserve/utils.py2
-rw-r--r--tests/wpt/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html9
-rw-r--r--tests/wpt/tests/trusted-types/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/uievents/mouse/mouse_boundary_events_after_reappending_last_over_target.tentative.html152
-rw-r--r--tests/wpt/tests/url/resources/urltestdata.json28
-rw-r--r--tests/wpt/tests/visual-viewport/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/wasm/jsapi/module/moduleSource.tentative.any.js16
-rw-r--r--tests/wpt/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js7
-rw-r--r--tests/wpt/tests/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html21
-rw-r--r--tests/wpt/tests/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.html21
-rw-r--r--tests/wpt/tests/wasm/webapi/esm-integration/source-phase.tentative.html2
-rw-r--r--tests/wpt/tests/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html13
-rw-r--r--tests/wpt/tests/webdriver/tests/classic/element_click/scroll_into_view.py8
-rw-r--r--tests/wpt/tests/webmidi/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js50
-rw-r--r--tests/wpt/tests/webnn/validation_tests/gruCell.https.any.js316
-rw-r--r--tests/wpt/tests/webnn/validation_tests/pooling.https.any.js263
-rw-r--r--tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk-worker.js13
-rw-r--r--tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html16
-rw-r--r--tests/wpt/tests/webrtc/RTCPeerConnection-GC.https.html30
-rw-r--r--tests/wpt/tests/websockets/Create-blocked-port.any.js2
-rw-r--r--tests/wpt/tests/webtransport/datagram-bad-chunk.https.any.js15
-rw-r--r--tests/wpt/tests/webtransport/sendstream-bad-chunk.https.any.js18
618 files changed, 14265 insertions, 1878 deletions
diff --git a/tests/wpt/tests/WebCryptoAPI/sign_verify/eddsa.js b/tests/wpt/tests/WebCryptoAPI/sign_verify/eddsa.js
index 4952df502d8..74452d26091 100644
--- a/tests/wpt/tests/WebCryptoAPI/sign_verify/eddsa.js
+++ b/tests/wpt/tests/WebCryptoAPI/sign_verify/eddsa.js
@@ -229,10 +229,10 @@ function run_test() {
publicKey = await subtle.importKey("raw", test.keyData, algorithm, false, ["verify"])
isVerified = await subtle.verify(algorithm, publicKey, test.signature, test.message);
} catch (err) {
- assert_false(publicKey === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''");
+ assert_true(publicKey !== undefined, "Public key should be valid.");
assert_unreached("The operation shouldn't fail, but it thown this error: " + err.name + ": " + err.message + ".");
}
- assert_false(isVerified, "Signature verification result.");
+ assert_equals(isVerified, test.verified, "Signature verification result.");
}, algorithmName + " Verification checks with small-order key of order - Test " + test.id);
});
});
diff --git a/tests/wpt/tests/audio-output/setSinkId-with-selectAudioOutput.https.html b/tests/wpt/tests/audio-output/setSinkId-with-selectAudioOutput.https.html
index dbe32e26069..a78d91dd12f 100644
--- a/tests/wpt/tests/audio-output/setSinkId-with-selectAudioOutput.https.html
+++ b/tests/wpt/tests/audio-output/setSinkId-with-selectAudioOutput.https.html
@@ -29,7 +29,7 @@ promise_test(async t => {
assert_equals(r, undefined, "resetting sinkid on default audio output should always work");
}, "setSinkId() after selectAudioOutput()");
-const src = "/media/movie_5.ogv";
+const src = "/media/movie_5.webm";
promise_test(async t => {
assert_not_equals(deviceId, undefined, "selectAudioOutput() resolved");
diff --git a/tests/wpt/tests/bluetooth/WEB_FEATURES.yml b/tests/wpt/tests/bluetooth/WEB_FEATURES.yml
new file mode 100644
index 00000000000..9b01ed7dae7
--- /dev/null
+++ b/tests/wpt/tests/bluetooth/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: web-bluetooth
+ files: "**"
diff --git a/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html b/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html
index a13493f7947..8ca1819df8b 100644
--- a/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html
+++ b/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html
@@ -5,6 +5,7 @@
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
<script>
'use strict';
diff --git a/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html b/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html
index fb076feac8b..b328debde29 100644
--- a/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html
+++ b/tests/wpt/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html
@@ -5,6 +5,7 @@
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
<script>
'use strict';
diff --git a/tests/wpt/tests/clipboard-apis/resources/user-activation.js b/tests/wpt/tests/clipboard-apis/resources/user-activation.js
index 6fa5bcba405..4535f8c6d74 100644
--- a/tests/wpt/tests/clipboard-apis/resources/user-activation.js
+++ b/tests/wpt/tests/clipboard-apis/resources/user-activation.js
@@ -34,7 +34,7 @@ async function trySetPermission(perm, state) {
}
}
-async function tryGrantReadPermission(state) {
+async function tryGrantReadPermission() {
await trySetPermission("clipboard-read", "granted");
}
diff --git a/tests/wpt/tests/common/media.js b/tests/wpt/tests/common/media.js
index 800593f5343..a5a8e957e9b 100644
--- a/tests/wpt/tests/common/media.js
+++ b/tests/wpt/tests/common/media.js
@@ -14,9 +14,6 @@ function getVideoURI(base)
if (videotag.canPlayType('video/webm; codecs="vp9, opus"') )
{
extension = '.webm';
- } else if ( videotag.canPlayType('video/ogg; codecs="theora, vorbis"') )
- {
- extension = '.ogv';
}
}
@@ -52,7 +49,6 @@ function getMediaContentType(url) {
var extension = new URL(url, location).pathname.split(".").pop();
var map = {
"mp4" : "video/mp4",
- "ogv" : "application/ogg",
"webm": "video/webm",
"mp3" : "audio/mp3",
"oga" : "application/ogg",
diff --git a/tests/wpt/tests/compression/WEB_FEATURES.yml b/tests/wpt/tests/compression/WEB_FEATURES.yml
new file mode 100644
index 00000000000..c8b072631ea
--- /dev/null
+++ b/tests/wpt/tests/compression/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: compression-streams
+ files: "**"
diff --git a/tests/wpt/tests/content-security-policy/media-src/media-src-7_1.html b/tests/wpt/tests/content-security-policy/media-src/media-src-7_1.html
index 8fd094e9553..9cf142185a6 100644
--- a/tests/wpt/tests/content-security-policy/media-src/media-src-7_1.html
+++ b/tests/wpt/tests/content-security-policy/media-src/media-src-7_1.html
@@ -35,14 +35,14 @@
<video id="videoObject" width="320" height="240" controls
onloadeddata="media_loaded(source_test)">
<source id="videoSourceObject"
- type="video/ogg"
+ type="video/webm"
onerror="media_error_handler(source_test)"
- src="/media/A4.ogv">
+ src="/media/A4.webm">
</video>
<video id="videoObject2" width="320" height="240" controls
onerror="media_error_handler(src_test)"
onloadeddata="media_loaded(src_test)"
- src="/media/A4.ogv">
+ src="/media/A4.webm">
</body>
</html>
diff --git a/tests/wpt/tests/content-security-policy/media-src/media-src-7_1_2.sub.html b/tests/wpt/tests/content-security-policy/media-src/media-src-7_1_2.sub.html
index 8312defb2e1..579668c83ee 100644
--- a/tests/wpt/tests/content-security-policy/media-src/media-src-7_1_2.sub.html
+++ b/tests/wpt/tests/content-security-policy/media-src/media-src-7_1_2.sub.html
@@ -24,7 +24,7 @@
}));
// we assume tests are run from 'hostname' and 'www.hostname' or 'www2.hostname' is a valid alias
- var mediaURL = location.protocol + "//{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv";
+ var mediaURL = location.protocol + "//{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm";
function media_loaded(t) {
t.step( function () {
@@ -41,7 +41,7 @@
<video id="videoObject" width="320" height="240" controls
onloadeddata="media_loaded(source_test)">
<source id="videoSourceObject"
- type="video/ogg"
+ type="video/webm"
onerror="media_error_handler(source_test)">
</video>
<video id="videoObject2" width="320" height="240" controls
diff --git a/tests/wpt/tests/content-security-policy/media-src/media-src-7_2_2.sub.html b/tests/wpt/tests/content-security-policy/media-src/media-src-7_2_2.sub.html
index ce6b8add3c8..e1627b138c9 100644
--- a/tests/wpt/tests/content-security-policy/media-src/media-src-7_2_2.sub.html
+++ b/tests/wpt/tests/content-security-policy/media-src/media-src-7_2_2.sub.html
@@ -48,7 +48,7 @@
<script>
let source = document.createElement("source");
source.src = mediaURL;
- source.type = "audio/ogg";
+ source.type = "audio/webm";
source.onerror = _ => { media_error_handler(source_test); }
document.getElementById("audioObject").appendChild(source);
document.getElementById("audioObject2").src = mediaURL;
diff --git a/tests/wpt/tests/content-security-policy/media-src/media-src-7_3.sub.html b/tests/wpt/tests/content-security-policy/media-src/media-src-7_3.sub.html
index 46489e2668f..2acae07b7ed 100644
--- a/tests/wpt/tests/content-security-policy/media-src/media-src-7_3.sub.html
+++ b/tests/wpt/tests/content-security-policy/media-src/media-src-7_3.sub.html
@@ -37,8 +37,8 @@
<video id="videoObject" width="320" height="240" controls
onloadeddata="media_loaded(source_test)" crossorigin>
<source id="audioSourceObject"
- type="audio/ogg"
- src="/media/A4.ogv">
+ type="video/webm"
+ src="/media/A4.webm">
<track id="trackObject"
kind="subtitles"
srclang="en"
diff --git a/tests/wpt/tests/content-security-policy/media-src/media-src-7_3_2.sub.html b/tests/wpt/tests/content-security-policy/media-src/media-src-7_3_2.sub.html
index 431a58608ab..cb3558e6c2a 100644
--- a/tests/wpt/tests/content-security-policy/media-src/media-src-7_3_2.sub.html
+++ b/tests/wpt/tests/content-security-policy/media-src/media-src-7_3_2.sub.html
@@ -43,8 +43,8 @@
onerror="media_error_handler(source_test)"
crossorigin>
<source id="audioSourceObject"
- type="audio/ogg"
- src="/media/A4.ogv">
+ type="video/webm"
+ src="/media/A4.webm">
<track default
id="trackObject"
kind="subtitles"
diff --git a/tests/wpt/tests/content-security-policy/media-src/media-src-blocked.sub.html b/tests/wpt/tests/content-security-policy/media-src/media-src-blocked.sub.html
index b2b57dec64e..16c36dfb204 100644
--- a/tests/wpt/tests/content-security-policy/media-src/media-src-blocked.sub.html
+++ b/tests/wpt/tests/content-security-policy/media-src/media-src-blocked.sub.html
@@ -14,7 +14,7 @@
<script>
const otherOrigin = get_host_info().OTHER_ORIGIN;
const audioUrl = otherOrigin + "/media/sound_5.oga";
- const videoUrl = otherOrigin + "/media/A4.ogv";
+ const videoUrl = otherOrigin + "/media/A4.webm";
// Asynchronously returns the next `securitypolicyviolation` event.
async function nextViolation() {
@@ -29,7 +29,7 @@
const violationPromise = nextViolation();
const video = document.createElement("video");
- video.type = "video/ogg";
+ video.type = "video/webm";
video.src = videoUrl;
video.onloadeddata = reject;
video.onerror = () => { resolve(violationPromise); };
@@ -49,7 +49,7 @@
video.onloadeddata = reject;
const source = document.createElement("source");
- source.type = "video/ogg";
+ source.type = "video/webm";
source.src = videoUrl;
source.onerror = () => { resolve(violationPromise); };
@@ -64,7 +64,7 @@
const violationPromise = nextViolation();
const audio = document.createElement("audio");
- audio.type = "audio/ogg";
+ audio.type = "audio/webm";
audio.src = audioUrl;
audio.oncanplay = reject;
audio.onloadedmetadata = reject;
@@ -86,7 +86,7 @@
audio.onloadeddata = reject;
const source = document.createElement("source");
- source.type = "audio/ogg";
+ source.type = "audio/webm";
source.src = audioUrl;
source.onerror = () => { resolve(violationPromise); };
diff --git a/tests/wpt/tests/content-security-policy/media-src/media-src-redir-bug.sub.html b/tests/wpt/tests/content-security-policy/media-src/media-src-redir-bug.sub.html
index a0708bf5ed3..ac8c167d5fe 100644
--- a/tests/wpt/tests/content-security-policy/media-src/media-src-redir-bug.sub.html
+++ b/tests/wpt/tests/content-security-policy/media-src/media-src-redir-bug.sub.html
@@ -44,28 +44,28 @@
<video id="videoObject" width="320" height="240" controls
onloadeddata="media_loaded(source_test)">
<source id="videoSourceObject"
- type="video/ogg"
+ type="video/webm"
onerror="media_error_handler(source_test)"
- src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv">
+ src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm">
</video>
<video id="videoObject2" width="320" height="240" controls
onerror="media_error_handler(src_test)"
onloadeddata="media_loaded(src_test)"
- src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv">
+ src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm">
<video id="videoObject3" width="320" height="240" controls
onloadeddata="media_loaded(source_redir_test)">
<source id="videoSourceObject"
- type="video/ogg"
+ type="video/webm"
onerror="media_error_handler(source_test)"
- src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv">
+ src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm">
</video>
<video id="videoObject2" width="320" height="240" controls
onerror="media_error_handler(src_redir_test)"
onloadeddata="media_loaded(src_redir_test)"
- src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv">
+ src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm">
</body>
</html>
diff --git a/tests/wpt/tests/css/CSS2/floats/block-in-inline-become-float.html b/tests/wpt/tests/css/CSS2/floats/block-in-inline-become-float.html
new file mode 100644
index 00000000000..2a415e18380
--- /dev/null
+++ b/tests/wpt/tests/css/CSS2/floats/block-in-inline-become-float.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="http://crbug.com/332396356">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<style>
+ .half {
+ display: inline-block;
+ vertical-align: top;
+ width: 50px;
+ height: 100px;
+ background: green;
+ }
+</style>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width:100px; height:100px; background:red;">
+ <span>
+ <div class="half"></div><div id="trouble"></div><div class="half"></div>
+ </span>
+</div>
+<script>
+ document.body.offsetTop;
+ trouble.style.cssFloat = "left";
+</script>
diff --git a/tests/wpt/tests/css/compositing/mix-blend-mode/support/RGB_Circles.oggtheora.ogv b/tests/wpt/tests/css/compositing/mix-blend-mode/support/RGB_Circles.oggtheora.ogv
deleted file mode 100644
index baed6e2f5df..00000000000
--- a/tests/wpt/tests/css/compositing/mix-blend-mode/support/RGB_Circles.oggtheora.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/css/css-align/parsing/gap-shorthand.html b/tests/wpt/tests/css/css-align/parsing/gap-shorthand.html
index 52397fb744b..6095104fa3b 100644
--- a/tests/wpt/tests/css/css-align/parsing/gap-shorthand.html
+++ b/tests/wpt/tests/css/css-align/parsing/gap-shorthand.html
@@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>CSS Box Alignment Level 3: gap sets longhands</title>
<link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-gap">
-<meta name="assert" content="row-gap supports the full grammar '<row-gap> <column-gap>?'.">
+<meta name="assert" content="gap supports the full grammar '<row-gap> <column-gap>?'.">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/shorthand-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-column-gap-computed.html b/tests/wpt/tests/css/css-align/parsing/grid-column-gap-computed.html
new file mode 100644
index 00000000000..ae43a2a3f5f
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-column-gap-computed.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().gridColumnGap</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-column-gap computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<style>
+ #target {
+ font-size: 40px;
+ }
+</style>
+<script>
+test_computed_value("grid-column-gap", "normal");
+
+test_computed_value("grid-column-gap", "calc(10px + 0.5em)", "30px");
+test_computed_value("grid-column-gap", "calc(10px - 0.5em)", "0px");
+test_computed_value("grid-column-gap", "40%");
+test_computed_value("grid-column-gap", "calc(50% + 60px)");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-column-gap-invalid.html b/tests/wpt/tests/css/css-align/parsing/grid-column-gap-invalid.html
new file mode 100644
index 00000000000..a43f05dbe1b
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-column-gap-invalid.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: parsing grid-column-gap with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-column-gap supports only the grammar '<length-percentage> | normal'.">
+<meta name="assert" content="grid-column-gap rejects negative <length-percentage>.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("grid-column-gap", "auto");
+
+test_invalid_value("grid-column-gap", "10");
+test_invalid_value("grid-column-gap", "10px 20px");
+test_invalid_value("grid-column-gap", "-1px");
+test_invalid_value("grid-column-gap", "-10%");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-column-gap-valid.html b/tests/wpt/tests/css/css-align/parsing/grid-column-gap-valid.html
new file mode 100644
index 00000000000..b96e36e4343
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-column-gap-valid.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: parsing grid-column-gap with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-column-gap supports the full grammar '<length-percentage> | normal'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("grid-column-gap", "normal");
+
+test_valid_value("grid-column-gap", "0", "0px");
+test_valid_value("grid-column-gap", "1px");
+test_valid_value("grid-column-gap", "calc(2em + 3ex)");
+test_valid_value("grid-column-gap", "4%");
+test_valid_value("grid-column-gap", "5vmin");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-gap-computed.html b/tests/wpt/tests/css/css-align/parsing/grid-gap-computed.html
new file mode 100644
index 00000000000..eee24ad653b
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-gap-computed.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().gridGap</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-gap computed value is a pair of keyword or <length-percentage> values.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+ #target {
+ font-size: 40px;
+ }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("grid-gap", "normal");
+test_computed_value("grid-gap", "10px");
+test_computed_value("grid-gap", "20%");
+test_computed_value("grid-gap", "calc(20% + 10px)");
+test_computed_value("grid-gap", "calc(-0.5em + 10px)", "0px");
+test_computed_value("grid-gap", "calc(0.5em + 10px)", "30px");
+
+test_computed_value("grid-gap", "normal 10px");
+test_computed_value("grid-gap", "10px 20%");
+test_computed_value("grid-gap", "20% calc(20% + 10px)");
+test_computed_value("grid-gap", "calc(20% + 10px) normal");
+
+test_computed_value("grid-gap", "calc(-0.5em + 10px) calc(0.5em + 10px)", "0px 30px");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-gap-invalid.html b/tests/wpt/tests/css/css-align/parsing/grid-gap-invalid.html
new file mode 100644
index 00000000000..7536b091d8c
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-gap-invalid.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: parsing grid-gap with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-gap supports only the grammar '<grid-row-gap> <grid-column-gap>?'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("grid-gap", "auto");
+test_invalid_value("grid-gap", "-10px");
+
+test_invalid_value("grid-gap", "10px 20% 30px");
+test_invalid_value("grid-gap", "normal 10px normal");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-gap-shorthand.html b/tests/wpt/tests/css/css-align/parsing/grid-gap-shorthand.html
new file mode 100644
index 00000000000..61ded441763
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-gap-shorthand.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: grid-gap sets longhands</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-gap supports the full grammar '<grid-row-gap> <column-gap>?'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/shorthand-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_shorthand_value('gap', 'normal', {
+ 'row-gap': 'normal',
+ 'column-gap': 'normal'
+});
+
+test_shorthand_value('gap', '10px 20%', {
+ 'row-gap': '10px',
+ 'column-gap': '20%'
+});
+
+test_shorthand_value('gap', '10px normal', {
+ 'row-gap': '10px',
+ 'column-gap': 'normal'
+});
+
+test_shorthand_value('gap', 'normal calc(20% + 10px)', {
+ 'grid-row-gap': 'normal',
+ 'grid-column-gap': 'calc(20% + 10px)'
+});
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-gap-valid.html b/tests/wpt/tests/css/css-align/parsing/grid-gap-valid.html
new file mode 100644
index 00000000000..c70b9205fd4
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-gap-valid.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: parsing grid-gap with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-gap supports the full grammar '<grid-row-gap> <grid-column-gap>?'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("grid-gap", "normal normal", "normal");
+test_valid_value("grid-gap", "10px 10px", "10px");
+test_valid_value("grid-gap", "20% 20%", "20%");
+test_valid_value("grid-gap", "calc(20% + 10px) calc(20% + 10px)", "calc(20% + 10px)");
+
+test_valid_value("grid-gap", "normal 10px");
+test_valid_value("grid-gap", "10px 20%");
+test_valid_value("grid-gap", "20% calc(20% + 10px)");
+test_valid_value("grid-gap", "calc(20% + 10px) 0px");
+test_valid_value("grid-gap", "0px normal");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-row-gap-computed.html b/tests/wpt/tests/css/css-align/parsing/grid-row-gap-computed.html
new file mode 100644
index 00000000000..d108cb2737b
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-row-gap-computed.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: getComputedStyle().gridRowGap</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-row-gap computed value is a specified keyword or a computed <length-percentage>.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<style>
+ #target {
+ font-size: 40px;
+ }
+</style>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("grid-row-gap", "normal");
+
+test_computed_value("grid-row-gap", "10px");
+test_computed_value("grid-row-gap", "20%");
+test_computed_value("grid-row-gap", "calc(20% + 10px)");
+
+test_computed_value("grid-row-gap", "calc(-0.5em + 10px)", "0px");
+test_computed_value("grid-row-gap", "calc(0.5em + 10px)", "30px");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-row-gap-invalid.html b/tests/wpt/tests/css/css-align/parsing/grid-row-gap-invalid.html
new file mode 100644
index 00000000000..39d0835a48f
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-row-gap-invalid.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: parsing grid-row-gap with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-row-gap supports only the grammar 'normal | <length-percentage>'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("grid-row-gap", "auto");
+test_invalid_value("grid-row-gap", "-10px");
+
+test_invalid_value("grid-row-gap", "10px 20%");
+test_invalid_value("grid-row-gap", "normal 10px");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-align/parsing/grid-row-gap-valid.html b/tests/wpt/tests/css/css-align/parsing/grid-row-gap-valid.html
new file mode 100644
index 00000000000..fe123763951
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/parsing/grid-row-gap-valid.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Box Alignment Level 3: parsing grid-row-gap with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy">
+<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com">
+<meta name="assert" content="grid-row-gap supports the full grammar 'normal | <length-percentage>'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("grid-row-gap", "normal");
+
+test_valid_value("grid-row-gap", "10px");
+test_valid_value("grid-row-gap", "20%");
+test_valid_value("grid-row-gap", "calc(20% + 10px)");
+test_valid_value("grid-row-gap", "0", "0px");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-dynamic-005.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-dynamic-005.html
new file mode 100644
index 00000000000..b22a060cbda
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-dynamic-005.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<style>
+ #cb {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ overflow: clip;
+ }
+ #anchor {
+ position: absolute;
+ inset: 0;
+ background: green;
+ width: 100px;
+ height: 100px;
+ anchor-name: --a;
+ }
+ #bug {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ background: red;
+ left: anchor(right);
+ top: anchor(top);
+ position-anchor: --a;
+ }
+ #target {
+ position: absolute;
+ top: anchor(top);
+ }
+</style>
+<p>Test passes if there is a filled green square.</p>
+<div id="cb">
+ <div id="anchor"></div>
+ <div>
+ <div id=bug></div>
+ </div>
+ <div id=target></div>
+</div>
+<script>
+document.body.offsetTop;
+document.getElementById('target').style.top = 'calc(anchor(top) + 10px)';
+</script>
+
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012-ref.html b/tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012-ref.html
new file mode 100644
index 00000000000..fbc0b5fc6da
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012-ref.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<style>
+#scroller {
+ width: 400px;
+ height: 400px;
+ overflow-y: scroll;
+}
+
+.box {
+ min-height: 100px;
+ width: 100px;
+}
+
+#anchor {
+ background: orange;
+}
+
+#anchored {
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+</style>
+
+<div id="scroller">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box" id="anchor"></div>
+ <div id="anchored"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+</div>
+
+<script>
+scroller.scrollTop = 150;
+</script>
+
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012.html b/tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012.html
new file mode 100644
index 00000000000..7c0b381999d
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-scroll-position-try-012.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Tests position fallback change on scroll with anchor and anchored under the same scroll container</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#scroll">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback">
+<link rel="match" href="anchor-scroll-position-try-012-ref.html">
+<style>
+#scroller {
+ position: relative;
+ width: 400px;
+ height: 400px;
+ overflow-y: scroll;
+}
+
+.box {
+ min-height: 100px;
+ width: 100px;
+}
+
+#anchor {
+ anchor-name: --a;
+ background: orange;
+}
+
+#anchored {
+ position: absolute;
+ top: anchor(--a bottom);
+ width: 100px;
+ height: 100px;
+ background: green;
+ position-anchor: --a;
+ position-try-options: --pf;
+}
+
+@position-try --pf {
+ top: auto;
+ bottom: anchor(--a top);
+}
+</style>
+
+<div id="scroller">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box" id="anchor"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ <div id="anchored"></div>
+</div>
+
+<script>
+requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ scroller.scrollTop = 150;
+ document.documentElement.classList.remove('reftest-wait');
+ });
+});
+</script>
+</html>
+
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-transition-attr.html b/tests/wpt/tests/css/css-anchor-position/anchor-transition-attr.html
new file mode 100644
index 00000000000..50f1154532e
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-transition-attr.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: Transition when the anchor attribute changes</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #cb {
+ display: inline-block;
+ position: relative;
+ width: 250px;
+ height: 250px;
+ border: 1px solid black;
+ }
+ #anchor1, #anchor2 {
+ width: 100px;
+ }
+ #anchor1 {
+ background: wheat;
+ height: 50px;
+ }
+ #anchor2 {
+ background: tomato;
+ height: 90px;
+ }
+ #anchored {
+ position: absolute;
+ width: anchor-size(width);
+ height: anchor-size(height);
+ top: anchor(top);
+ left: anchor(right);
+ transition-duration: 1000s;
+ transition-timing-function: steps(2, start);
+ transition-property: top, height;
+ background-color: skyblue;
+ }
+</style>
+<div id=cb>
+ <div id=anchor1>Anchor1</div>
+ <div id=anchor2>Anchor2</div>
+ <div id=anchored anchor=anchor1></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(anchored.offsetTop, 0);
+ assert_equals(anchored.offsetHeight, 50);
+ anchored.setAttribute('anchor', 'anchor2');
+ assert_equals(anchored.offsetTop, 25);
+ assert_equals(anchored.offsetHeight, 70);
+ }, 'Transition when the anchor attribute changes');
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-transition-default.html b/tests/wpt/tests/css/css-anchor-position/anchor-transition-default.html
new file mode 100644
index 00000000000..1bee0cbbe4c
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-transition-default.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: Transition when position-anchor changes</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #cb {
+ display: inline-block;
+ position: relative;
+ width: 250px;
+ height: 250px;
+ border: 1px solid black;
+ }
+ #anchor1, #anchor2 {
+ width: 100px;
+ }
+ #anchor1 {
+ background: wheat;
+ height: 50px;
+ anchor-name: --a1;
+ }
+ #anchor2 {
+ background: tomato;
+ height: 90px;
+ anchor-name: --a2;
+ }
+ #anchored {
+ position: absolute;
+ width: anchor-size(width);
+ height: anchor-size(height);
+ top: anchor(top);
+ left: anchor(right);
+ transition-duration: 1000s;
+ transition-timing-function: steps(2, start);
+ transition-property: top, height;
+ background-color: skyblue;
+ position-anchor: --a1;
+ }
+</style>
+<div id=cb>
+ <div id=anchor1>Anchor1</div>
+ <div id=anchor2>Anchor2</div>
+ <div id=anchored></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(anchored.offsetTop, 0);
+ assert_equals(anchored.offsetHeight, 50);
+ anchored.style.positionAnchor = '--a2';
+ assert_equals(anchored.offsetTop, 25);
+ assert_equals(anchored.offsetHeight, 70);
+ }, 'Transition when position-anchor changes');
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-transition-eval.html b/tests/wpt/tests/css/css-anchor-position/anchor-transition-eval.html
new file mode 100644
index 00000000000..cf65742b2dc
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-transition-eval.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: Transition when the result of anchor()/anchor-size() changes</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .cb {
+ display: inline-block;
+ position: relative;
+ width: 250px;
+ height: 250px;
+ border: 1px solid black;
+ }
+ .anchor {
+ position: absolute;
+ width: 50px;
+ height: 40px;
+ left: 60px;
+ top: 40px;
+ background: coral;
+ anchor-name: --a;
+ }
+ .shift {
+ left: 80px;
+ }
+ .grow {
+ width: 70px;
+ }
+ .anchored {
+ position: absolute;
+ width: anchor-size(width);
+ height: 40px;
+ position-anchor: --a;
+ top: anchor(bottom);
+ left: anchor(right);
+ transition-duration: 1000s;
+ transition-timing-function: steps(2, start);
+ background-color: skyblue;
+ }
+</style>
+
+<div id=anchor_test class=cb>
+ <div class=anchor></div>
+ <div class=anchored style="transition-property:left"></div>
+</div>
+<script>
+ test(() => {
+ let anchor = anchor_test.querySelector('.anchor');
+ let anchored = anchor_test.querySelector('.anchored');
+ assert_equals(anchored.offsetLeft, 110);
+ anchor.classList.add('shift');
+ assert_equals(anchored.offsetLeft, 120);
+ }, 'Transition when the result of anchor() changes');
+</script>
+
+<div id=anchor_size_test class=cb>
+ <div class=anchor></div>
+ <div class=anchored style="transition-property:width"></div>
+</div>
+<script>
+ test(() => {
+ let anchor = anchor_size_test.querySelector('.anchor');
+ let anchored = anchor_size_test.querySelector('.anchored');
+ assert_equals(anchored.offsetWidth, 50);
+ anchor.classList.add('grow');
+ assert_equals(anchored.offsetWidth, 60);
+ }, 'Transition when the result of anchor-size() changes');
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-transition-name.html b/tests/wpt/tests/css/css-anchor-position/anchor-transition-name.html
new file mode 100644
index 00000000000..ea7b403e612
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-transition-name.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: Transition when the dereferenced anchor name changes</title>
+<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #cb {
+ display: inline-block;
+ position: relative;
+ width: 250px;
+ height: 250px;
+ border: 1px solid black;
+ }
+ #anchor1, #anchor2 {
+ width: 100px;
+ }
+ #anchor1 {
+ background: wheat;
+ height: 50px;
+ }
+ #anchor2 {
+ background: tomato;
+ height: 90px;
+ }
+ .anchor-name {
+ anchor-name: --a;
+ }
+ #anchored {
+ position: absolute;
+ width: anchor-size(width);
+ height: anchor-size(height);
+ position-anchor: --a;
+ top: anchor(top);
+ left: anchor(right);
+ transition-duration: 1000s;
+ transition-timing-function: steps(2, start);
+ transition-property: top, height;
+ background-color: skyblue;
+ }
+</style>
+<div id=cb>
+ <div id=anchor1 class=anchor-name>Anchor1</div>
+ <div id=anchor2>Anchor2</div>
+ <div id=anchored></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(anchored.offsetTop, 0);
+ assert_equals(anchored.offsetHeight, 50);
+ anchor1.classList.toggle('anchor-name');
+ anchor2.classList.toggle('anchor-name');
+ assert_equals(anchored.offsetTop, 25);
+ assert_equals(anchored.offsetHeight, 70);
+ }, 'Transition when the dereferenced anchor name changes');
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-typed-om.html b/tests/wpt/tests/css/css-anchor-position/anchor-typed-om.html
new file mode 100644
index 00000000000..d4fec49dd3d
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-typed-om.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning Test: anchor()/anchor-size() functions in CSS Typed OM</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #cb {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ border: 1px solid black;
+ }
+ #anchor {
+ position: absolute;
+ left: 10px;
+ top: 10px;
+ width: 20px;
+ height: 20px;
+ background: coral;
+ anchor-name: --a;
+ }
+ #target {
+ position: absolute;
+ background: skyblue;
+ position-anchor: --a;
+ left: anchor(right);
+ top: calc(anchor(bottom) + 5px);
+ width: anchor-size(--unknown width, 25px);
+ height: calc(anchor-size(height) * 2);
+ }
+</style>
+
+<div id=cb>
+ <div id=anchor></div>
+ <div id=target></div>
+</div>
+<script>
+ function assert_unit_value(actual, expected) {
+ assert_true(actual instanceof CSSUnitValue);
+ assert_true(expected instanceof CSSUnitValue);
+ assert_equals(actual.value, expected.value);
+ assert_equals(actual.unit, expected.unit);
+ }
+
+ test(() => {
+ assert_unit_value(target.computedStyleMap().get('left'), CSS.px(30));
+ assert_unit_value(target.computedStyleMap().get('top'), CSS.px(35));
+ }, 'anchor() computes to pixels');
+
+ test(() => {
+ assert_unit_value(target.computedStyleMap().get('width'), CSS.px(25));
+ assert_unit_value(target.computedStyleMap().get('height'), CSS.px(40));
+ }, 'anchor-size() computes to pixels');
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/chrome-40286059-crash.html b/tests/wpt/tests/css/css-anchor-position/chrome-40286059-crash.html
new file mode 100644
index 00000000000..dbbeb5ac4df
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/chrome-40286059-crash.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/40286059">
+<p>Pass if no crash</p>
+<div id="pop" style="position-try-options: --foo" popover></div>
+<script>
+ getComputedStyle(pop).left;
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/inset-area-basic.html b/tests/wpt/tests/css/css-anchor-position/inset-area-basic.html
index 01c543f1fef..b89d0e24289 100644
--- a/tests/wpt/tests/css/css-anchor-position/inset-area-basic.html
+++ b/tests/wpt/tests/css/css-anchor-position/inset-area-basic.html
@@ -49,10 +49,10 @@
function test_inset_area(inset_area, expected_offsets) {
anchored.style.insetArea = inset_area;
test(() => {
- assert_equals(anchored.offsetLeft, expected_offsets.left);
- assert_equals(anchored.offsetTop, expected_offsets.top);
- assert_equals(anchored.offsetWidth, expected_offsets.width);
- assert_equals(anchored.offsetHeight, expected_offsets.height);
+ assert_equals(anchored.offsetLeft, expected_offsets.left, "Check expected offsetLeft");
+ assert_equals(anchored.offsetTop, expected_offsets.top, "Check expected offsetTop");
+ assert_equals(anchored.offsetWidth, expected_offsets.width, "Check expected offsetWidth");
+ assert_equals(anchored.offsetHeight, expected_offsets.height, "Check expected offsetHeight");
}, "Offsets for: " + inset_area);
}
diff --git a/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-computed.html b/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-computed.html
index f0944b7faaa..91b27d4f317 100644
--- a/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-computed.html
+++ b/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-computed.html
@@ -19,6 +19,15 @@
test_computed_value("position-try-options", "flip-start flip-inline flip-block");
test_computed_value("position-try-options", "flip-start --flop", "--flop flip-start");
test_computed_value("position-try-options", "--flop flip-start");
+ test_computed_value("position-try-options", "inset-area(left top)");
+ test_computed_value("position-try-options", "inset-area(top left)", "inset-area(left top)");
+ test_computed_value("position-try-options", "inset-area(start start)", "inset-area(start)");
+ test_computed_value("position-try-options", "inset-area(left), inset-area(right)");
+ test_computed_value("position-try-options", "--foo, inset-area(left)");
+ test_computed_value("position-try-options", "--foo, inset-area(left), --bar");
+ test_computed_value("position-try-options", "--foo, flip-start, inset-area(left)");
+ test_computed_value("position-try-options", "--foo flip-start, inset-area(left)");
+ test_computed_value("position-try-options", "inset-area(left), --bar flip-start");
assert_not_inherited("position-try-options", "none", "flip-inline");
</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-parsing.html b/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-parsing.html
index 303cc4d0dd2..72bc74111bc 100644
--- a/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-parsing.html
+++ b/tests/wpt/tests/css/css-anchor-position/parsing/position-try-options-parsing.html
@@ -25,6 +25,15 @@
test_valid_value("position-try-options", "--bar flip-inline flip-block");
test_valid_value("position-try-options", "flip-inline --foo", "--foo flip-inline");
test_valid_value("position-try-options", "flip-inline flip-start --foo", "--foo flip-inline flip-start");
+ test_valid_value("position-try-options", "inset-area(left top)");
+ test_valid_value("position-try-options", "inset-area(top left)", "inset-area(left top)");
+ test_valid_value("position-try-options", "inset-area(start start)", "inset-area(start)");
+ test_valid_value("position-try-options", "inset-area(left), inset-area(right)");
+ test_valid_value("position-try-options", "--foo, inset-area(left)");
+ test_valid_value("position-try-options", "--foo, inset-area(left), --bar");
+ test_valid_value("position-try-options", "--foo, flip-start, inset-area(left)");
+ test_valid_value("position-try-options", "--foo flip-start, inset-area(left)");
+ test_valid_value("position-try-options", "inset-area(left), --bar flip-start");
test_invalid_value("position-try-options", "none, flip-start");
test_invalid_value("position-try-options", "flip-block flip-block");
@@ -37,4 +46,10 @@
test_invalid_value("position-try-options", "foo");
test_invalid_value("position-try-options", "flip-start 123");
test_invalid_value("position-try-options", "--foo 123");
+ test_invalid_value("position-try-options", "--foo inset-area(left)");
+ test_invalid_value("position-try-options", "flip-start inset-area(left)");
+ test_invalid_value("position-try-options", "inset-area(left) --foo ");
+ test_invalid_value("position-try-options", "inset-area(left) flip-start");
+ test_invalid_value("position-try-options", "--foo, none");
+ test_invalid_value("position-try-options", "--foo, inset-area(none)");
</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-001.html b/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-001.html
deleted file mode 100644
index f379b691e0b..00000000000
--- a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-001.html
+++ /dev/null
@@ -1,84 +0,0 @@
-<!DOCTYPE html>
-<title>Tests basic functionalities of 'position-fallback-bounds'</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
-<link rel="author" href="mailto:xiaochengh@chromium.org">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/check-layout-th.js"></script>
-<script src="support/test-common.js"></script>
-
-<style>
-body {
- margin: 0;
-}
-
-.container {
- position: relative;
- width: 400px;
- height: 400px;
- top: 100px;
- anchor-name: --bounds;
- outline: 1px dashed black;
-}
-
-.anchor {
- position: absolute;
- width: 100px;
- height: 100px;
- left: 150px;
- background-color: orange;
-}
-
-.target {
- position: fixed;
- width: 100px;
- height: 100px;
- background-color: lime;
- left: anchor(left);
- position-fallback-bounds: --bounds;
-}
-
-#anchor1 {
- top: 0;
- anchor-name: --a1;
-}
-#anchor2 {
- bottom: 0;
- anchor-name: --a2;
-}
-
-#target1 {
- position-anchor: --a1;
- bottom: anchor(top);
- position-try-options: --bottom;
-}
-#target2 {
- position-anchor: --a2;
- top: anchor(bottom);
- position-try-options: --top;
-}
-
-@position-try --bottom {
- bottom: auto;
- top: anchor(bottom);
-}
-@position-try --top {
- top: auto;
- bottom: anchor(top);
-}
-</style>
-
-<body onload="checkLayoutForAnchorPos('.target')">
- <div class="container" id="bounds">
- <div class="anchor" id="anchor1"></div>
- <div class="anchor" id="anchor2"></div>
- </div>
-
- <!-- Enough space above the anchor in the viewport but not in the additional
- bounds rect, which triggers fallback -->
- <div class="target" id="target1" data-offset-y=200></div>
-
- <!-- Enough space below the anchor in the viewport but not in the additional
- bounds rect, which triggers fallback -->
- <div class="target" id="target2" data-offset-y=300></div>
-</body>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-002.html b/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-002.html
deleted file mode 100644
index 960a530b97b..00000000000
--- a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-002.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!DOCTYPE html>
-<title>Tests 'position-fallback-bounds' with mixed writing modes</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
-<link rel="author" href="mailto:xiaochengh@chromium.org">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/check-layout-th.js"></script>
-<script src="support/test-common.js"></script>
-
-<style>
-body {
- margin: 0;
-}
-
-.container {
- position: relative;
- width: 400px;
- height: 400px;
- top: 100px;
- anchor-name: --bounds;
- outline: 1px dashed black;
-}
-
-.anchor {
- position: absolute;
- width: 100px;
- height: 100px;
- left: 150px;
- background-color: orange;
-}
-
-.target {
- position: fixed;
- width: 100px;
- height: 100px;
- background-color: lime;
- left: anchor(left);
- position-fallback-bounds: --bounds;
-}
-
-#anchor1 {
- top: 0;
- anchor-name: --a1;
-}
-#anchor2 {
- bottom: 0;
- anchor-name: --a2;
-}
-
-#target1 {
- position-anchor: --a1;
- bottom: anchor(top);
- position-try: --bottom;
- writing-mode: vertical-rl;
-}
-#target2 {
- position-anchor: --a2;
- top: anchor(bottom);
- position-try: --top;
- writing-mode: vertical-lr;
- direction: rtl;
-}
-
-@position-try --bottom {
- bottom: auto;
- top: anchor(bottom);
-}
-@position-try --top {
- top: auto;
- bottom: anchor(top);
-}
-</style>
-
-<body onload="checkLayoutForAnchorPos('.target')">
- <div class="container" id="bounds">
- <div class="anchor" id="anchor1"></div>
- <div class="anchor" id="anchor2"></div>
- </div>
-
- <!-- Enough space above the anchor in the viewport but not in the additional
- bounds rect, which triggers fallback -->
- <div class="target" id="target1" data-offset-y=200></div>
-
- <!-- Enough space below the anchor in the viewport but not in the additional
- bounds rect, which triggers fallback -->
- <div class="target" id="target2" data-offset-y=300></div>
-</body>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-003.html b/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-003.html
deleted file mode 100644
index 1bee43b3616..00000000000
--- a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-003.html
+++ /dev/null
@@ -1,95 +0,0 @@
-<!DOCTYPE html>
-<title>Tests basic interaction between 'position-fallback-bounds' and scrolling</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
-<link rel="author" href="mailto:xiaochengh@chromium.org">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/test-common.js"></script>
-
-<style>
-body {
- position: relative;
- width: 200vw;
- height: 200vh;
-}
-
-#bounds {
- position: fixed;
- inset: 50px;
- background: gray;
- opacity: 0.1;
- z-index: -1;
- anchor-name: --bounds;
-}
-
-#anchor {
- position: absolute;
- width: 100px;
- height: 100px;
- left: 200px;
- top: 200px;
- background-color: orange;
- anchor-name: --a;
-}
-
-#target {
- position: fixed;
- width: 100px;
- height: 100px;
- background-color: lime;
- position-anchor: --a;
- position-fallback-bounds: --bounds;
- position-try-options: --corner1, --corner2, --corner3;
- bottom: anchor(top);
- right: anchor(left);
-}
-
-@position-try --corner1 {
- inset: auto;
- top: anchor(bottom);
- right: anchor(left);
-}
-@position-try --corner2 {
- inset: auto;
- bottom: anchor(top);
- left: anchor(right);
-}
-@position-try --corner3 {
- inset: auto;
- top: anchor(bottom);
- left: anchor(right);
-}
-</style>
-
-<div id="bounds"></div>
-<div id="anchor"></div>
-<div id="target"></div>
-
-<script>
-promise_test(async () => {
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'top');
- assert_fallback_position(target, anchor, 'left');
-}, "Target is at anchor's top-left corner at initial scroll position");
-
-promise_test(async () => {
- document.documentElement.scrollTop = 100;
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'bottom');
- assert_fallback_position(target, anchor, 'left');
-}, "Target falls back to anchor's bottom-left corner after anchor is scrolled upwards");
-
-promise_test(async () => {
- document.documentElement.scrollLeft = 100;
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'bottom');
- assert_fallback_position(target, anchor, 'right');
-}, "Target falls back to anchor's bottom-right corner after anchor is further scrolled leftwards");
-
-promise_test(async () => {
- document.documentElement.scrollTop = 0;
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'top');
- assert_fallback_position(target, anchor, 'right');
-}, "Target falls back to anchor's top-left corner after anchor is scrolled back downwards");
-</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-004.html b/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-004.html
deleted file mode 100644
index 15bdf113276..00000000000
--- a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-004.html
+++ /dev/null
@@ -1,99 +0,0 @@
-<!DOCTYPE html>
-<title>Tests complex interaction between 'position-fallback-bounds' and scrolling</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
-<link rel="author" href="mailto:xiaochengh@chromium.org">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/test-common.js"></script>
-
-<style>
-body {
- margin: 0;
-}
-
-#anchor-scroller {
- position: absolute;
- height: 175px;
- width: 200px;
- left: 200px;
- top: 100px;
- overflow-y: scroll;
- z-index: 100;
-}
-
-#anchor {
- position: absolute;
- width: 100px;
- height: 100px;
- left: 0;
- top: 150px;
- background-color: orange;
- anchor-name: --a;
-}
-
-#bounds-scroller {
- position: absolute;
- width: 400px;
- height: 400px;
- left: 100px;
- top: 0;
- overflow-y: scroll;
-}
-
-#bounds {
- position: absolute;
- width: 400px;
- height: 400px;
- top: 100px;
- background: gray;
- anchor-name: --bounds;
-}
-
-#target {
- position: fixed;
- width: 100px;
- height: 100px;
- background-color: lime;
- left: anchor(left);
- position-anchor: --a;
- position-fallback-bounds: --bounds;
- position-try-options: --bottom;
- bottom: anchor(top);
-}
-
-@position-try --bottom {
- top: anchor(bottom);
- bottom: auto;
-}
-</style>
-
-<div id="anchor-scroller">
- <div id="anchor"></div>
-</div>
-
-<div id="bounds-scroller">
- <div id="bounds"></div>
-</div>
-
-<div id="target"></div>
-
-<script>
-promise_test(async () => {
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'top');
-}, 'Target is above anchor at initial scroll position');
-
-promise_test(async () => {
- const anchorScroller = document.getElementById('anchor-scroller');
- anchorScroller.scrollTop = 100;
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'bottom');
-}, 'Target falls back to below anchor after anchor is scrolled upwards');
-
-promise_test(async () => {
- const boundsScroller = document.getElementById('bounds-scroller');
- boundsScroller.scrollTop = 100;
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'top');
-}, 'Target returns to above anchor after bounds are scrolled upwards');
-</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-005.html b/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-005.html
deleted file mode 100644
index d1e9fd7f51e..00000000000
--- a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-005.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<!DOCTYPE html>
-<title>Tests relayout after 'position-fallback-bounds' change</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
-<link rel="author" href="mailto:xiaochengh@chromium.org">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/test-common.js"></script>
-
-<style>
-.bounds {
- position: absolute;
- left: 100px;
- width: 200px;
- height: 200px;
- background: gray;
- opacity: 0.1;
-}
-
-#bounds1 {
- top: 0;
- anchor-name: --bounds1;
-}
-
-#bounds2 {
- top: 300px;
- anchor-name: --bounds2;
-}
-
-#anchor {
- position: absolute;
- width: 100px;
- height: 100px;
- background: orange;
- left: 150px;
- top: 200px;
- anchor-name: --a;
-}
-
-#target {
- position: fixed;
- width: 100px;
- height: 100px;
- background: lime;
- left: anchor(left);
- bottom: anchor(top);
- position-anchor: --a;
- position-try-options: --bottom;
- position-fallback-bounds: --bounds1;
-}
-
-@position-try --bottom {
- bottom: auto;
- top: anchor(bottom);
-}
-</style>
-
-<div class="bounds" id="bounds1"></div>
-<div class="bounds" id="bounds2"></div>
-<div id="anchor"></div>
-<div id="target"></div>
-
-<script>
-test(() => {
- assert_fallback_position(target, anchor, 'top');
-}, 'Initial layout');
-
-test(() => {
- target.style = 'position-fallback-bounds: --bounds2';
- assert_fallback_position(target, anchor, 'bottom');
-}, 'Layout is updated after position-fallback-bounds property changes');
-
-test(() => {
- bounds2.style = 'top: 0; height: 500px';
- assert_fallback_position(target, anchor, 'top');
-}, 'Layout is updated after additional fallback-bounds rect changes');
-</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-006.html b/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-006.html
deleted file mode 100644
index 023c650cfc9..00000000000
--- a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-006.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<!DOCTYPE html>
-<title>Tests 'position-fallback-bounds' should work without default anchor</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds">
-<link rel="author" href="mailto:xiaochengh@chromium.org">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="support/test-common.js"></script>
-
-<style>
-body {
- margin: 0;
-}
-
-#bounds {
- position: absolute;
- left: 0;
- right: 0;
- top: 100px;
- height: calc(100vh + 200px);
- background: lightgray;
- anchor-name: --bounds;
-}
-
-#anchor {
- position: fixed;
- width: 100px;
- height: 100px;
- left: 0;
- top: 100px;
- background: orange;
- anchor-name: --a;
-}
-
-#target {
- position: fixed;
- width: 100px;
- height: 100px;
- background: lime;
- position-try-options: --bottom;
- position-fallback-bounds: --bounds;
- bottom: anchor(--a top);
-}
-
-@position-try --bottom {
- top: anchor(--a bottom);
- bottom: auto;
-}
-</style>
-
-<div id=bounds></div>
-<div id=anchor></div>
-<div id=target></div>
-
-<script>
-promise_test(async () => {
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'bottom');
-}, "Target is below anchor at initial scroll position");
-
-promise_test(async () => {
- document.documentElement.scrollTop = 100;
- await waitUntilNextAnimationFrame();
- assert_fallback_position(target, anchor, 'top');
-}, "Target moves to above anchor after the additional fallback-bounds rect is scrolled upwards");
-</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-basics.html b/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-basics.html
deleted file mode 100644
index 3b8b67bd978..00000000000
--- a/tests/wpt/tests/css/css-anchor-position/position-fallback-bounds-basics.html
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE html>
-<title>Tests basics of the 'position-fallback' property</title>
-<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#propdef-position-fallback-bounds">
-<link rel="author" href="mailto:xiaochengh@chromium.org">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/css/support/parsing-testcommon.js"></script>
-<script src="/css/support/computed-testcommon.js"></script>
-<script src="/css/support/inheritance-testcommon.js"></script>
-<script src="/css/support/interpolation-testcommon.js"></script>
-
-<div id="container">
- <div id="target"></div>
-</div>
-
-<script>
-// position-fallback-bounds: normal | <dashed-ident>
-test_valid_value('position-fallback-bounds', 'normal');
-test_valid_value('position-fallback-bounds', '--foo');
-test_invalid_value('position-fallback-bounds', 'foo-bar');
-test_invalid_value('position-fallback-bounds', '--foo --bar')
-test_invalid_value('position-fallback-bounds', '--foo, --bar')
-test_invalid_value('position-fallback-bounds', '100px');
-test_invalid_value('position-fallback-bounds', '100%');
-
-// Computed value: as specified
-test_computed_value('position-fallback-bounds', 'normal');
-test_computed_value('position-fallback-bounds', '--foo');
-
-// Initial: normal
-// Inherited: no
-assert_not_inherited('position-fallback-bounds', 'normal', '--foo');
-
-// Animation type: discrete
-test_no_interpolation({
- property: 'position-fallback-bounds',
- from: '--foo',
- to: 'normal',
-});
-</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-try-order-basic.html b/tests/wpt/tests/css/css-anchor-position/position-try-order-basic.html
new file mode 100644
index 00000000000..d1c74e7b9fd
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-try-order-basic.html
@@ -0,0 +1,197 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: Basic position-try-order behavior</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#position-try-order-property">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #cb {
+ position: absolute;
+ width: 400px;
+ height: 400px;
+ border: 1px solid black;
+ }
+ #anchor {
+ position: absolute;
+ left: 150px;
+ top: 200px;
+ width: 150px;
+ height: 150px;
+ background-color: coral;
+ anchor-name: --a;
+ }
+ #target, #ref {
+ position: absolute;
+ left: 450px; /* force fallback */
+ width: 40px;
+ height: 40px;
+ background-color: skyblue;
+ position-anchor: --a;
+ }
+ #ref {
+ background-color: seagreen;
+ }
+
+/*
+
+The IMCB for --right is the whole area to the right of the anchor, and similarly
+for --left, etc.
+
+ ┌──────────────┐
+ │ xxxx│
+ │ xxxx│
+ │ ┌────┐xxxx│
+ │ │ │xxxx│
+ │ └────┘xxxx│
+ │ xxxx│
+ │ xxxx│
+ └──────────────┘
+
+**/
+
+ @position-try --right {
+ inset: unset;
+ left: anchor(right);
+ }
+ @position-try --left {
+ inset: unset;
+ right: anchor(left);
+ }
+ @position-try --top {
+ inset: unset;
+ bottom: anchor(top);
+ }
+ @position-try --bottom {
+ inset: unset;
+ top: anchor(bottom);
+ }
+
+/*
+
+The IMCB for --right-sweep is the area that would be "swept" by the anchor if it
+moved right, and similarly for --left-sweep, etc.
+
+ ┌──────────────┐
+ │ │
+ │ │
+ │ ┌────┐xxxx│
+ │ │ │xxxx│
+ │ └────┘xxxx│
+ │ │
+ │ │
+ └──────────────┘
+
+*/
+
+ @position-try --right-sweep {
+ inset: unset;
+ top: anchor(top);
+ bottom: anchor(bottom);
+ left: anchor(right);
+ align-self: center;
+ }
+
+ @position-try --left-sweep {
+ inset: unset;
+ top: anchor(top);
+ bottom: anchor(bottom);
+ right: anchor(left);
+ align-self: center;
+ }
+
+ @position-try --bottom-sweep {
+ left: anchor(left);
+ right: anchor(right);
+ top: anchor(bottom);
+ justify-self: center;
+ }
+
+ @position-try --top-sweep {
+ left: anchor(left);
+ right: anchor(right);
+ bottom: anchor(top);
+ justify-self: center;
+ }
+
+</style>
+<style id=style>
+</style>
+<div id=cb>
+ <div id=anchor></div>
+ <div id=target></div>
+ <div id=ref></div>
+</div>
+<script>
+
+// Test that an element with the specified `position_try` gets the same
+// position as a reference element with `position_try_expected`.
+function test_order(position_try, position_try_expected) {
+ test((t) => {
+ style.textContent = `
+ #target {
+ position-try: ${position_try};
+ }
+ #ref {
+ position-try: ${position_try_expected};
+ }
+ `;
+ assert_true(CSS.supports('position-try', 'normal --x'));
+ assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft');
+ assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop');
+ }, `${position_try} | ${position_try_expected}`);
+}
+
+// Note: --right, --left, --top, and --bottom all fit, but have different
+// inset-modifed containing blocks.
+test_order('--right', '--right');
+test_order('--left', '--left');
+test_order('--top', '--top');
+test_order('--bottom', '--bottom');
+
+// position-try-order:normal just picks the first option.
+test_order('--right, --left, --bottom, --top', '--right');
+test_order('normal --right, --left, --bottom, --top', '--right');
+test_order('normal --top, --left, --bottom, --right', '--top');
+
+// --right and --left have the same IMCB block-size.
+test_order('most-block-size --right, --left', '--right');
+test_order('most-height --right, --left', '--right');
+// --left has more inline-size than --right.
+test_order('most-inline-size --right, --left', '--left');
+test_order('most-width --right, --left', '--left');
+
+// --bottom and --top have the same IMCB inline-size.
+test_order('most-inline-size --bottom, --top', '--bottom');
+test_order('most-width --bottom, --top', '--bottom');
+// --top has more block-size than --bottom.
+test_order('most-block-size --bottom, --top', '--top');
+test_order('most-height --bottom, --top', '--top');
+
+// --bottom/--top has more IMBC inline-size than --right/--left.
+test_order('most-inline-size --right, --left, --bottom, --top', '--bottom');
+test_order('most-inline-size --right, --left, --top, --bottom', '--top');
+
+// --right/--left has more IMBC block-size than --bottom/--top.
+test_order('most-block-size --bottom, --top, --right, --left', '--right');
+test_order('most-block-size --bottom, --top, --left, --right', '--left');
+
+// --left-sweep and --bottom-sweep has the same IMBC inline-size ...
+test_order('most-inline-size --left-sweep, --bottom-sweep', '--left-sweep');
+test_order('most-inline-size --bottom-sweep, --left-sweep', '--bottom-sweep');
+// ... but not the same block-size.
+test_order('most-block-size --left-sweep, --bottom-sweep', '--left-sweep');
+test_order('most-block-size --bottom-sweep, --left-sweep', '--left-sweep');
+
+test_order('most-inline-size --right-sweep, --left-sweep, --bottom-sweep, --top-sweep', '--left-sweep');
+test_order('most-block-size --right-sweep, --left-sweep, --bottom-sweep, --top-sweep', '--top-sweep');
+
+test_order(`most-inline-size
+ --right-sweep, --left-sweep, --bottom-sweep, --top-sweep,
+ --right, --left, --bottom, --top
+ `, '--left-sweep');
+
+test_order(`most-block-size
+ --right-sweep, --left-sweep, --bottom-sweep, --top-sweep,
+ --right, --left, --bottom, --top
+ `, '--right');
+
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-try-transition-basic.html b/tests/wpt/tests/css/css-anchor-position/position-try-transition-basic.html
new file mode 100644
index 00000000000..3bb982f073a
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-try-transition-basic.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: Transition when @position-try is applied</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #cb {
+ display: inline-block;
+ position: relative;
+ width: 400px;
+ height: 250px;
+ border: 1px solid black;
+ }
+ #cb.shrink {
+ width: 300px;
+ }
+ #target {
+ position: absolute;
+ left: 300px;
+ width: 50px;
+ height: 50px;
+ background: skyblue;
+ position-try-options: --200;
+ }
+ #target.anim {
+ transition: left 1000s steps(2, start);
+ }
+ @position-try --200 {
+ left: 200px;
+ }
+</style>
+<div id=cb>
+ <div id=target></div>
+</div>
+<script>
+ function cleanup() {
+ target.className = '';
+ cb.className = '';
+ }
+
+ test((t) => {
+ t.add_cleanup(cleanup);
+ assert_equals(target.offsetLeft, 300);
+ cb.classList.add('shrink');
+ target.classList.add('anim');
+ // Now we take the --200 option:
+ assert_equals(target.offsetLeft, 250);
+ }, 'Transition when @position-try is applied');
+
+ test((t) => {
+ t.add_cleanup(cleanup);
+ cb.classList.add('shrink');
+ assert_equals(target.offsetLeft, 200);
+ cb.classList.remove('shrink');
+ target.classList.add('anim');
+ assert_equals(target.offsetLeft, 250);
+ }, 'Transition when @position-try is unapplied');
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-try-transition-flip.html b/tests/wpt/tests/css/css-anchor-position/position-try-transition-flip.html
new file mode 100644
index 00000000000..6adacadfd42
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-try-transition-flip.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: Transition to a flipped state</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply">
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #cb {
+ display: inline-block;
+ position: relative;
+ width: 400px;
+ height: 250px;
+ border: 1px solid black;
+ }
+ #cb.shrink {
+ width: 325px;
+ }
+ #anchor {
+ position: absolute;
+ width: 50px;
+ height: 50px;
+ background: coral;
+ left: 250px;
+ top: 50px;
+ anchor-name: --a;
+ }
+ #target {
+ position-anchor: --a;
+ position: absolute;
+ left: anchor(right);
+ top: anchor(top);
+ width: 50px;
+ height: 50px;
+ background: skyblue;
+ position-try-options: flip-start;
+ }
+ #target.anim {
+ transition: left 1000s steps(2, start);
+ }
+</style>
+<div id=cb>
+ <div id=anchor></div>
+ <div id=target></div>
+</div>
+<script>
+ function cleanup() {
+ target.className = '';
+ cb.className = '';
+ }
+
+ test((t) => {
+ t.add_cleanup(cleanup);
+ assert_equals(target.offsetLeft, 300);
+ cb.classList.add('shrink');
+ target.classList.add('anim');
+ assert_equals(target.offsetLeft, 275);
+ }, 'Transition to a flipped state');
+
+ test((t) => {
+ t.add_cleanup(cleanup);
+ cb.classList.add('shrink');
+ assert_equals(target.offsetLeft, 250);
+ cb.classList.remove('shrink');
+ target.classList.add('anim');
+ assert_equals(target.offsetLeft, 275);
+ }, 'Transition to an unflipped state');
+
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-try-typed-om.html b/tests/wpt/tests/css/css-anchor-position/position-try-typed-om.html
new file mode 100644
index 00000000000..76ec9411b8d
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-try-typed-om.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning Test: Effects from @position-try in CSS Typed OM</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @position-try --pt {
+ left: anchor(right);
+ top: 50px;
+ width: anchor-size(width);
+ height: 40px;
+ }
+ #cb {
+ position: relative;
+ width: 100px;
+ height: 100px;
+ border: 1px solid black;
+ }
+ #anchor {
+ position: absolute;
+ left: 10px;
+ top: 10px;
+ width: 20px;
+ height: 20px;
+ background: coral;
+ anchor-name: --a;
+ }
+ #target {
+ position: absolute;
+ background: skyblue;
+ position-anchor: --a;
+ left: 9999px; /* force overflow */
+ position-try-options: --pt;
+ }
+</style>
+
+<div id=cb>
+ <div id=anchor></div>
+ <div id=target></div>
+</div>
+<script>
+ function assert_unit_value(actual, expected) {
+ assert_true(actual instanceof CSSUnitValue);
+ assert_true(expected instanceof CSSUnitValue);
+ assert_equals(actual.value, expected.value);
+ assert_equals(actual.unit, expected.unit);
+ }
+
+ test(() => {
+ assert_unit_value(target.computedStyleMap().get('left'), CSS.px(30));
+ assert_unit_value(target.computedStyleMap().get('top'), CSS.px(50));
+ assert_unit_value(target.computedStyleMap().get('width'), CSS.px(20));
+ assert_unit_value(target.computedStyleMap().get('height'), CSS.px(40));
+ }, 'Effects of position-try-options are visible in the computed values');
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in-ref.html
new file mode 100644
index 00000000000..10f74d4fb09
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ #anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ margin-bottom: 100px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ background: green;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor">anchor</div>
+</div>
+<div id="target">target</div>
+
+<script>
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 0;
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.tentative.html
new file mode 100644
index 00000000000..cea439c55f4
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.tentative.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<meta charset="utf-8">
+<meta name="assert" content="Scrolling an anchor in to view should cause a position-visibility: anchors-visible element to appear." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-after-scroll-in-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/rendering-utils.js"></script>
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #spacer {
+ height: 100px;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: block-end;
+ width: 100px;
+ height: 100px;
+ background: green;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor">anchor</div>
+ <div id="spacer"></div>
+ <div id="target">target</div>
+</div>
+
+<script>
+ // #target should be initially visible because it is anchored to #anchor,
+ // which is visible.
+ waitForAtLeastOneFrame().then(() => {
+ // Scroll #anchor out of view.
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+ // #target should now be invisible.
+
+ waitForAtLeastOneFrame().then(() => {
+ // Scroll #anchor back into view.
+ scroller.scrollTop = 0;
+
+ // #target should now be visible again.
+ takeScreenshot();
+ });
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out-ref.html
new file mode 100644
index 00000000000..bd4fe1f09f8
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ #spacer {
+ height: 200px;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="spacer"><div>
+</div>
+
+<script>
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.tentative.html
new file mode 100644
index 00000000000..b2e3643b077
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.tentative.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<meta charset="utf-8">
+<meta name="assert" content="Scrolling an anchor out of view should cause a position-visibility: anchors-visible element to disappear." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-after-scroll-out-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/rendering-utils.js"></script>
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #spacer {
+ height: 100px;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom;
+ width: 100px;
+ height: 100px;
+ background: red;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor">anchor</div>
+ <div id="spacer"></div>
+ <div id="target">target</div>
+</div>
+
+<script>
+ // #target should be initially visible because it is anchored to #anchor,
+ // which is visible.
+
+ waitForAtLeastOneFrame().then(() => {
+ // Scroll #anchor so that it is out of view.
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+
+ // #target should now be invisible.
+ takeScreenshot();
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed-ref.html
new file mode 100644
index 00000000000..7a9d69a9e3b
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<!-- This test passes if both the anchor and anchored elements are hidden. -->
+<div style="height: 200vh;"></div>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed.tentative.html
new file mode 100644
index 00000000000..3ea26c896e9
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-both-position-fixed.tentative.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="assert" content="position-visibility: anchors-visible should work with a fixed-position anchored element." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-both-position-fixed-ref.html">
+<style>
+ #anchor {
+ anchor-name: --a1;
+ position: fixed;
+ top: -100px;
+ left: 0;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom right;
+ width: 100px;
+ height: 100px;
+ background: red;
+ position: fixed;
+ top: 0;
+ left: 0;
+ }
+
+ #spacer {
+ height: 200vh;
+ }
+</style>
+
+<!-- Test passes if #target is not visible, due to #anchor being off-screen. -->
+<div id="anchor">anchor</div>
+<div id="target">target</div>
+<div id="spacer"></div>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor-ref.html
new file mode 100644
index 00000000000..cc35e4cd1f2
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+ #anchor {
+ width: 100px;
+ height: 200px;
+ background: orange;
+ }
+ #target {
+ width: 100px;
+ height: 100px;
+ background: green;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor"></div>
+</div>
+<div id="target">target</div>
+
+<script>
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor.tentative.html
new file mode 100644
index 00000000000..f8b1cc6d100
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor.tentative.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<meta charset="utf-8">
+<meta name="assert" content="Position-visibility should not be affected by the visibility of a previous anchor." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-change-anchor-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/rendering-utils.js"></script>
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ .anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ display: inline-block;
+ }
+
+ #anchor1 {
+ height: 200px;
+ anchor-name: --a1;
+ }
+
+ #anchor2 {
+ anchor-name: --a2;
+ }
+
+ #target {
+ position-anchor: --a2;
+ position-visibility: anchors-visible;
+ inset-area: bottom;
+ width: 100px;
+ height: 100px;
+ background: green;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor1" class="anchor">anchor1</div>
+ <div id="anchor2" class="anchor">anchor2</div>
+ <div id="target">target</div>
+</div>
+
+<script>
+ // #target should be initially visible because it is anchored to #anchor2,
+ // which is visible.
+ waitForAtLeastOneFrame().then(() => {
+ // Change #target to be anchored to #anchor1.
+ target.style.positionAnchor = '--a1';
+ // #target should be still be visible because #anchor1 is also visible.
+ waitForAtLeastOneFrame().then(() => {
+ // Scroll #anchor2 out of view, with #anchor1 still in view.
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+ // #target should still be visible because it is anchored to #anchor1,
+ // which is still visible.
+ takeScreenshot();
+ });
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility-ref.html
new file mode 100644
index 00000000000..c4af73bf65b
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+ #anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+ #target {
+ width: 100px;
+ height: 100px;
+ background: green;
+ }
+</style>
+<div id="anchor">anchor</div>
+<div id="target">target</div>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.tentative.html
new file mode 100644
index 00000000000..22a30658c81
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<meta charset="utf-8">
+<meta name="assert" content="Position-visibility: anchors-visible should show an element after an anchor changes from visibility: hidden to visibility: visible." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-change-css-visibility-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/rendering-utils.js"></script>
+<style>
+ #container {
+ visibility: hidden;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom;
+ width: 100px;
+ height: 100px;
+ background: green;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="container">
+ <div id="anchor">anchor</div>
+</div>
+<div id="target">target</div>
+
+<script>
+ // #target should be initially hidden because it is anchored to #anchor,
+ // which is hidden with "visibility: hidden".
+
+ waitForAtLeastOneFrame().then(() => {
+ // Change #container to "visibility: visible". #target should become
+ // visible again, because #anchor is no longer hidden.
+ container.style.visibility = 'visible';
+ takeScreenshot();
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility-ref.html
new file mode 100644
index 00000000000..3ac85888e48
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- This test passes if both the anchor and anchored elements are hidden. -->
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility.tentative.html
new file mode 100644
index 00000000000..31be7977989
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility.tentative.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="assert" content="Position-visibility: anchors-visible should hide an element with an anchor that has visibility: hidden." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-css-visibility-ref.html">
+<style>
+ #container {
+ visibility: hidden;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom right;
+ width: 100px;
+ height: 100px;
+ background: red;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="container">
+ <div id="anchor">anchor</div>
+</div>
+<div id="target">target</div>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container-ref.html
new file mode 100644
index 00000000000..3b6532e27b0
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+ #target {
+ width: 100px;
+ height: 100px;
+ background: green;
+ }
+</style>
+<div id="target">target</div>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.tentative.html
new file mode 100644
index 00000000000..7b84976fd3a
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.tentative.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="assert" content="position-visibility: anchors-visible should consider the visibility of the anchor relative the containing scroller, ignoring visibility in other scrollers." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-non-intervening-container-ref.html">
+<style>
+ #non-intervening-scroll-container {
+ overflow: hidden;
+ width: 200px;
+ height: 200px;
+ position: relative;
+ }
+
+ #position-container {
+ position: relative;
+ }
+
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 400px;
+ height: 100px;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #spacer {
+ height: 100px;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: right;
+ width: 100px;
+ height: 100px;
+ background: green;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="non-intervening-scroll-container">
+ <div id="position-container">
+ <div id="scroll-container">
+ <!-- The anchor is not visible to the screen, but it is visible in the -->
+ <!-- containing block of anchor1 and target1, so the target should not -->
+ <!-- be hidden due to position-visibility: anchors-visible. -->
+ <div id="anchor">anchor</div>
+ <div id="spacer"></div>
+ <div id="target">target</div>
+ </div>
+ </div>
+</div>
+
+<script>
+ const non_intervening_scroller = document.getElementById('non-intervening-scroll-container');
+ non_intervening_scroller.scrollLeft = 100;
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed-ref.html
new file mode 100644
index 00000000000..e24992fd5ea
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+
+<!-- This test passes if both the anchor and anchored elements are hidden. -->
+<div style="height: 200vh;"></div>
+
+<script>
+ window.scrollTo(0, 100);
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed.tentative.html
new file mode 100644
index 00000000000..25665ae4668
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed.tentative.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="assert" content="position-visibility: anchors-visible should work with a fixed-position anchored element." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-position-fixed-ref.html">
+<style>
+ #anchor {
+ anchor-name: --a1;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom right;
+ width: 100px;
+ height: 100px;
+ background: red;
+ position: fixed;
+ top: 0;
+ left: 0;
+ }
+
+ #spacer {
+ height: 200vh;
+ }
+</style>
+
+<!-- Test passes if #target is not visible, due to #anchor being off-screen. -->
+<div id="anchor">anchor</div>
+<div id="target">target</div>
+<div id="spacer"></div>
+
+<script>
+ window.scrollTo(0, 100);
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html
index 6f8d3cb971e..17798173804 100644
--- a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html
@@ -3,30 +3,17 @@
<style>
#scroll-container {
overflow: hidden scroll;
- width: 400px;
+ width: 300px;
height: 100px;
}
- #contents-container {
- height: 400px;
- }
-
- .anchor {
- width: 100px;
- height: 100px;
- background: orange;
- display: inline-block;
+ #spacer {
+ height: 200px;
}
</style>
<div id="scroll-container">
- <div id="contents-container">
- <div class="anchor">anchor1</div>
-
- <div class="anchor" style="height: 150px;">anchor2</div>
-
- <div class="anchor" style="height: 150px;">anchor3</div>
- </div>
+ <div id="spacer"></div>
</div>
<script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-stacked-child.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-stacked-child.tentative.html
new file mode 100644
index 00000000000..7c0d5dc6aad
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-stacked-child.tentative.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="assert" content="Position-visibility: anchors-visible should hide an element and stacked children with an out-of-view anchor." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-ref.html">
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #spacer {
+ height: 100px;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom right;
+ width: 100px;
+ height: 100px;
+ background: red;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+ #stacking-child {
+ /* stacking context */
+ z-index: 1;
+ width: 100px;
+ height: 100px;
+ background: maroon;
+ position: absolute;
+ top: 25px;
+ left: 25px;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor">anchor</div>
+ <div id="spacer"></div>
+ <div id="target">target
+ <div id="stacking-child"></div>
+ </div>
+</div>
+
+<script>
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+ // #target should not be visible because #anchor is scrolled out of view.
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-with-position.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-with-position.tentative.html
new file mode 100644
index 00000000000..82eed0beb9d
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible-with-position.tentative.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="assert" content="Position-visibility: anchors-visible should hide an element with an out-of-view anchor and a relpos scroller." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-anchors-visible-ref.html">
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ /* Same as position-visibility-anchors-visible.html, but with relpos here */
+ position: relative;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #spacer {
+ height: 100px;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom right;
+ width: 100px;
+ height: 100px;
+ background: red;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor">anchor</div>
+ <div id="spacer"></div>
+ <div id="target">target</div>
+</div>
+
+<script>
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+ // #target should not be visible because #anchor is scrolled out of view.
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html
index 6605bbc9783..85b8d897db8 100644
--- a/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html
@@ -1,55 +1,48 @@
<!DOCTYPE html>
<meta charset="utf-8">
+<meta name="assert" content="Position-visibility: anchors-visible should hide an element with an out-of-view anchor." />
<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
<link rel="match" href="position-visibility-anchors-visible-ref.html">
<style>
#scroll-container {
overflow: hidden scroll;
- width: 400px;
+ width: 300px;
height: 100px;
}
- #contents-container {
- height: 400px;
- }
-
- .anchor {
+ #anchor {
+ anchor-name: --a1;
width: 100px;
height: 100px;
background: orange;
- display: inline-block;
}
- .target {
- position: absolute;
+ #spacer {
+ height: 100px;
+ }
+
+ #target {
+ position-anchor: --a1;
position-visibility: anchors-visible;
- inset-area: block-end;
+ inset-area: bottom right;
width: 100px;
height: 100px;
background: red;
+ position: absolute;
top: 0;
left: 0;
}
</style>
<div id="scroll-container">
- <div id="contents-container">
- <!-- #target1 should not be visible because anchor is scrolled to not be visible. -->
- <div class="anchor" style="anchor-name: --a1;">anchor1</div>
- <div id="target1" class="target" style="position-anchor: --a1;">target1</div>
-
- <!-- #target2 should not be visible because referenced name in anchor() is not visible. -->
- <div class="anchor" style="anchor-name: --a2; height: 150px;">anchor2</div>
- <div id="target2" class="target" style="position-anchor: --a2; top: anchor(--a1 bottom);">target2</div>
-
- <!-- #target3 should not be visible because referenced name in anchor-size() is not visible. -->
- <div class="anchor" style="anchor-name: --a3; height: 150px;">anchor3</div>
- <div id="target3" class="target" style="position-anchor: --a3; min-width: anchor-width(--a1 width);">target3</div>
- </div>
+ <div id="anchor">anchor</div>
+ <div id="spacer"></div>
+ <div id="target">target</div>
</div>
<script>
const scroller = document.getElementById('scroll-container');
scroller.scrollTop = 100;
+ // #target should not be visible because #anchor is scrolled out of view.
</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll-ref.html
new file mode 100644
index 00000000000..b62a6d1078b
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 400px;
+ height: 150px;
+ }
+
+ .anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ display: inline-block;
+ }
+
+ .target {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id="scroll-container">
+ <div class="anchor">anchor1</div>
+ <div class="anchor" style="position: relative; top: 100px">anchor2</div>
+ <div id="target1" class="target" style="background: green">target1</div>
+ <div style="height: 200px"></div>
+</div>
+<script>
+document.getElementById('scroll-container').scrollTop = 50;
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll.tentative.html
new file mode 100644
index 00000000000..4751faeb0d0
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-scroll.tentative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>CSS Anchor Positioning Test: position-visibility: no-overflow</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-no-overflow-scroll-ref.html">
+<style>
+ #scroll-container {
+ position: relative;
+ overflow: hidden scroll;
+ width: 400px;
+ height: 150px;
+ }
+
+ .anchor {
+ width: 100px;
+ height: 100px;
+ background: orange;
+ display: inline-block;
+ }
+
+ .target {
+ position: absolute;
+ position-visibility: no-overflow;
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id="scroll-container">
+ <div class="anchor" style="anchor-name: --a1;">anchor1</div>
+ <div class="anchor" style="anchor-name: --a2; position: relative; top: 100px">anchor2</div>
+ <div id="target1" class="target" style="position-anchor: --a1; top: anchor(bottom); background: green">target1</div>
+ <div id="target2" class="target" style="position-anchor: --a2; left: anchor(left); bottom: anchor(top); background: red">target2</div>
+ <div style="height: 300px"></div>
+</div>
+<script>
+requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ document.getElementById('scroll-container').scrollTop = 50;
+ document.documentElement.classList.remove('reftest-wait');
+ });
+});
+</script>
+</html>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible-ref.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible-ref.html
new file mode 100644
index 00000000000..135763bf6bb
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ #target {
+ width: 100px;
+ height: 100px;
+ margin-top: 100px;
+ background: green;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="target">target</div>
+</div>
+
+<script>
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible.tentative.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible.tentative.html
new file mode 100644
index 00000000000..c6649e5f930
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-remove-anchors-visible.tentative.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<meta charset="utf-8">
+<meta name="assert" content="Removing position-visibility: anchors-visible from an invisible anchored element should cause it to become visible." />
+<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758">
+<link rel="match" href="position-visibility-remove-anchors-visible-ref.html">
+<script src="/common/reftest-wait.js"></script>
+<script src="/common/rendering-utils.js"></script>
+<style>
+ #scroll-container {
+ overflow: hidden scroll;
+ width: 300px;
+ height: 100px;
+ }
+
+ #anchor {
+ anchor-name: --a1;
+ width: 100px;
+ height: 100px;
+ background: orange;
+ }
+
+ #spacer {
+ height: 100px;
+ }
+
+ #target {
+ position-anchor: --a1;
+ position-visibility: anchors-visible;
+ inset-area: bottom;
+ width: 100px;
+ height: 100px;
+ background: green;
+ position: absolute;
+ top: 0;
+ left: 0;
+ }
+</style>
+
+<div id="scroll-container">
+ <div id="anchor">anchor</div>
+ <div id="spacer"></div>
+ <div id="target">target</div>
+</div>
+
+<script>
+ // #target should be initially visible because it is anchored to #anchor,
+ // which is visible.
+
+ // Scroll #anchor so that it is no longer visible.
+ const scroller = document.getElementById('scroll-container');
+ scroller.scrollTop = 100;
+
+ waitForAtLeastOneFrame().then(() => {
+ // Remove position-visibility: anchors-visible. #target should become
+ // visible again.
+ target.style.positionVisibility = 'initial';
+ takeScreenshot();
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/property-interpolations.html b/tests/wpt/tests/css/css-anchor-position/property-interpolations.html
index 1b8621a2130..954e5642dd7 100644
--- a/tests/wpt/tests/css/css-anchor-position/property-interpolations.html
+++ b/tests/wpt/tests/css/css-anchor-position/property-interpolations.html
@@ -76,15 +76,4 @@
from: 'most-width',
to: 'most-height',
});
-
- test_no_interpolation({
- property: 'position-fallback-bounds',
- from: 'normal',
- to: '--foo',
- });
- test_no_interpolation({
- property: 'position-fallback-bounds',
- from: '--foo',
- to: '--bar',
- });
</script>
diff --git a/tests/wpt/tests/css/css-anchor-position/try-tactic-wm.html b/tests/wpt/tests/css/css-anchor-position/try-tactic-wm.html
new file mode 100644
index 00000000000..bc1c82c35d1
--- /dev/null
+++ b/tests/wpt/tests/css/css-anchor-position/try-tactic-wm.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<title>CSS Anchor Positioning: try-tactic under different writing modes</title>
+<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @position-try --pf {
+ left:10px;
+ top:20px;
+ }
+ #cb {
+ position: absolute;
+ width: 400px;
+ height: 400px;
+ border: 1px solid black;
+ }
+ #target {
+ position: absolute;
+ left: 99999px; /* force fallback */
+ width: 30px;
+ height: 40px;
+ background-color: skyblue;
+ }
+</style>
+<div id=cb>
+ <div id=target></div>
+</div>
+<script>
+
+ function test_try_tactic_wm(try_tactic, writing_mode, direction, expected_offsets) {
+ target.style.positionTryOptions = `--pf ${try_tactic}`;
+ target.style.writingMode = writing_mode;
+ target.style.direction = direction;
+ test(() => {
+ assert_equals(target.offsetLeft, expected_offsets.left, 'offsetLeft');
+ assert_equals(target.offsetTop, expected_offsets.top, 'offsetTop');
+ assert_equals(target.offsetWidth, expected_offsets.width, 'offsetWidth');
+ assert_equals(target.offsetHeight, expected_offsets.height, 'offsetHeight');
+ }, `${try_tactic} ${writing_mode} ${direction}`);
+ }
+
+ test_try_tactic_wm('', 'horizontal-tb', 'ltr', {left:10, top:20, width:30, height:40});
+
+ // Effectively flips left:10px to right:10px:
+ test_try_tactic_wm('flip-inline', 'horizontal-tb', 'ltr', {left:360, top:20, width:30, height:40});
+
+ // Effectively flips top:20px to bottom:20px:
+ test_try_tactic_wm('flip-inline', 'vertical-lr', 'ltr', {left:10, top:340, width:30, height:40});
+
+ // Mirror across the [left,top]=>[bottom,right] diagonal:
+ test_try_tactic_wm('flip-start', 'horizontal-tb', 'ltr', {left:20, top:10, width:40, height:30});
+
+ // Mirror across the [right,top]=>[bottom,left] diagonal:
+ test_try_tactic_wm('flip-start', 'horizontal-tb', 'rtl', {left:340, top:360, width:40, height:30});
+
+</script>
diff --git a/tests/wpt/tests/css/css-animations/WEB_FEATURES.yml b/tests/wpt/tests/css/css-animations/WEB_FEATURES.yml
new file mode 100644
index 00000000000..2059bd49274
--- /dev/null
+++ b/tests/wpt/tests/css/css-animations/WEB_FEATURES.yml
@@ -0,0 +1,5 @@
+features:
+- name: animation-composition
+ files:
+ - animation-composition.html
+ - animation-composition-*
diff --git a/tests/wpt/tests/css/css-animations/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-animations/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..7e3ccd1b3b7
--- /dev/null
+++ b/tests/wpt/tests/css/css-animations/parsing/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: animation-composition
+ files:
+ - animation-composition-*
diff --git a/tests/wpt/tests/css/css-backgrounds/WEB_FEATURES.yml b/tests/wpt/tests/css/css-backgrounds/WEB_FEATURES.yml
new file mode 100644
index 00000000000..bee11ae42cf
--- /dev/null
+++ b/tests/wpt/tests/css/css-backgrounds/WEB_FEATURES.yml
@@ -0,0 +1,9 @@
+features:
+- name: background-clip
+ files:
+ - background-clip*
+ - css3-background-clip*
+- name: border-image
+ files:
+ - border-image*
+ - css3-border-image*
diff --git a/tests/wpt/tests/css/css-backgrounds/animations/WEB_FEATURES.yml b/tests/wpt/tests/css/css-backgrounds/animations/WEB_FEATURES.yml
new file mode 100644
index 00000000000..0d5911d01d5
--- /dev/null
+++ b/tests/wpt/tests/css/css-backgrounds/animations/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: border-image
+ files:
+ - border-image-*
diff --git a/tests/wpt/tests/css/css-backgrounds/background-clip/WEB_FEATURES.yml b/tests/wpt/tests/css/css-backgrounds/background-clip/WEB_FEATURES.yml
new file mode 100644
index 00000000000..5258340ceeb
--- /dev/null
+++ b/tests/wpt/tests/css/css-backgrounds/background-clip/WEB_FEATURES.yml
@@ -0,0 +1,10 @@
+features:
+- name: background-clip
+ files:
+ - clip-border-box*
+ - clip-content-box*
+ - clip-padding-box*
+ - clip-rounded-corner.html
+- name: background-clip-text
+ files:
+ - clip-text-*
diff --git a/tests/wpt/tests/css/css-backgrounds/background-clip/list.txt b/tests/wpt/tests/css/css-backgrounds/background-clip/list.txt
deleted file mode 100644
index 52c47ebba99..00000000000
--- a/tests/wpt/tests/css/css-backgrounds/background-clip/list.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-background-clip/border-box.html
-background-clip/border-box_with_position.html
-background-clip/border-box_with_radius.html
-background-clip/border-box_with_size.html
-background-clip/content-box.html
-background-clip/content-box_with_position.html
-background-clip/content-box_with_radius.html
-background-clip/content-box_with_size.html
-background-clip/padding-box.html
-background-clip/padding-box_with_position.html
-background-clip/padding-box_with_radius.html
-background-clip/padding-box_with_size.html
diff --git a/tests/wpt/tests/css/css-backgrounds/background-origin/list.txt b/tests/wpt/tests/css/css-backgrounds/background-origin/list.txt
deleted file mode 100644
index a6fdedde542..00000000000
--- a/tests/wpt/tests/css/css-backgrounds/background-origin/list.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-background-origin/border-box.html
-background-origin/border-box_with_position.html
-background-origin/border-box_with_radius.html
-background-origin/border-box_with_size.html
-background-origin/content-box.html
-background-origin/content-box_with_position.html
-background-origin/content-box_with_radius.html
-background-origin/content-box_with_size.html
-background-origin/padding-box.html
-background-origin/padding-box_with_position.html
-background-origin/padding-box_with_radius.html
-background-origin/padding-box_with_size.html
diff --git a/tests/wpt/tests/css/css-backgrounds/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-backgrounds/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..6a2438077a7
--- /dev/null
+++ b/tests/wpt/tests/css/css-backgrounds/parsing/WEB_FEATURES.yml
@@ -0,0 +1,7 @@
+features:
+- name: background-clip
+ files:
+ - background-clip-*
+- name: border-image
+ files:
+ - border-image-*
diff --git a/tests/wpt/tests/css/printing/transform-001-print-ref.html b/tests/wpt/tests/css/css-break/transform-022-print-ref.html
index 45c3abf1810..45c3abf1810 100644
--- a/tests/wpt/tests/css/printing/transform-001-print-ref.html
+++ b/tests/wpt/tests/css/css-break/transform-022-print-ref.html
diff --git a/tests/wpt/tests/css/printing/transform-001-print.html b/tests/wpt/tests/css/css-break/transform-022-print.html
index f19b889d07b..b8a097f225b 100644
--- a/tests/wpt/tests/css/printing/transform-001-print.html
+++ b/tests/wpt/tests/css/css-break/transform-022-print.html
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442522">
-<link rel="match" href="transform-001-print-ref.html">
+<link rel="match" href="transform-022-print-ref.html">
<style>
body { margin: 0; }
</style>
diff --git a/tests/wpt/tests/css/printing/transform-002-print-ref.html b/tests/wpt/tests/css/css-break/transform-023-print-ref.html
index 7204e1d6571..7204e1d6571 100644
--- a/tests/wpt/tests/css/printing/transform-002-print-ref.html
+++ b/tests/wpt/tests/css/css-break/transform-023-print-ref.html
diff --git a/tests/wpt/tests/css/printing/transform-002-print.html b/tests/wpt/tests/css/css-break/transform-023-print.html
index 3ab37bf5bd8..7650571abe1 100644
--- a/tests/wpt/tests/css/printing/transform-002-print.html
+++ b/tests/wpt/tests/css/css-break/transform-023-print.html
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1371426">
-<link rel="match" href="transform-002-print-ref.html">
+<link rel="match" href="transform-023-print-ref.html">
<p>There should be a green square on the second page, and no red.</p>
<div style="break-before:page; width:100px; height:100px; background:red;">
<div style="position:absolute; width:0; height:0; transform:translateX(49px);">
diff --git a/tests/wpt/tests/css/printing/transform-003-print-ref.html b/tests/wpt/tests/css/css-break/transform-024-print-ref.html
index 766c415a1be..766c415a1be 100644
--- a/tests/wpt/tests/css/printing/transform-003-print-ref.html
+++ b/tests/wpt/tests/css/css-break/transform-024-print-ref.html
diff --git a/tests/wpt/tests/css/printing/transform-003-print.html b/tests/wpt/tests/css/css-break/transform-024-print.html
index c8effbb8f35..510b4c2e44f 100644
--- a/tests/wpt/tests/css/printing/transform-003-print.html
+++ b/tests/wpt/tests/css/css-break/transform-024-print.html
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1371426">
-<link rel="match" href="transform-003-print-ref.html">
+<link rel="match" href="transform-024-print-ref.html">
<style>
body { margin: 0; }
</style>
diff --git a/tests/wpt/tests/css/css-cascade/WEB_FEATURES.yml b/tests/wpt/tests/css/css-cascade/WEB_FEATURES.yml
new file mode 100644
index 00000000000..38aaee30212
--- /dev/null
+++ b/tests/wpt/tests/css/css-cascade/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: cascade-layers
+ files:
+ - layer-*
diff --git a/tests/wpt/tests/css/css-cascade/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-cascade/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..56f7e89b85a
--- /dev/null
+++ b/tests/wpt/tests/css/css-cascade/parsing/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: cascade-layers
+ files:
+ - layer.html
diff --git a/tests/wpt/tests/css/css-color/WEB_FEATURES.yml b/tests/wpt/tests/css/css-color/WEB_FEATURES.yml
new file mode 100644
index 00000000000..8910deab28b
--- /dev/null
+++ b/tests/wpt/tests/css/css-color/WEB_FEATURES.yml
@@ -0,0 +1,22 @@
+features:
+- name: color-function
+ files:
+ - a98rgb-*
+ - display-p3-*
+ - predefined-*
+ - prophoto-rgb-*
+ - rec2020-*
+ - srgb-*
+ - xyz-*
+- name: color-mix
+ files:
+ - color-mix-*
+ - nested-color-mix-with-currentcolor.html
+- name: lab
+ files:
+ - lab-*
+ - lch-*
+- name: oklab
+ files:
+ - oklab-*
+ - oklch-*
diff --git a/tests/wpt/tests/css/css-color/clip-opacity-out-of-flow-ref.html b/tests/wpt/tests/css/css-color/clip-opacity-out-of-flow-ref.html
new file mode 100644
index 00000000000..7610a6f1911
--- /dev/null
+++ b/tests/wpt/tests/css/css-color/clip-opacity-out-of-flow-ref.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<div style="position: absolute; width: 200px; height: 200px; background: rgb(255 255 128)"></div>
+<div style="position: absolute; width: 250px; height: 100px; background: rgb(128 128 255)"></div>
+<div style="position: absolute; width: 100px; height: 250px; background: rgb(128 192 128)"></div>
+</div>
diff --git a/tests/wpt/tests/css/css-color/clip-opacity-out-of-flow.html b/tests/wpt/tests/css/css-color/clip-opacity-out-of-flow.html
new file mode 100644
index 00000000000..2ad01938064
--- /dev/null
+++ b/tests/wpt/tests/css/css-color/clip-opacity-out-of-flow.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-color/#transparency">
+<link rel="help" href="https://drafts.csswg.org/css-position/#def-cb">
+<link rel="match" href="clip-opacity-out-of-flow-ref.html">
+<meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-60000">
+<div style="overflow: hidden; width: 250px; height: 250px; will-change:transform">
+ <div style="overflow: hidden; width: 300px; height: 300px; transform: translateX(0)">
+ <div style="overflow: hidden; width: 200px; height: 200px">
+ <div style="width: 300px; height: 300px; background: red; opacity: 0.5">
+ <div style="width: 400px; height: 400px; background: yellow">
+ <!-- These out-of-flow positioned elements are not contained by the inner clip,
+ while the outer clip and the opacity still apply. The opacity should also
+ apply to all descendants as a single group. -->
+ <div style="position: absolute; width: 400px; height: 100px; background: blue"></div>
+ <div style="position: fixed; width: 100px; height: 400px; background: green"></div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..cc8273c2adc
--- /dev/null
+++ b/tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml
@@ -0,0 +1,5 @@
+features:
+- name: color-mix
+ files:
+ - "*-color-mix-*"
+ - color-mix-out-of-gamut.html
diff --git a/tests/wpt/tests/css/css-color/parsing/color-computed-color-mix-function.html b/tests/wpt/tests/css/css-color/parsing/color-computed-color-mix-function.html
index 4ac7cb6a272..88cb20e6eba 100644
--- a/tests/wpt/tests/css/css-color/parsing/color-computed-color-mix-function.html
+++ b/tests/wpt/tests/css/css-color/parsing/color-computed-color-mix-function.html
@@ -441,7 +441,7 @@
fuzzy_test_computed_color(`color-mix(in oklab, oklab(0.1 0.2 0.3 / 25%) 0%, oklab(none none none / 0.5))`, `oklab(0.1 0.2 0.3 / 0.5)`);
fuzzy_test_computed_color(`color-mix(in oklab, oklab(0.1 0.2 0.3 / 25%) 0%, oklab(0.5 none none / 0.5))`, `oklab(0.5 0.2 0.3 / 0.5)`);
- for (const colorSpace of [ "srgb", "srgb-linear", "xyz", "xyz-d50", "xyz-d65" ]) {
+ for (const colorSpace of [ "srgb", "srgb-linear", "display-p3", "a98-rgb", "prophoto-rgb", "rec2020", "xyz", "xyz-d50", "xyz-d65" ]) {
const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace;
fuzzy_test_computed_color(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3), color(${colorSpace} .5 .6 .7))`, `color(${resultColorSpace} 0.3 0.4 0.5)`);
diff --git a/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html b/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html
index fd998238079..ac2f9c87aac 100644
--- a/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html
+++ b/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html
@@ -106,6 +106,10 @@
fuzzy_test_computed_color(`rgb(from rgb(100 110 120 / 0.8) calc(r + 1) calc(g + 1) calc(b + 1) / calc(alpha + 0.01))`, `color(srgb 0.396 0.435 0.474 / 0.81)`); // rgb(101 111 121)
fuzzy_test_computed_color(`rgb(from rebeccapurple calc((r / 255) * 100%) calc((g / 255) * 100%) calc((b / 255) * 100%) / calc(alpha * 100%))`, `color(srgb 0.4 0.2 0.6)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`rgb(from rgb(from rebeccapurple r g b / calc(alpha + 0.5)) r g b / calc(alpha - 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`);
+ fuzzy_test_computed_color(`rgb(from rgb(from rebeccapurple r g b / calc(alpha - 1.5)) r g b / calc(alpha + 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`);
+
// Testing with 'none'. Missing components are resolved to zero during color space conversion.
// https://drafts.csswg.org/css-color-4/#missing
fuzzy_test_computed_color(`rgb(from rebeccapurple none none none)`, `color(srgb 0 0 0)`);
@@ -119,6 +123,7 @@
fuzzy_test_computed_color(`rgb(from rgb(none none none / none) r g b / alpha)`, `color(srgb 0 0 0 / 0)`);
fuzzy_test_computed_color(`rgb(from rgb(20% none 60%) r g b)`, `color(srgb 0.2 0 0.6)`);
fuzzy_test_computed_color(`rgb(from rgb(20% 40% 60% / none) r g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0)`);
+ fuzzy_test_computed_color(`color-mix(in srgb, rgb(from rebeccapurple none g b), rebeccapurple)`, `color(srgb 0.4 0.2 0.6)`);
// color-mix
fuzzy_test_computed_color(`rgb(from color-mix(in srgb, red, red) r g b / alpha)`, `color(srgb 1 0 0)`);
@@ -179,6 +184,10 @@
fuzzy_test_computed_color(`hsl(from hsl(20 30 40 / 0.8) calc(h + 1) calc(s + 1) calc(l + 1) / calc(alpha + 0.01))`, `color(srgb 0.537 0.372 0.283 / 0.81)`); // hsl(21 31 41)
fuzzy_test_computed_color(`hsl(from rebeccapurple calc((h / 360) * 360deg) calc((s / 100) * 100%) calc((l / 100) * 100%) / calc(alpha * 100%))`, `color(srgb 0.4 0.2 0.6)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`hsl(from hsl(from rebeccapurple h s l / calc(alpha + 0.5)) h s l / calc(alpha - 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`);
+ fuzzy_test_computed_color(`hsl(from hsl(from rebeccapurple h s l / calc(alpha - 1.5)) h s l / calc(alpha + 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`);
+
// Testing with 'none'. Missing components are resolved to zero during color space conversion.
// https://drafts.csswg.org/css-color-4/#missing
fuzzy_test_computed_color(`hsl(from rebeccapurple none none none)`, `color(srgb 0 0 0)`);
@@ -195,6 +204,7 @@
fuzzy_test_computed_color(`hsl(from hsl(120deg none 50% / .5) h s l)`, `color(srgb 0.5 0.5 0.5 / 0.5)`);
fuzzy_test_computed_color(`hsl(from hsl(120deg 20% 50% / none) h s l / alpha)`, `color(srgb 0.4 0.6 0.4 / 0)`);
fuzzy_test_computed_color(`hsl(from hsl(none 20% 50% / .5) h s l / alpha)`, `color(srgb 0.6 0.4 0.4 / 0.5)`);
+ fuzzy_test_computed_color(`color-mix(in hsl, hsl(from rebeccapurple none s l), rebeccapurple)`, `color(srgb 0.4 0.2 0.6)`);
// color-mix
fuzzy_test_computed_color(`hsl(from color-mix(in srgb, red, red) h s l / alpha)`, `color(srgb 1 0 0)`);
@@ -254,6 +264,10 @@
fuzzy_test_computed_color(`hwb(from hwb(20 30 40 / 0.8) calc(h + 1) calc(w + 1) calc(b + 1) / calc(alpha + 0.01))`, `color(srgb 0.59 0.41 0.31 / 0.81)`); // hwb(21 31 41)
fuzzy_test_computed_color(`hwb(from rebeccapurple calc((h / 360) * 360deg) calc((w / 100) * 100%) calc((b / 100) * 100%) / calc(alpha * 100%))`, `color(srgb 0.4 0.2 0.6)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`hwb(from hwb(from rebeccapurple h w b / calc(alpha + 0.5)) h w b / calc(alpha - 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`);
+ fuzzy_test_computed_color(`hwb(from hwb(from rebeccapurple h w b / calc(alpha - 1.5)) h w b / calc(alpha + 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`);
+
// Testing with 'none'. Missing components are resolved to zero during color space conversion.
// https://drafts.csswg.org/css-color-4/#missing
fuzzy_test_computed_color(`hwb(from rebeccapurple none none none)`, `color(srgb 1 0 0)`);
@@ -270,6 +284,7 @@
fuzzy_test_computed_color(`hwb(from hwb(120deg none 50% / .5) h w b)`, `color(srgb 0 0.5 0 / 0.5)`);
fuzzy_test_computed_color(`hwb(from hwb(120deg 20% 50% / none) h w b / alpha)`, `color(srgb 0.2 0.5 0.2 / 0)`);
fuzzy_test_computed_color(`hwb(from hwb(none 20% 50% / .5) h w b / alpha)`, `color(srgb 0.5 0.2 0.2 / 0.5)`);
+ fuzzy_test_computed_color(`color-mix(in hwb, hwb(from rebeccapurple none w b), rebeccapurple)`, `color(srgb 0.4 0.2 0.6)`);
// color-mix
fuzzy_test_computed_color(`hwb(from color-mix(in srgb, red, red) h w b / alpha)`, `color(srgb 1 0 0)`);
@@ -325,6 +340,10 @@
fuzzy_test_computed_color(`lab(from lab(50 5 10 / 0.8) calc(l + 1) calc(a + 1) calc(b + 1) / calc(alpha + 0.01))`, `lab(51 6 11 / 0.81)`);
fuzzy_test_computed_color(`lab(from lab(25 20 50) calc((l / 100) * 100%) calc((a / 125) * 100%) calc((b / 125) * 100%) / calc(alpha * 100%))`, `lab(25 20 50)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`lab(from lab(from lab(25 20 50) l a b / calc(alpha + 0.5)) l a b / calc(alpha - 0.5))`, `lab(25 20 50 / 0.5)`);
+ fuzzy_test_computed_color(`lab(from lab(from lab(25 20 50) l a b / calc(alpha - 1.5)) l a b / calc(alpha + 0.5))`, `lab(25 20 50 / 0.5)`);
+
// Testing with 'none'.
fuzzy_test_computed_color(`lab(from lab(25 20 50) none none none)`, `lab(none none none)`);
fuzzy_test_computed_color(`lab(from lab(25 20 50) none none none / none)`, `lab(none none none / none)`);
@@ -338,6 +357,7 @@
fuzzy_test_computed_color(`lab(from lab(none none none / none) l a b / alpha)`, `lab(0 0 0 / 0)`);
fuzzy_test_computed_color(`lab(from lab(25 none 50) l a b)`, `lab(25 0 50)`);
fuzzy_test_computed_color(`lab(from lab(25 20 50 / none) l a b / alpha)`, `lab(25 20 50 / 0)`);
+ fuzzy_test_computed_color(`color-mix(in lab, lab(from lab(25 20 50) none a b), lab(25 20 50))`, `lab(25 20 50)`);
// color-mix
fuzzy_test_computed_color(`lab(from color-mix(in lab, lab(25 20 50), lab(25 20 50)) l a b / alpha)`, `lab(25 20 50)`);
@@ -393,6 +413,10 @@
fuzzy_test_computed_color(`oklab(from oklab(0.5 .05 0.1 / 0.8) calc(l + 0.01) calc(a + 0.01) calc(b + 0.01) / calc(alpha + 0.01))`, `oklab(0.51 .06 0.11 / 0.81)`);
fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5) calc(l * 100%) calc((a / 0.4) * 100%) calc((b / 0.4) * 100%) / calc(alpha * 100%))`, `oklab(0.25 0.2 0.5)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`oklab(from oklab(from oklab(0.25 0.2 0.5) l a b / calc(alpha + 0.5)) l a b / calc(alpha - 0.5))`, `oklab(0.25 0.2 0.5 / 0.5)`);
+ fuzzy_test_computed_color(`oklab(from oklab(from oklab(0.25 0.2 0.5) l a b / calc(alpha - 1.5)) l a b / calc(alpha + 0.5))`, `oklab(0.25 0.2 0.5 / 0.5)`);
+
// Testing with 'none'.
fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5) none none none)`, `oklab(none none none)`);
fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5) none none none / none)`, `oklab(none none none / none)`);
@@ -406,6 +430,7 @@
fuzzy_test_computed_color(`oklab(from oklab(none none none / none) l a b / alpha)`, `oklab(0 0 0 / 0)`);
fuzzy_test_computed_color(`oklab(from oklab(0.25 none 0.5) l a b)`, `oklab(0.25 0 0.5)`);
fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5 / none) l a b / alpha)`, `oklab(0.25 0.2 0.5 / 0)`);
+ fuzzy_test_computed_color(`color-mix(in oklab, oklab(from oklab(0.25 0.2 0.5) none a b), oklab(0.25 0.2 0.5))`, `oklab(0.25 0.2 0.5)`);
// color-mix
fuzzy_test_computed_color(`oklab(from color-mix(in oklab, oklab(0.25 0.2 0.5), oklab(0.25 0.2 0.5)) l a b / alpha)`, `oklab(0.25 0.2 0.5)`);
@@ -469,6 +494,10 @@
fuzzy_test_computed_color(`lch(from lch(50 5 10 / 0.8) calc(l + 1) calc(c + 1) calc(h + 1) / calc(alpha + 0.01))`, `lch(51 6 11 / 0.81)`);
fuzzy_test_computed_color(`lch(from lch(0.7 45 30) calc((l / 100) * 100%) calc((c / 150) * 100%) calc((h / 360) * 360deg) / calc(alpha * 100%))`, `lch(0.7 45 30)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`lch(from lch(from lch(0.7 45 30) l c h / calc(alpha + 0.5)) l c h / calc(alpha - 0.5))`, `lch(0.7 45 30 / 0.5)`);
+ fuzzy_test_computed_color(`lch(from lch(from lch(0.7 45 30) l c h / calc(alpha - 1.5)) l c h / calc(alpha + 0.5))`, `lch(0.7 45 30 / 0.5)`);
+
// Testing with 'none'.
fuzzy_test_computed_color(`lch(from lch(0.7 45 30) none none none)`, `lch(none none none)`);
fuzzy_test_computed_color(`lch(from lch(0.7 45 30) none none none / none)`, `lch(none none none / none)`);
@@ -482,6 +511,7 @@
fuzzy_test_computed_color(`lch(from lch(none none none / none) l c h / alpha)`, `lch(0 0 0 / 0)`);
fuzzy_test_computed_color(`lch(from lch(0.7 none 30) l c h)`, `lch(0.7 0 30)`);
fuzzy_test_computed_color(`lch(from lch(0.7 45 30 / none) l c h / alpha)`, `lch(0.7 45 30 / 0)`);
+ fuzzy_test_computed_color(`color-mix(in lch, lch(from lch(0.7 45 30) l c none), lch(0.7 45 30))`, `lch(0.7 45 30)`);
// color-mix
fuzzy_test_computed_color(`lch(from color-mix(in lch, lch(70 45 30), lch(70 45 30)) l c h / alpha)`, `lch(70 45 30)`);
@@ -546,6 +576,10 @@
fuzzy_test_computed_color(`oklch(from oklch(0.5 .05 0.1 / 0.8) calc(l + 0.01) calc(c + 0.01) calc(h + 0.01) / calc(alpha + 0.01))`, `oklch(0.51 .06 0.11 / 0.81)`);
fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30) calc(l * 100%) calc((c / 0.4) * 100%) calc((h / 360) * 360deg) / calc(alpha * 100%))`, `oklch(0.7 0.45 30)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`oklch(from oklch(from oklch(0.7 0.45 30) l c h / calc(alpha + 0.5)) l c h / calc(alpha - 0.5))`, `oklch(0.7 0.45 30 / 0.5)`);
+ fuzzy_test_computed_color(`oklch(from oklch(from oklch(0.7 0.45 30) l c h / calc(alpha - 1.5)) l c h / calc(alpha + 0.5))`, `oklch(0.7 0.45 30 / 0.5)`);
+
// Testing with 'none'.
fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30) none none none)`, `oklch(none none none)`);
fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30) none none none / none)`, `oklch(none none none / none)`);
@@ -559,6 +593,7 @@
fuzzy_test_computed_color(`oklch(from oklch(none none none / none) l c h / alpha)`, `oklch(0 0 0 / 0)`);
fuzzy_test_computed_color(`oklch(from oklch(0.7 none 30) l c h)`, `oklch(0.7 0 30)`);
fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30 / none) l c h / alpha)`, `oklch(0.7 0.45 30 / 0)`);
+ fuzzy_test_computed_color(`color-mix(in oklch, oklch(from oklch(0.7 0.45 30) l c none), oklch(0.7 0.45 30))`, `oklch(0.7 0.45 30)`);
// color-mix
fuzzy_test_computed_color(`oklch(from color-mix(in oklch, oklch(0.7 0.45 30), oklch(0.7 0.45 30)) l c h / alpha)`, `oklch(0.7 0.45 30)`);
@@ -637,6 +672,10 @@
fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3 / 0.8) ${colorSpace} calc(r + 0.01) calc(g + 0.01) calc(b + 0.01) / calc(alpha + 0.01))`, `color(${colorSpace} 0.71 0.51 0.31 / 0.81)`);
fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} calc(r * 100%) calc(g * 100%) calc(b * 100%) / calc(alpha * 100%))`, `color(${colorSpace} 0.7 0.5 0.3)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / calc(alpha + 0.5)) ${colorSpace} r g b / calc(alpha - 0.5))`, `color(${colorSpace} 0.7 0.5 0.3 / 0.5)`);
+ fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / calc(alpha - 1.5)) ${colorSpace} r g b / calc(alpha + 0.5))`, `color(${colorSpace} 0.7 0.5 0.3 / 0.5)`);
+
// Testing with 'none'.
fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} none none none)`, `color(${colorSpace} none none none)`);
fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} none none none / none)`, `color(${colorSpace} none none none / none)`);
@@ -702,6 +741,10 @@
fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100 / 0.8) ${colorSpace} calc(x + 1) calc(y + 1) calc(z + 1) / calc(alpha + 0.01))`, `color(${resultColorSpace} 8 -19.5 101 / 0.81)`);
fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} calc(x * 100%) calc(y * 100%) calc(z * 100%) / calc(alpha * 100%))`, `color(${resultColorSpace} 7 -20.5 100)`);
+ // Alpha is clamped to [0,1]
+ fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / calc(alpha + 0.5)) ${colorSpace} x y z / calc(alpha - 0.5))`, `color(${resultColorSpace} 7 -20.5 100 / 0.5)`);
+ fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / calc(alpha - 1.5)) ${colorSpace} x y z / calc(alpha + 0.5))`, `color(${resultColorSpace} 7 -20.5 100 / 0.5)`);
+
// Testing with 'none'.
fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} none none none)`, `color(${resultColorSpace} none none none)`);
fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} none none none / none)`, `color(${resultColorSpace} none none none / none)`);
diff --git a/tests/wpt/tests/css/css-color/parsing/color-invalid-color-mix-function.html b/tests/wpt/tests/css/css-color/parsing/color-invalid-color-mix-function.html
index 40299644bff..2f815f81400 100644
--- a/tests/wpt/tests/css/css-color/parsing/color-invalid-color-mix-function.html
+++ b/tests/wpt/tests/css/css-color/parsing/color-invalid-color-mix-function.html
@@ -74,7 +74,7 @@
test_invalid_value(`color`, `color-mix(${colorSpace}(10% 20 30), ${colorSpace}(50% 60 70))`); // Missing interpolation method.
}
- for (const colorSpace of [ "srgb", "srgb-linear", "xyz", "xyz-d50", "xyz-d65" ]) {
+ for (const colorSpace of [ "srgb", "srgb-linear", "display-p3", "a98-rgb", "prophoto-rgb", "rec2020", "xyz", "xyz-d50", "xyz-d65" ]) {
test_invalid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) -10%, color(${colorSpace} .5 .6 .7))`); // Percentages less than 0 are not valid.
test_invalid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) 150%, color(${colorSpace} .5 .6 .7))`); // Percentages greater than 100 are not valid.
test_invalid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) 0%, color(${colorSpace} .5 .6 .7) 0%)`); // Sum of percengates cannot be 0%.
diff --git a/tests/wpt/tests/css/css-color/parsing/color-valid-color-mix-function.html b/tests/wpt/tests/css/css-color/parsing/color-valid-color-mix-function.html
index f11ecc8e472..cd1f381a1aa 100644
--- a/tests/wpt/tests/css/css-color/parsing/color-valid-color-mix-function.html
+++ b/tests/wpt/tests/css/css-color/parsing/color-valid-color-mix-function.html
@@ -380,7 +380,7 @@
test_valid_value(`color`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / 0.5))`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / 0.5))`);
test_valid_value(`color`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / none))`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / none))`);
- for (const colorSpace of [ "srgb", "srgb-linear", "xyz", "xyz-d50", "xyz-d65" ]) {
+ for (const colorSpace of [ "srgb", "srgb-linear", "display-p3", "a98-rgb", "prophoto-rgb", "rec2020", "xyz", "xyz-d50", "xyz-d65" ]) {
const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace;
test_valid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3), color(${colorSpace} .5 .6 .7))`, `color-mix(in ${resultColorSpace}, color(${resultColorSpace} 0.1 0.2 0.3), color(${resultColorSpace} 0.5 0.6 0.7))`);
diff --git a/tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml b/tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml
new file mode 100644
index 00000000000..5e69c923abf
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml
@@ -0,0 +1,13 @@
+features:
+- name: font-palette
+ files:
+ - font-palette.html
+ - font-palette-*
+ - palette-values-rule-*
+- name: font-synthesis
+ files:
+ - font-synthesis-*
+- name: font-variant-alternates
+ files:
+ - alternates-order.html
+ - font-variant-alternates-*
diff --git a/tests/wpt/tests/css/css-fonts/animations/WEB_FEATURES.yml b/tests/wpt/tests/css/css-fonts/animations/WEB_FEATURES.yml
new file mode 100644
index 00000000000..f99e4bb4e0f
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/animations/WEB_FEATURES.yml
@@ -0,0 +1,6 @@
+features:
+- name: font-palette-animation
+ files:
+ - font-palette-animation-not-specified-endpoints.html
+ - font-palette-interpolation.html
+ - multiple-elements-font-palette-animation.html
diff --git a/tests/wpt/tests/css/css-fonts/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-fonts/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..6a24d269b48
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/parsing/WEB_FEATURES.yml
@@ -0,0 +1,14 @@
+features:
+- name: font-optical-sizing
+ files:
+ - font-optical-sizing-*
+- name: font-palette
+ files:
+ - font-palette-*
+ - font-palette-values-*
+- name: font-synthesis
+ files:
+ - font-synthesis-*
+- name: font-variant-alternates
+ files:
+ - font-variant-alternates-*
diff --git a/tests/wpt/tests/css/css-fonts/variations/WEB_FEATURES.yml b/tests/wpt/tests/css/css-fonts/variations/WEB_FEATURES.yml
new file mode 100644
index 00000000000..c035a0ee11b
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/variations/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: font-optical-sizing
+ files:
+ - variable-opsz*
diff --git a/tests/wpt/tests/css/css-grid/subgrid/placement-invalidation-001.html b/tests/wpt/tests/css/css-grid/subgrid/placement-invalidation-001.html
new file mode 100644
index 00000000000..954880e9965
--- /dev/null
+++ b/tests/wpt/tests/css/css-grid/subgrid/placement-invalidation-001.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Test: Subgridded item placement invalidation</title>
+<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid-2/#track-sizing">
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+#grid {
+ width: 200px;
+ display: grid;
+ background: lightgray;
+ grid-template-columns: [start] auto [end] 1fr;
+ grid-template-rows: 100px;
+}
+#subgrid {
+ display: grid;
+ grid-column: 1 / -1;
+ grid-template-columns: subgrid;
+}
+#item {
+ width: 50px;
+ background: lightblue;
+ border: 5px solid gray;
+ grid-column: start / end;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<div id="grid">
+ <div id="subgrid">
+ <div id="item" data-offset-x="140"></div>
+ </div>
+</div>
+<script>
+"use strict";
+
+let grid = document.getElementById("grid");
+let item = document.getElementById("item");
+
+// Computing an offset forces layout.
+let item_offset = item.offsetLeft;
+
+grid.style.gridTemplateColumns = "1fr [start] auto [end]";
+item_offset = item.offsetLeft;
+checkLayout("#item");
+</script>
diff --git a/tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops-ref.html b/tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops-ref.html
new file mode 100644
index 00000000000..28d57bc19c7
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link rel="author" title="Aditya Keerthi" href="https://github.com/pxlcoder">
+<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#color-scheme-prop">
+<link rel="help" href="https://www.w3.org/TR/css-color-4/#css-system-colors">
+<link rel="help" href="https://www.w3.org/TR/css-color-5/#light-dark">
+<link rel="help" href="https://www.w3.org/TR/css-color-5/#color-mix">
+<title>Reference: Test changing used color-scheme updates gradient with color-scheme dependent color stops.</title>
+<style>
+
+.box {
+ color-scheme: dark;
+
+ width: 100px;
+ height: 100px;
+}
+
+#system-color {
+ background-image: linear-gradient(CanvasText, CanvasText);
+}
+
+#system-color-in-color-mix {
+ background-image: linear-gradient(color-mix(in lch, Canvas, pink), color-mix(in lch, Canvas, pink));
+}
+
+#light-dark {
+ background-image: linear-gradient(light-dark(red, green), light-dark(red, green));
+}
+
+#light-dark-in-color-mix {
+ background-image: linear-gradient(color-mix(in lch, light-dark(red, green), pink), color-mix(in lch, light-dark(red, green), pink));
+}
+
+</style>
+</head>
+<body>
+<p>Test system color</p>
+<div id="system-color" class="box"></div>
+<p>Test system color in color-mix()</p>
+<div id="system-color-in-color-mix" class="box"></div>
+<p>Test light-dark()</p>
+<div id="light-dark" class="box"></div>
+<p>Test light-dark() in color-mix()</p>
+<div id="light-dark-in-color-mix" class="box"></div>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops.html b/tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops.html
new file mode 100644
index 00000000000..f95a557703c
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/gradient/color-scheme-dependent-color-stops.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<link rel="author" title="Aditya Keerthi" href="https://github.com/pxlcoder">
+<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#color-scheme-prop">
+<link rel="help" href="https://www.w3.org/TR/css-color-4/#css-system-colors">
+<link rel="help" href="https://www.w3.org/TR/css-color-5/#light-dark">
+<link rel="help" href="https://www.w3.org/TR/css-color-5/#color-mix">
+<title>Test changing used color-scheme updates gradient with color-scheme dependent color stops.</title>
+<link rel="match" href="color-scheme-dependent-color-stops-ref.html">
+<style>
+
+.dark {
+ color-scheme: dark;
+}
+
+.box {
+ width: 100px;
+ height: 100px;
+}
+
+#system-color {
+ background-image: linear-gradient(CanvasText, CanvasText);
+}
+
+#system-color-in-color-mix {
+ background-image: linear-gradient(color-mix(in lch, Canvas, pink), color-mix(in lch, Canvas, pink));
+}
+
+#light-dark {
+ background-image: linear-gradient(light-dark(red, green), light-dark(red, green));
+}
+
+#light-dark-in-color-mix {
+ background-image: linear-gradient(color-mix(in lch, light-dark(red, green), pink), color-mix(in lch, light-dark(red, green), pink));
+}
+
+</style>
+</head>
+<body>
+<p>Test system color</p>
+<div id="system-color" class="box"></div>
+<p>Test system color in color-mix()</p>
+<div id="system-color-in-color-mix" class="box"></div>
+<p>Test light-dark()</p>
+<div id="light-dark" class="box"></div>
+<p>Test light-dark() in color-mix()</p>
+<div id="light-dark-in-color-mix" class="box"></div>
+<script>
+
+requestAnimationFrame(() => {
+ document.querySelectorAll(".box").forEach((box) => {
+ box.classList.add("dark");
+ });
+
+ document.documentElement.className = '';
+});
+
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-masking/animations/clip-path-interpolation-shape.html b/tests/wpt/tests/css/css-masking/animations/clip-path-interpolation-shape.html
index cda6703ca72..638e133c26b 100644
--- a/tests/wpt/tests/css/css-masking/animations/clip-path-interpolation-shape.html
+++ b/tests/wpt/tests/css/css-masking/animations/clip-path-interpolation-shape.html
@@ -83,7 +83,6 @@ test_no_interpolation({
to: 'shape(from 10px 10px, close)',
});
-
test_interpolation({
property: 'clip-path',
from: 'shape(from 5% 5px, hline to 5%, vline to -5px, close)',
@@ -116,6 +115,7 @@ test_interpolation({
{at: -0.3, expect: 'shape(from 2% 2px, curve by 7% 13px via -3% 86px, curve by 33% 17px via 17% 53px 34% 61px)'},
{at: 0, expect: 'shape(from 5% 5px, curve by 10% 10px via 0% 80px, curve by 30% 20px via 20% 50px 25% 70px)'},
{at: 0.5, expect: 'shape(from 10% 10px, curve by 15% 5px via 5% 70px, curve by 25% 25px via 25% 45px 10% 85px)'},
+ {at: 1, expect: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)'},
{at: 1.5, expect: 'shape(from 20% 20px, curve by 25% -5px via 15% 50px, curve by 15% 35px via 35% 35px -20% 115px)'},
]);
@@ -127,6 +127,7 @@ test_interpolation({
{at: -0.3, expect: 'shape(from 2% 2px, smooth to 7% 13px via -3% 86px, smooth to 33% 17px)'},
{at: 0, expect: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)'},
{at: 0.5, expect: 'shape(from 10% 10px, smooth to 15% 5px via 5% 70px, smooth to 25% 25px)'},
+ {at: 1, expect: 'shape(from 15% 15px, smooth to 20% 0px via 10% 60px, smooth to 20% 30px)'},
{at: 1.5, expect: 'shape(from 20% 20px, smooth to 25% -5px via 15% 50px, smooth to 15% 35px)'},
]);
@@ -138,6 +139,7 @@ test_interpolation({
{at: -0.3, expect: 'shape(from 2% 2px, smooth by 7% 13px via -3% 86px, smooth by 33% 17px)'},
{at: 0, expect: 'shape(from 5% 5px, smooth by 10% 10px via 0% 80px, smooth by 30% 20px)'},
{at: 0.5, expect: 'shape(from 10% 10px, smooth by 15% 5px via 5% 70px, smooth by 25% 25px)'},
+ {at: 1, expect: 'shape(from 15% 15px, smooth by 20% 0px via 10% 60px, smooth by 20% 30px)'},
{at: 1.5, expect: 'shape(from 20% 20px, smooth by 25% -5px via 15% 50px, smooth by 15% 35px)'},
]);
@@ -166,5 +168,115 @@ test_interpolation({
{at: 1.5, expect: 'shape(from -10px calc(2.5% + 7.5px), hline to 25px, vline by calc(15% - 80px), hline by 19.88px, close, vline by 10px)'},
]);
+test_no_interpolation({
+ property: 'clip-path',
+ from: 'shape(from 10px 10px, move to 10% 10%)',
+ to: 'path("M10 10 z")',
+});
+
+test_no_interpolation({
+ property: 'clip-path',
+ from: 'path("M10 10 M10 10")',
+ to: 'shape(from 10px 10px, close)',
+});
+
+test_no_interpolation({
+ property: 'clip-path',
+ from: 'path("M10 10 h 5")',
+ to: 'shape(from 10px 10px, hline to 5px)',
+});
+
+test_no_interpolation({
+ property: 'clip-path',
+ from: 'shape(nonzero from 10px 10px, move to 10% 10%)',
+ to: 'path(evenodd, "M0 0 M20 20")',
+});
+
+test_interpolation({
+ property: 'clip-path',
+ from: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)',
+ to: 'path("M 15 15 H 25 V -15 Z")',
+}, [
+ {at: -0.3, expect: 'shape(from 2px 2px, hline to -1px, vline to -2px, close)'},
+ {at: 0, expect: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)'},
+ {at: 0.5, expect: 'shape(from 10px 10px, hline to 15px, vline to -10px, close)'},
+ {at: 1, expect: 'shape(from 15px 15px, hline to 25px, vline to -15px, close)'},
+ {at: 1.5, expect: 'shape(from 20px 20px, hline to 35px, vline to -20px, close)'},
+]);
+
+test_interpolation({
+ property: 'clip-path',
+ from: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)',
+ to: 'path("M 15 15 Q 10 60 20 0 C 30 40 -5 100 20 30")',
+}, [
+ {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, curve to calc(13% - 6px) 13px via calc(0% - 3px) 86px, curve to calc(39% - 6px) 17px via calc(26% - 9px) 53px calc(32.5% + 1.5px) 61px)'},
+ {at: 0, expect: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)'},
+ {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, curve to calc(5% + 10px) 5px via calc(0% + 5px) 70px, curve to calc(15% + 10px) 25px via calc(10% + 15px) 45px calc(12.5% - 2.5px) 85px)'},
+ {at: 1, expect: 'shape(from calc(0% + 15px) 15px, curve to calc(0% + 20px) 0px via calc(0% + 10px) 60px, curve to calc(0% + 20px) 30px via calc(0% + 30px) 40px calc(0% - 5px) 100px)'},
+ {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, curve to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, curve to calc(-15% + 30px) 35px via calc(-10% + 45px) 35px calc(-12.5% - 7.5px) 115px)'},
+]);
+
+test_interpolation({
+ property: 'clip-path',
+ from: 'path("M 5 5 q 0 80 10 10 c 20 50 25 70 30 20")',
+ to: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)',
+}, [
+ {at: -0.3, expect: 'shape(from calc(-4.5% + 6.5px) 2px, curve by calc(-6% + 13px) 13px via -3% 86px, curve by calc(-6% + 39px) 17px via calc(-9% + 26px) 53px calc(1.5% + 32.5px) 61px)'},
+ {at: 0, expect: 'shape(from calc(0% + 5px) 5px, curve by calc(0% + 10px) 10px via 0% 80px, curve by calc(0% + 30px) 20px via calc(0% + 20px) 50px calc(0% + 25px) 70px)'},
+ {at: 0.5, expect: 'shape(from calc(7.5% + 2.5px) 10px, curve by calc(10% + 5px) 5px via 5% 70px, curve by calc(10% + 15px) 25px via calc(15% + 10px) 45px calc(-2.5% + 12.5px) 85px)'},
+ {at: 1, expect: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)'},
+ {at: 1.5, expect: 'shape(from calc(22.5% - 2.5px) 20px, curve by calc(30% - 5px) -5px via 15% 50px, curve by calc(30% - 15px) 35px via calc(45% - 10px) 35px calc(-7.5% - 12.5px) 115px)'},
+]);
+
+test_interpolation({
+ property: 'clip-path',
+ from: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)',
+ to: 'path("M 15 15 S 10 60 20 0 T 20 30")',
+}, [
+ {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, smooth to calc(13% - 6px) 13px via calc(0% - 3px) 86px, smooth to calc(39% - 6px) 17px)'},
+ {at: 0, expect: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)'},
+ {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, smooth to calc(5% + 10px) 5px via calc(0% + 5px) 70px, smooth to calc(15% + 10px) 25px)'},
+ {at: 1, expect: 'shape(from calc(0% + 15px) 15px, smooth to calc(0% + 20px) 0px via calc(0% + 10px) 60px, smooth to calc(0% + 20px) 30px)'},
+ {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, smooth to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, smooth to calc(-15% + 30px) 35px)'},
+]);
+
+test_interpolation({
+ property: 'clip-path',
+ from: 'path("M 5 5 s 0 80 10 10 t 30 20")',
+ to: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)',
+}, [
+ {at: -0.3, expect: 'shape(from 2px 2px, smooth by 7px 13px via -3px 86px, smooth by 33px 17px)'},
+ {at: 0, expect: 'shape(from 5px 5px, smooth by 10px 10px via 0px 80px, smooth by 30px 20px)'},
+ {at: 0.5, expect: 'shape(from 10px 10px, smooth by 15px 5px via 5px 70px, smooth by 25px 25px)'},
+ {at: 1, expect: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)'},
+ {at: 1.5, expect: 'shape(from 20px 20px, smooth by 25px -5px via 15px 50px, smooth by 15px 35px)'},
+]);
+
+test_interpolation({
+ property: 'clip-path',
+ from: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)',
+ to: 'path("M 15 15 A 20,30 0 0,0 5,-25 a 20,20 270 0,1 25,-15 A 10,5 0 0,0 25 20")',
+}, [
+ {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, arc to calc(19.5% - 1.5px) -12px of 7px 17px, arc by calc(19.5% - 7.5px) -2px of 33px cw large rotate -42deg, arc to calc(32.5% - 7.5px) 20px of 10px 5px)'},
+ {at: 0, expect: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)'},
+ {at: 0.3, expect: 'shape(from calc(3.5% + 4.5px) 8px, arc to calc(10.5% + 1.5px) -18px of 13px 23px, arc by calc(10.5% + 7.5px) -8px of 27px cw large rotate 102deg, arc to calc(17.5% + 7.5px) 20px of 10px 5px)'},
+ {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, arc to calc(7.5% + 2.5px) -20px of 15px 25px, arc by calc(7.5% + 12.5px) -10px of 25px cw large rotate 150deg, arc to calc(12.5% + 12.5px) 20px of 10px 5px)'},
+ {at: 1, expect: 'shape(from calc(0% + 15px) 15px, arc to calc(0% + 5px) -25px of 20px 30px, arc by calc(0% + 25px) -15px of 20px cw rotate 270deg, arc to calc(0% + 25px) 20px of 10px 5px)'},
+ {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, arc to calc(-7.5% + 7.5px) -30px of 25px 35px, arc by calc(-7.5% + 37.5px) -20px of 15px cw rotate 390deg, arc to calc(-12.5% + 37.5px) 20px of 10px 5px)'},
+]);
+
+test_interpolation({
+ property: 'clip-path',
+ from: 'path("M 5 5 A 10,20 0 0,0 15,-15 a 30,30 30 1,1 15,-5 A 10,5 0 0,0 25 20")',
+ to: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px cw rotate 270deg small, arc to 25px 20px of 10px 5px small cw)'
+}, [
+ {at: -0.3, expect: 'shape(from 2px 2px, arc to 18px -12px of 7px 17px ccw small, arc by 12px -2px of 33px 33px rotate -42deg cw large , arc to 25px 20px of 10px 5px ccw small)'},
+ {at: 0, expect: 'shape(from 5px 5px, arc to 15px -15px of 10px 20px, arc by 15px -5px of 30px cw rotate 30deg large, arc to 25px 20px of 10px 5px small)'},
+ {at: 0.3, expect: 'shape(from 8px 8px, arc to 12px -18px of 13px 23px ccw small, arc by 18px -8px of 27px 27px rotate 102deg cw large, arc to 25px 20px of 10px 5px ccw small )'},
+ {at: 0.5, expect: 'shape(from 10px 10px, arc to 10px -20px of 15px 25px ccw small, arc by 20px -10px of 25px rotate 150deg cw large, arc to 25px 20px of 10px 5px cw small)'},
+ {at: 1, expect: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px rotate 270deg cw small, arc to 25px 20px of 10px 5px cw small)'},
+ {at: 1.5, expect: 'shape(from 20px 20px, arc to 0px -30px of 25px 35px ccw small, arc by 30px -20px of 15px rotate 390deg cw small, arc to 25px 20px of 10px 5px cw small)'},
+]);
+
</script>
</body>
diff --git a/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-003.html b/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-003.html
new file mode 100644
index 00000000000..1324aad97f3
--- /dev/null
+++ b/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-003.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>CSS Masking: Test clip-path interpolation from shape() to path()</title>
+ <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape">
+ <link rel="match" href="clip-path-path-interpolation-001-ref.html">
+ <meta name="assert" content="The clip-path property takes the basic shape
+ 'shape()' for clipping. Test the interpolation of nonzero
+ shape and path function.">
+ <style>
+ @keyframes anim {
+ from {
+ clip-path: shape(nonzero from 20px 20px,
+ hline by 60px, vline by 60px, hline by -60%, close,
+ move to 30% 30px, hline by 40px, vline by 40px, hline by -40px, close);
+ }
+ to {
+ clip-path: path(nonzero, "M50 50 h50 v50 h-50 z M20 20 h50 v50 h-50 z");
+ }
+ }
+ #rect {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ animation: anim 10s -5s paused linear;
+ }
+ </style>
+</head>
+<body>
+ <div id="rect"></div>
+</body>
+<script>
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove('reftest-wait');
+ });
+</script>
+</html>
diff --git a/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-004.html b/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-004.html
new file mode 100644
index 00000000000..69bec3c097c
--- /dev/null
+++ b/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-004.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>CSS Masking: Test clip-path interpolation from path() to shape()</title>
+ <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape">
+ <link rel="match" href="clip-path-path-interpolation-002-ref.html">
+ <meta name="assert" content="The clip-path property takes the basic shape
+ 'shape()' for clipping. Test the interpolation of evenodd
+ path and shape function.">
+ <style>
+ @keyframes anim {
+ from {
+ clip-path: path(evenodd, "M20 20 h60 v60 h-60 z M30 30 h40 v40 h-40 z");
+ }
+ to {
+ clip-path: shape(evenodd from 50px 50px,
+ hline by 50px, vline by 50px, hline by -50%, close,
+ move to 20px 20%, hline by 50px, vline by 50px, hline by -50px, close);
+ }
+ }
+ #rect {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ animation: anim 10s -5s paused linear;
+ }
+ </style>
+</head>
+<body>
+ <div id="rect"></div>
+</body>
+<script>
+ requestAnimationFrame(() => {
+ document.documentElement.classList.remove('reftest-wait');
+ });
+</script>
+</html>
diff --git a/tests/wpt/tests/css/css-masking/clip-path/clip-path-scaled-video.html b/tests/wpt/tests/css/css-masking/clip-path/clip-path-scaled-video.html
index c92702d8e78..03e1b21d175 100644
--- a/tests/wpt/tests/css/css-masking/clip-path/clip-path-scaled-video.html
+++ b/tests/wpt/tests/css/css-masking/clip-path/clip-path-scaled-video.html
@@ -14,32 +14,32 @@
</clipPath>
<g clip-path="url(#clip)" transform="scale(0.112)">
<foreignObject width="320" height="240">
- <video src="/media/test.ogv" autoplay loop></video>
+ <video src="/media/test.webm" autoplay loop></video>
</foreignObject>
</g>
<g clip-path="url(#clip)" transform="scale(0.345)">
<foreignObject width="320" height="240">
- <video src="/media/test.ogv" autoplay loop></video>
+ <video src="/media/test.webm" autoplay loop></video>
</foreignObject>
</g>
<g clip-path="url(#clip)" transform="scale(0.778)">
<foreignObject width="320" height="240">
- <video src="/media/test.ogv" autoplay loop></video>
+ <video src="/media/test.webm" autoplay loop></video>
</foreignObject>
</g>
<g clip-path="url(#clip)" transform="scale(0.912)">
<foreignObject width="320" height="240">
- <video src="/media/test.ogv" autoplay loop></video>
+ <video src="/media/test.webm" autoplay loop></video>
</foreignObject>
</g>
<g clip-path="url(#clip)" transform="scale(1.678)">
<foreignObject width="320" height="240">
- <video src="/media/test.ogv" autoplay loop></video>
+ <video src="/media/test.webm" autoplay loop></video>
</foreignObject>
</g>
<g clip-path="url(#clip)" transform="scale(3.333)">
<foreignObject width="320" height="240">
- <video src="/media/test.ogv" oncanplaythrough="takeScreenshot()" autoplay loop></video>
+ <video src="/media/test.webm" oncanplaythrough="takeScreenshot()" autoplay loop></video>
</foreignObject>
</g>
</svg>
diff --git a/tests/wpt/tests/css/css-multicol/crashtests/block-in-inline-become-float.html b/tests/wpt/tests/css/css-multicol/crashtests/block-in-inline-become-float.html
new file mode 100644
index 00000000000..6d557b7c3ab
--- /dev/null
+++ b/tests/wpt/tests/css/css-multicol/crashtests/block-in-inline-become-float.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="http://crbug.com/329674902">
+<div style="width:10px; line-height:20px; columns:3; gap:0; height:40px; column-fill:auto; orphans:1; widows:1;">
+ <br>
+ <span>
+ <div id="trouble" style="contain:size; width:100%; height:100px;"></div>
+ xxxxxxxxxxxxxxxxxxxxxxxx
+ xxxxxxxxxxxxxxxxxxxxxxxx
+ </span>
+ <script>
+ document.body.offsetTop;
+ trouble.style.cssFloat = "left";
+ </script>
+</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-001.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-001.tentative.html
new file mode 100644
index 00000000000..c8cfcb1066d
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-001.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp basic test</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="line-clamp should clamp to the specified number of lines, without needing display: -webkit-box, -webkit-box-orient, or overflow: hidden.">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ white-space: pre;
+ background-color: yellow;
+ padding: 0 4px;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-002.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-002.tentative.html
new file mode 100644
index 00000000000..5f21b545fb3
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-002.tentative.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with fewer lines than specified</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-001-ref.html">
+<meta name="assert" content="line-clamp should not have an effect on an element with fewer lines than specified.">
+<style>
+.clamp {
+ line-clamp: 6;
+ font: 16px / 32px serif;
+ white-space: pre;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-003.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-003.tentative.html
new file mode 100644
index 00000000000..fa3b7472e5e
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-003.tentative.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with exactly as many lines as specified</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-001-ref.html">
+<meta name="assert" content="line-clamp should not have an effect on an element with exactly as many lines as specified.">
+<style>
+.clamp {
+ line-clamp: 5;
+ font: 16px / 32px serif;
+ white-space: pre;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-004.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-004.tentative.html
new file mode 100644
index 00000000000..c766d195b7c
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-004.tentative.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: sizing of line-clamp affected elements</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-006-ref.html">
+<meta name="assert" content="line-clamp should size the element to the clamped number of lines.">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ white-space: pre;
+ padding: 0 4px;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-005.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-005.tentative.html
new file mode 100644
index 00000000000..143aa65d899
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-005.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with same-BFC block children</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-009-ref.html">
+<meta name="assert" content="line-clamp should count lines in same-BFC block children">
+<style>
+.clamp {
+ line-clamp: 3;
+ font: 16px / 32px serif;
+ white-space: pre;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.child {
+ font: 24px / 48px serif;
+ color: blue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2<div class="child">Line 3
+Line 4</div></div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-006.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-006.tentative.html
new file mode 100644
index 00000000000..f06d94161b6
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-006.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with same-BFC block children</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-010-ref.html">
+<meta name="assert" content="line-clamp should count lines in same-BFC block children">
+<style>
+.clamp {
+ line-clamp: 5;
+ font: 16px / 32px serif;
+ white-space: pre;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.child {
+ font: 24px / 48px serif;
+ color: blue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2<div class="child">Line 3
+Line 4</div>Line 5
+Line 6</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-007.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-007.tentative.html
new file mode 100644
index 00000000000..c71068641b8
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-007.tentative.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with independent BFC children</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-011-ref.html">
+<meta name="assert" content="line-clamp should skip lines in children with independent BFCs">
+<style>
+.clamp {
+ line-clamp: 3;
+ font: 16px / 32px serif;
+ white-space: pre;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.child {
+ overflow: auto;
+ font: 24px / 48px serif;
+ color: blue;
+ padding: 0 4px;
+}
+</style>
+<div class="clamp">Line 1
+Line 2<div class="child">Line 3
+Line 4</div>Line 5
+Line 6</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-008.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-008.tentative.html
new file mode 100644
index 00000000000..0d91b3612d0
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-008.tentative.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp hides lines and in-flow boxes after the clamp point</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="line-clamp should hide lines and in-flow boxes after the clamp point">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.pre {
+ white-space: pre;
+}
+.red {
+ background-color: red;
+}
+</style>
+<div class="clamp">
+<div class="pre">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
+
+<div class="red">Test</div>
+
+<table class="red">
+ <tr>
+ <td>A</td>
+ <td>B</td>
+ </tr>
+ <tr>
+ <td>C</td>
+ <td>D</td>
+ </tr>
+</table>
+
+</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-009.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-009.tentative.html
new file mode 100644
index 00000000000..4dfd3d6194a
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-009.tentative.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: sizing of line-clamp affected elements with clamped block boxes</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-006-ref.html">
+<meta name="assert" content="line-clamp should size the element to the clamped number of lines, regardless of whether there are hidden block boxes after the clamp point">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.pre {
+ white-space: pre;
+}
+.red {
+ background-color: red;
+}
+</style>
+<div class="clamp">
+<div class="pre">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
+
+<div class="red">Test</div>
+
+<table class="red">
+ <tr>
+ <td>A</td>
+ <td>B</td>
+ </tr>
+ <tr>
+ <td>C</td>
+ <td>D</td>
+ </tr>
+</table>
+
+</div>
+
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-010.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-010.tentative.html
new file mode 100644
index 00000000000..1386b147ce6
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-010.tentative.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: when clamping by lines, lines after clamp are hidden even when they don't overflow</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-010-ref.html">
+<meta name="assert" content="When line-clamp is used with a number of lines, it should hide all lines after clamp, regardless of whether the box's height is set so they don't overflow">
+<style>
+.clamp {
+ line-clamp: 2;
+ font: 16px / 32px serif;
+ height: 4lh;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-011.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-011.tentative.html
new file mode 100644
index 00000000000..953f0c4faa9
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-011.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: when clamping by lines, lines before clamp are not hidden even when they overflow</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-011-ref.html">
+<meta name="assert" content="When line-clamp is used with a number of lines, it should not hide any lines before clamp, regardless of whether the box's height is set so they overflow">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ height: 3lh;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-012.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-012.tentative.html
new file mode 100644
index 00000000000..be390740377
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-012.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: when clamping by lines, borders and padding are respected</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-012-ref.html">
+<meta name="assert" content="when line-clamp is used with a number of lines, the box and its clamped children should be sized respecting borders and padding">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 4px;
+ white-space: pre;
+ background-color: yellow;
+ border: 2px solid black;
+}
+.inner {
+ background-color: skyblue;
+ padding: 4px;
+ border: 2px solid purple;
+}
+</style>
+<div class="clamp">Line 1
+Line 2<div class="inner">Line 3
+Line 4
+Line 5
+Line 6</div></div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-013.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-013.tentative.html
new file mode 100644
index 00000000000..1bda501f028
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-013.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: when clamping by lines, clamped block descendent heights are respected</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-013-ref.html">
+<meta name="assert" content="when line-clamp is used with a number of lines, if the clamp happens inside a block descendent with a set height, that height will be respected">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 4px;
+ white-space: pre;
+ background-color: yellow;
+ border: 2px solid black;
+}
+.inner {
+ background-color: skyblue;
+ height: 3lh;
+ padding: 4px;
+ border: 2px solid purple;
+}
+</style>
+<div class="clamp">Line 1
+Line 2<div class="inner">Line 3
+Line 4
+Line 5
+Line 6</div></div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-014.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-014.tentative.html
new file mode 100644
index 00000000000..9ca7c893724
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-014.tentative.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp doesn't apply to inline boxes</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-001-ref.html">
+<meta name="assert" content="line-clamp only affects block containers, not inline boxes">
+<style>
+.block {
+ font: 16px / 32px serif;
+ background-color: yellow;
+}
+.clamp {
+ line-clamp: 4;
+ white-space: pre;
+}
+</style>
+<div class="block"><span class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</span></div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-015.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-015.tentative.html
new file mode 100644
index 00000000000..82030073223
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-015.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp applies to inline blocks</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-015-ref.html">
+<meta name="assert" content="line-clamp affects block containers, which includes inline blocks">
+<style>
+.clamp {
+ display: inline-block;
+ line-clamp: 3;
+ font: 16px / 32px monospace;
+ white-space: pre;
+ padding: 0 4px;
+ width: 7.01ch;
+ background-color: yellow;
+}
+</style>
+Before <div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div> After</div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-016.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-016.tentative.html
new file mode 100644
index 00000000000..09714c499de
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-016.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with -webkit-box and -webkit-box-orient</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="If display: -webkit-box and -webkit-box-orient: vertical are present on the same box as line-clamp, it becomes a block container, and so line-clamp applies to that box.">
+<style>
+.clamp {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ white-space: pre;
+ padding: 0 4px;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-017.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-017.tentative.html
new file mode 100644
index 00000000000..11d6ceeb556
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-017.tentative.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with -webkit-box and -webkit-box-orient</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="If display: -webkit-box and -webkit-box-orient: vertical are present on the same box as line-clamp, it becomes a block container, and other flexbox properties don't apply.">
+<style>
+.clamp {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ white-space: pre;
+ padding: 0 4px;
+ background-color: yellow;
+
+ /* These properties horizontally center the child, if this box is either a
+ * -webkit-box or a regular flexbox. */
+ -webkit-box-align: center;
+ align-items: center;
+}
+</style>
+<div class="clamp"><div>Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div></div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-018.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-018.tentative.html
new file mode 100644
index 00000000000..af75f7dfb0d
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-018.tentative.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp with -webkit-box and -webkit-box-orient</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-001-ref.html">
+<meta name="assert" content="If display: -webkit-box and -webkit-box-orient: vertical are present on the same box as line-clamp, it becomes a block container, and other flexbox properties don't apply. This happens even if there is no clamping.">
+<style>
+.clamp {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ line-clamp: 6;
+ font: 16px / 32px serif;
+ white-space: pre;
+ background-color: yellow;
+
+ /* These properties horizontally center the child, if this box is either a
+ * -webkit-box or a regular flexbox. */
+ -webkit-box-align: center;
+ align-items: center;
+}
+</style>
+<div class="clamp"><div>Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div></div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-019.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-019.tentative.html
new file mode 100644
index 00000000000..b39376d3950
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-019.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp takes priority over -webkit-line-clamp</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="If both line-clamp and -webkit-line-clamp are present, line-clamp takes priority">
+<style>
+.clamp {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ white-space: pre;
+ padding: 0 4px;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-020.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-020.tentative.html
new file mode 100644
index 00000000000..9d8a2b4d06c
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-020.tentative.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp: none has no effect</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-001-ref.html">
+<meta name="assert" content="line-clamp: none should have no effect.">
+<style>
+.clamp {
+ line-clamp: none;
+ font: 16px / 32px serif;
+ white-space: pre;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-001.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-001.tentative.html
new file mode 100644
index 00000000000..79667f23fbd
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-001.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos at the start of a line-clamp</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-with-abspos-001-ref.html">
+<meta name="assert" content="Absolute positioned boxes in an inline formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point.">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp"><div class="abspos"></div>Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-002.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-002.tentative.html
new file mode 100644
index 00000000000..cecb9d52bc6
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-002.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos at the start of a line-clamp</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-with-abspos-001-ref.html">
+<meta name="assert" content="Absolute positioned boxes in a block formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point.">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+.pre {
+ white-space: pre;
+}
+</style>
+<div class="clamp">
+<div class="abspos"></div>
+<div class="pre">Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
+</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-003.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-003.tentative.html
new file mode 100644
index 00000000000..e4bd1de222e
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-003.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos in line-clamp after clamp point</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="Absolute positioned boxes in an inline formatting context inside a line-clamp container are always hidden if they are in the box tree after the clamp point.">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+<div class="abspos"></div>Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-004.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-004.tentative.html
new file mode 100644
index 00000000000..483e6d1da6d
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-004.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos in line-clamp after clamp point</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="Absolute positioned boxes in a block formatting context inside a line-clamp container are always hidden if they are in the box tree after the clamp point.">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+.pre {
+ white-space: pre;
+}
+</style>
+<div class="clamp">
+<div class="pre">Line 1
+Line 2
+Line 3
+Line 4</div>
+<div class="abspos"></div>
+<div>Line 5</div>
+</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-005.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-005.tentative.html
new file mode 100644
index 00000000000..3dc77831a06
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-005.tentative.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos in line-clamp before clamp point which overflows</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-with-abspos-005-ref.html">
+<meta name="assert" content="Absolute positioned boxes in an inline formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point, even if they visually extend beyond that point">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4<div class="abspos"></div>
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-006.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-006.tentative.html
new file mode 100644
index 00000000000..f18fed6c2da
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-006.tentative.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos in line-clamp before clamp point which overflows</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-with-abspos-006-ref.html">
+<meta name="assert" content="Absolute positioned boxes in a block formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point, even if they visually extend beyond that point">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 75px;
+ margin: 4px;
+ background-color: skyblue;
+}
+.pre {
+ white-space: pre;
+}
+</style>
+<div class="clamp">
+<div class="pre">Line 1
+Line 2
+Line 3</div>
+<div class="abspos"></div>
+<div class="pre">Line 4
+Line 5</div>
+</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-007.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-007.tentative.html
new file mode 100644
index 00000000000..f0a1f58c8d3
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-007.tentative.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos in line-clamp before clamp point positioned after it</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-with-abspos-007-ref.html">
+<meta name="assert" content="Absolute positioned boxes inside a line-clamp container are not hidden if they are in the box tree before the clamp point, even if they are positioned after that point">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ top: 148px;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp"><div class="abspos"></div>Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-008.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-008.tentative.html
new file mode 100644
index 00000000000..9c62e44f389
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-008.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: bottom: 0 abspos in line-clamp before clamp point</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-with-abspos-008-ref.html">
+<meta name="assert" content="Absolute positioned boxes inside a line-clamp container are not hidden if they are in the box tree before the clamp point.">
+<style>
+.clamp {
+ line-clamp: 4;
+ position: relative;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp"><div class="abspos"></div>Line 1
+Line 2
+Line 3
+Line 4
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-009.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-009.tentative.html
new file mode 100644
index 00000000000..dce04d720cf
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-009.tentative.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: abspos in line-clamp after clamp point positioned before it</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/webkit-line-clamp-005-ref.html">
+<meta name="assert" content="Absolute positioned boxes inside a line-clamp container are hidden if they are in the box tree after the clamp point, even if they are positioned before that point">
+<style>
+.clamp {
+ line-clamp: 4;
+ position: relative;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4
+Line 5<div class="abspos"></div></div>
diff --git a/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-010.tentative.html b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-010.tentative.html
new file mode 100644
index 00000000000..325278b3a0b
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/line-clamp-with-abspos-010.tentative.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Overflow: line-clamp doesn't propagate to abspos</title>
+<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp">
+<link rel="match" href="reference/line-clamp-with-abspos-010-ref.html">
+<meta name="assert" content="Absolute positioned boxes create a new BFC, and line-clamp does not propagate into independent BFCs">
+<style>
+.clamp {
+ line-clamp: 4;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ margin: 4px;
+ white-space: pre;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4<div class="abspos">Line A
+Line B
+Line C
+Line D
+Line E</div>
+Line 5</div>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-010-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-010-ref.html
new file mode 100644
index 00000000000..46ca731c544
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-010-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ white-space: pre;
+ height: 4lh;
+ padding: 0 4px;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2…</div>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-011-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-011-ref.html
new file mode 100644
index 00000000000..04297fff2b5
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-011-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ white-space: pre;
+ height: 3lh;
+ padding: 0 4px;
+ background-color: yellow;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4…</div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-012-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-012-ref.html
new file mode 100644
index 00000000000..f412e0110d4
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-012-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ padding: 4px;
+ white-space: pre;
+ background-color: yellow;
+ border: 2px solid black;
+}
+.inner {
+ background-color: skyblue;
+ padding: 4px;
+ border: 2px solid purple;
+}
+</style>
+<div class="clamp">Line 1
+Line 2<div class="inner">Line 3
+Line 4…</div></div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-013-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-013-ref.html
new file mode 100644
index 00000000000..b2eb05e884d
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-013-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ padding: 4px;
+ white-space: pre;
+ background-color: yellow;
+ border: 2px solid black;
+}
+.inner {
+ background-color: skyblue;
+ height: 3lh;
+ padding: 4px;
+ border: 2px solid purple;
+}
+</style>
+<div class="clamp">Line 1
+Line 2<div class="inner">Line 3
+Line 4…</div></div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-015-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-015-ref.html
new file mode 100644
index 00000000000..1af45c1225c
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-015-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ display: inline-block;
+ font: 16px / 32px monospace;
+ white-space: pre;
+ padding: 0 4px;
+ width: 7.01ch;
+ background-color: yellow;
+}
+</style>
+Before <div class="clamp">Line 1
+Line 2
+Line 3…</div> After</div>
+<p>Following content.</p>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-001-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-001-ref.html
new file mode 100644
index 00000000000..d756162dde0
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-001-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp"><div class="abspos"></div>Line 1
+Line 2
+Line 3
+Line 4…</div>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-005-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-005-ref.html
new file mode 100644
index 00000000000..3b1f9218e88
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-005-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4…<div class="abspos"></div></div>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-006-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-006-ref.html
new file mode 100644
index 00000000000..4b55c37a033
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-006-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ width: 50px;
+ height: 75px;
+ margin: 4px;
+ background-color: skyblue;
+}
+.pre {
+ white-space: pre;
+}
+</style>
+<div class="clamp">
+<div class="pre">Line 1
+Line 2
+Line 3</div>
+<div class="abspos"></div>
+<div class="pre">Line 4…</div>
+</div>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-007-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-007-ref.html
new file mode 100644
index 00000000000..e3dcc696e39
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-007-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ top: 148px;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp"><div class="abspos"></div>Line 1
+Line 2
+Line 3
+Line 4…</div>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-008-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-008-ref.html
new file mode 100644
index 00000000000..373b2755c1d
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-008-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ position: relative;
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ width: 50px;
+ height: 50px;
+ margin: 4px;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp"><div class="abspos"></div>Line 1
+Line 2
+Line 3
+Line 4…</div>
diff --git a/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-010-ref.html b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-010-ref.html
new file mode 100644
index 00000000000..ecc2fcee1b5
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/reference/line-clamp-with-abspos-010-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Reference</title>
+<style>
+.clamp {
+ font: 16px / 32px serif;
+ padding: 0 4px;
+ white-space: pre;
+ background-color: yellow;
+}
+.abspos {
+ position: absolute;
+ right: 0;
+ margin: 4px;
+ white-space: pre;
+ background-color: skyblue;
+}
+</style>
+<div class="clamp">Line 1
+Line 2
+Line 3
+Line 4…<div class="abspos">Line A
+Line B
+Line C
+Line D
+Line E</div></div>
diff --git a/tests/wpt/tests/css/printing/crashtests/root-element-remove-print.html b/tests/wpt/tests/css/css-page/crashtests/root-element-remove-print.html
index 8497e8c4fd7..8497e8c4fd7 100644
--- a/tests/wpt/tests/css/printing/crashtests/root-element-remove-print.html
+++ b/tests/wpt/tests/css/css-page/crashtests/root-element-remove-print.html
diff --git a/tests/wpt/tests/css/printing/crashtests/tall-inline-block-in-float-in-table-cell-print.html b/tests/wpt/tests/css/css-page/crashtests/tall-inline-block-in-float-in-table-cell-print.html
index c70dce2160a..c70dce2160a 100644
--- a/tests/wpt/tests/css/printing/crashtests/tall-inline-block-in-float-in-table-cell-print.html
+++ b/tests/wpt/tests/css/css-page/crashtests/tall-inline-block-in-float-in-table-cell-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-001-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-001-print-ref.html
index 3d66305db0e..3d66305db0e 100644
--- a/tests/wpt/tests/css/printing/fixedpos-001-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-001-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-001-print.html b/tests/wpt/tests/css/css-page/fixedpos-001-print.html
index 04feb96e847..04feb96e847 100644
--- a/tests/wpt/tests/css/printing/fixedpos-001-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-001-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-002-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-002-print-ref.html
index 3d66305db0e..3d66305db0e 100644
--- a/tests/wpt/tests/css/printing/fixedpos-002-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-002-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-002-print.html b/tests/wpt/tests/css/css-page/fixedpos-002-print.html
index c23c6be7d2a..c23c6be7d2a 100644
--- a/tests/wpt/tests/css/printing/fixedpos-002-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-002-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-003-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-003-print-ref.html
index 3d66305db0e..3d66305db0e 100644
--- a/tests/wpt/tests/css/printing/fixedpos-003-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-003-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-003-print.html b/tests/wpt/tests/css/css-page/fixedpos-003-print.html
index 1b06257175f..1b06257175f 100644
--- a/tests/wpt/tests/css/printing/fixedpos-003-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-003-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-004-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-004-print-ref.html
index 3e3473bcb8e..3e3473bcb8e 100644
--- a/tests/wpt/tests/css/printing/fixedpos-004-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-004-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-004-print.html b/tests/wpt/tests/css/css-page/fixedpos-004-print.html
index c138e9cd6aa..c138e9cd6aa 100644
--- a/tests/wpt/tests/css/printing/fixedpos-004-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-004-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-005-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-005-print-ref.html
index e692ff8db37..e692ff8db37 100644
--- a/tests/wpt/tests/css/printing/fixedpos-005-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-005-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-005-print.html b/tests/wpt/tests/css/css-page/fixedpos-005-print.html
index 0a2edc71788..0a2edc71788 100644
--- a/tests/wpt/tests/css/printing/fixedpos-005-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-005-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-006-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-006-print-ref.html
index b03e1d78d00..b03e1d78d00 100644
--- a/tests/wpt/tests/css/printing/fixedpos-006-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-006-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-006-print.html b/tests/wpt/tests/css/css-page/fixedpos-006-print.html
index 2386c166c81..2386c166c81 100644
--- a/tests/wpt/tests/css/printing/fixedpos-006-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-006-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-007-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-007-print-ref.html
index f576c937715..f576c937715 100644
--- a/tests/wpt/tests/css/printing/fixedpos-007-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-007-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-007-print.html b/tests/wpt/tests/css/css-page/fixedpos-007-print.html
index 8dcb700b96d..8dcb700b96d 100644
--- a/tests/wpt/tests/css/printing/fixedpos-007-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-007-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-008-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-008-print-ref.html
index 6ed2528115a..6ed2528115a 100644
--- a/tests/wpt/tests/css/printing/fixedpos-008-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-008-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-008-print.html b/tests/wpt/tests/css/css-page/fixedpos-008-print.html
index 02b5d63cc7c..02b5d63cc7c 100644
--- a/tests/wpt/tests/css/printing/fixedpos-008-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-008-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-with-abspos-with-link-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-with-abspos-with-link-print-ref.html
index 000c05350b5..000c05350b5 100644
--- a/tests/wpt/tests/css/printing/fixedpos-with-abspos-with-link-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-with-abspos-with-link-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-with-abspos-with-link-print.html b/tests/wpt/tests/css/css-page/fixedpos-with-abspos-with-link-print.html
index 057ddc91460..057ddc91460 100644
--- a/tests/wpt/tests/css/printing/fixedpos-with-abspos-with-link-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-with-abspos-with-link-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-with-iframe-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-with-iframe-print-ref.html
index 5c171404508..5c171404508 100644
--- a/tests/wpt/tests/css/printing/fixedpos-with-iframe-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-with-iframe-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-with-iframe-print.html b/tests/wpt/tests/css/css-page/fixedpos-with-iframe-print.html
index 5102d045c42..5102d045c42 100644
--- a/tests/wpt/tests/css/printing/fixedpos-with-iframe-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-with-iframe-print.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-with-link-with-inline-child-print-ref.html b/tests/wpt/tests/css/css-page/fixedpos-with-link-with-inline-child-print-ref.html
index f12d31acd9d..f12d31acd9d 100644
--- a/tests/wpt/tests/css/printing/fixedpos-with-link-with-inline-child-print-ref.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-with-link-with-inline-child-print-ref.html
diff --git a/tests/wpt/tests/css/printing/fixedpos-with-link-with-inline-child-print.html b/tests/wpt/tests/css/css-page/fixedpos-with-link-with-inline-child-print.html
index 694e5376cbf..694e5376cbf 100644
--- a/tests/wpt/tests/css/printing/fixedpos-with-link-with-inline-child-print.html
+++ b/tests/wpt/tests/css/css-page/fixedpos-with-link-with-inline-child-print.html
diff --git a/tests/wpt/tests/css/printing/media-queries-001-print-ref.html b/tests/wpt/tests/css/css-page/media-queries-001-print-ref.html
index 35af04dc3c4..35af04dc3c4 100644
--- a/tests/wpt/tests/css/printing/media-queries-001-print-ref.html
+++ b/tests/wpt/tests/css/css-page/media-queries-001-print-ref.html
diff --git a/tests/wpt/tests/css/printing/media-queries-001-print.html b/tests/wpt/tests/css/css-page/media-queries-001-print.html
index 01b2a00e470..01b2a00e470 100644
--- a/tests/wpt/tests/css/printing/media-queries-001-print.html
+++ b/tests/wpt/tests/css/css-page/media-queries-001-print.html
diff --git a/tests/wpt/tests/css/printing/media-queries-002-print.html b/tests/wpt/tests/css/css-page/media-queries-002-print.html
index 5f71f3d17a7..5f71f3d17a7 100644
--- a/tests/wpt/tests/css/printing/media-queries-002-print.html
+++ b/tests/wpt/tests/css/css-page/media-queries-002-print.html
diff --git a/tests/wpt/tests/css/printing/media-queries-003-print-ref.html b/tests/wpt/tests/css/css-page/media-queries-003-print-ref.html
index 99642ca5674..99642ca5674 100644
--- a/tests/wpt/tests/css/printing/media-queries-003-print-ref.html
+++ b/tests/wpt/tests/css/css-page/media-queries-003-print-ref.html
diff --git a/tests/wpt/tests/css/printing/media-queries-003-print.html b/tests/wpt/tests/css/css-page/media-queries-003-print.html
index 2c125296ad2..2c125296ad2 100644
--- a/tests/wpt/tests/css/printing/media-queries-003-print.html
+++ b/tests/wpt/tests/css/css-page/media-queries-003-print.html
diff --git a/tests/wpt/tests/css/printing/page-margin-001-print-ref.html b/tests/wpt/tests/css/css-page/page-margin-001-print-ref.html
index 12ebd76dc91..12ebd76dc91 100644
--- a/tests/wpt/tests/css/printing/page-margin-001-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-margin-001-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-margin-001-print.html b/tests/wpt/tests/css/css-page/page-margin-001-print.html
index c59b3c64276..c59b3c64276 100644
--- a/tests/wpt/tests/css/printing/page-margin-001-print.html
+++ b/tests/wpt/tests/css/css-page/page-margin-001-print.html
diff --git a/tests/wpt/tests/css/printing/page-margin-002-print-ref.html b/tests/wpt/tests/css/css-page/page-margin-002-print-ref.html
index 2b565048558..2b565048558 100644
--- a/tests/wpt/tests/css/printing/page-margin-002-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-margin-002-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-margin-002-print.html b/tests/wpt/tests/css/css-page/page-margin-002-print.html
index 944d7efcfd0..944d7efcfd0 100644
--- a/tests/wpt/tests/css/printing/page-margin-002-print.html
+++ b/tests/wpt/tests/css/css-page/page-margin-002-print.html
diff --git a/tests/wpt/tests/css/printing/page-margin-003-print-ref.html b/tests/wpt/tests/css/css-page/page-margin-003-print-ref.html
index 52bf7360908..52bf7360908 100644
--- a/tests/wpt/tests/css/printing/page-margin-003-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-margin-003-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-margin-003-print.html b/tests/wpt/tests/css/css-page/page-margin-003-print.html
index e7410c02e09..e7410c02e09 100644
--- a/tests/wpt/tests/css/printing/page-margin-003-print.html
+++ b/tests/wpt/tests/css/css-page/page-margin-003-print.html
diff --git a/tests/wpt/tests/css/printing/page-margin-004-print-ref.html b/tests/wpt/tests/css/css-page/page-margin-004-print-ref.html
index 24549d49542..24549d49542 100644
--- a/tests/wpt/tests/css/printing/page-margin-004-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-margin-004-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-margin-004-print.html b/tests/wpt/tests/css/css-page/page-margin-004-print.html
index cb9343dcf17..cb9343dcf17 100644
--- a/tests/wpt/tests/css/printing/page-margin-004-print.html
+++ b/tests/wpt/tests/css/css-page/page-margin-004-print.html
diff --git a/tests/wpt/tests/css/printing/page-margin-005-print-ref.html b/tests/wpt/tests/css/css-page/page-margin-005-print-ref.html
index bd1829be383..bd1829be383 100644
--- a/tests/wpt/tests/css/printing/page-margin-005-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-margin-005-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-margin-005-print.html b/tests/wpt/tests/css/css-page/page-margin-005-print.html
index 8ca80bc6972..8ca80bc6972 100644
--- a/tests/wpt/tests/css/printing/page-margin-005-print.html
+++ b/tests/wpt/tests/css/css-page/page-margin-005-print.html
diff --git a/tests/wpt/tests/css/printing/page-margin-006-print-ref.html b/tests/wpt/tests/css/css-page/page-margin-006-print-ref.html
index fe7a2c66db2..fe7a2c66db2 100644
--- a/tests/wpt/tests/css/printing/page-margin-006-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-margin-006-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-margin-006-print.html b/tests/wpt/tests/css/css-page/page-margin-006-print.html
index 43621acf809..43621acf809 100644
--- a/tests/wpt/tests/css/printing/page-margin-006-print.html
+++ b/tests/wpt/tests/css/css-page/page-margin-006-print.html
diff --git a/tests/wpt/tests/css/printing/page-name-001-print-ref.html b/tests/wpt/tests/css/css-page/page-name-000-print-ref.html
index 69c5c37b5a3..69c5c37b5a3 100644
--- a/tests/wpt/tests/css/printing/page-name-001-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-name-000-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-name-001-print.html b/tests/wpt/tests/css/css-page/page-name-000-print.html
index 56281bb4ed5..f01d5cac48c 100644
--- a/tests/wpt/tests/css/printing/page-name-001-print.html
+++ b/tests/wpt/tests/css/css-page/page-name-000-print.html
@@ -2,7 +2,7 @@
<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
<link rel="help" href="https://drafts.csswg.org/css-page-3/#using-named-pages">
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1414718">
-<link rel="match" href="page-name-001-print-ref.html">
+<link rel="match" href="page-name-000-print-ref.html">
<div style="page:foo;">
<div style="float:left;">First page</div>
<div style="clear:both;">Also first page</div>
diff --git a/tests/wpt/tests/css/printing/page-name-002-print-ref.html b/tests/wpt/tests/css/css-page/page-name-002-print-ref.html
index 17ecc93e57a..17ecc93e57a 100644
--- a/tests/wpt/tests/css/printing/page-name-002-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-name-002-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-name-002-print.html b/tests/wpt/tests/css/css-page/page-name-002-print.html
index 060b93b0bf9..060b93b0bf9 100644
--- a/tests/wpt/tests/css/printing/page-name-002-print.html
+++ b/tests/wpt/tests/css/css-page/page-name-002-print.html
diff --git a/tests/wpt/tests/css/printing/page-name-003-print-ref.html b/tests/wpt/tests/css/css-page/page-name-003-print-ref.html
index 52ea6bfffcb..52ea6bfffcb 100644
--- a/tests/wpt/tests/css/printing/page-name-003-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-name-003-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-name-003-print.html b/tests/wpt/tests/css/css-page/page-name-003-print.html
index 9ef4db8c754..9ef4db8c754 100644
--- a/tests/wpt/tests/css/printing/page-name-003-print.html
+++ b/tests/wpt/tests/css/css-page/page-name-003-print.html
diff --git a/tests/wpt/tests/css/printing/page-name-and-break-001-print.html b/tests/wpt/tests/css/css-page/page-name-and-break-001-print.html
index 04de5f7073b..04de5f7073b 100644
--- a/tests/wpt/tests/css/printing/page-name-and-break-001-print.html
+++ b/tests/wpt/tests/css/css-page/page-name-and-break-001-print.html
diff --git a/tests/wpt/tests/css/printing/page-name-and-break-002-print.html b/tests/wpt/tests/css/css-page/page-name-and-break-002-print.html
index 3dd755b0278..3dd755b0278 100644
--- a/tests/wpt/tests/css/printing/page-name-and-break-002-print.html
+++ b/tests/wpt/tests/css/css-page/page-name-and-break-002-print.html
diff --git a/tests/wpt/tests/css/printing/page-name-and-break-003-print.html b/tests/wpt/tests/css/css-page/page-name-and-break-003-print.html
index b7e8a20e1d0..b7e8a20e1d0 100644
--- a/tests/wpt/tests/css/printing/page-name-and-break-003-print.html
+++ b/tests/wpt/tests/css/css-page/page-name-and-break-003-print.html
diff --git a/tests/wpt/tests/css/printing/page-name-and-break-004-print.html b/tests/wpt/tests/css/css-page/page-name-and-break-004-print.html
index 2e252ea4eb5..2e252ea4eb5 100644
--- a/tests/wpt/tests/css/printing/page-name-and-break-004-print.html
+++ b/tests/wpt/tests/css/css-page/page-name-and-break-004-print.html
diff --git a/tests/wpt/tests/css/printing/page-name-and-break-print-ref.html b/tests/wpt/tests/css/css-page/page-name-and-break-print-ref.html
index e3f73e41e2e..e3f73e41e2e 100644
--- a/tests/wpt/tests/css/printing/page-name-and-break-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-name-and-break-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-001-print-ref.html b/tests/wpt/tests/css/css-page/page-size-001-print-ref.html
index b016d8949fd..b016d8949fd 100644
--- a/tests/wpt/tests/css/printing/page-size-001-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-001-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-001-print.html b/tests/wpt/tests/css/css-page/page-size-001-print.html
index 19d72b0231f..19d72b0231f 100644
--- a/tests/wpt/tests/css/printing/page-size-001-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-001-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-002-print-ref.html b/tests/wpt/tests/css/css-page/page-size-002-print-ref.html
index 78a16469436..78a16469436 100644
--- a/tests/wpt/tests/css/printing/page-size-002-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-002-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-002-print.html b/tests/wpt/tests/css/css-page/page-size-002-print.html
index 243c382ca2c..243c382ca2c 100644
--- a/tests/wpt/tests/css/printing/page-size-002-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-002-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-003-print-ref.html b/tests/wpt/tests/css/css-page/page-size-003-print-ref.html
index 38ea1508781..38ea1508781 100644
--- a/tests/wpt/tests/css/printing/page-size-003-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-003-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-003-print.html b/tests/wpt/tests/css/css-page/page-size-003-print.html
index 805ff0e568a..805ff0e568a 100644
--- a/tests/wpt/tests/css/printing/page-size-003-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-003-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-004-print-ref.html b/tests/wpt/tests/css/css-page/page-size-004-print-ref.html
index 0468a745b23..0468a745b23 100644
--- a/tests/wpt/tests/css/printing/page-size-004-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-004-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-004-print.html b/tests/wpt/tests/css/css-page/page-size-004-print.html
index 125d7636d26..125d7636d26 100644
--- a/tests/wpt/tests/css/printing/page-size-004-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-004-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-005-print-ref.html b/tests/wpt/tests/css/css-page/page-size-005-print-ref.html
index c2e07daa295..c2e07daa295 100644
--- a/tests/wpt/tests/css/printing/page-size-005-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-005-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-005-print.html b/tests/wpt/tests/css/css-page/page-size-005-print.html
index 37876ae230e..37876ae230e 100644
--- a/tests/wpt/tests/css/printing/page-size-005-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-005-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-006-print-ref.html b/tests/wpt/tests/css/css-page/page-size-006-print-ref.html
index 7167da2ac26..7167da2ac26 100644
--- a/tests/wpt/tests/css/printing/page-size-006-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-006-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-006-print.html b/tests/wpt/tests/css/css-page/page-size-006-print.html
index 1da29d334d7..1da29d334d7 100644
--- a/tests/wpt/tests/css/printing/page-size-006-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-006-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-007-print-ref.html b/tests/wpt/tests/css/css-page/page-size-007-print-ref.html
index 9498e2c17b2..9498e2c17b2 100644
--- a/tests/wpt/tests/css/printing/page-size-007-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-007-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-007-print.html b/tests/wpt/tests/css/css-page/page-size-007-print.html
index 50ab2f4d109..50ab2f4d109 100644
--- a/tests/wpt/tests/css/printing/page-size-007-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-007-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-008-print-ref.html b/tests/wpt/tests/css/css-page/page-size-008-print-ref.html
index f50ad3531e4..f50ad3531e4 100644
--- a/tests/wpt/tests/css/printing/page-size-008-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-008-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-008-print.html b/tests/wpt/tests/css/css-page/page-size-008-print.html
index 683bdc3a50a..683bdc3a50a 100644
--- a/tests/wpt/tests/css/printing/page-size-008-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-008-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-009-print-ref.html b/tests/wpt/tests/css/css-page/page-size-009-print-ref.html
index 86ffa278c93..86ffa278c93 100644
--- a/tests/wpt/tests/css/printing/page-size-009-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-009-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-009-print.html b/tests/wpt/tests/css/css-page/page-size-009-print.html
index 48ead4eab38..48ead4eab38 100644
--- a/tests/wpt/tests/css/printing/page-size-009-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-009-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-010-print-ref.html b/tests/wpt/tests/css/css-page/page-size-010-print-ref.html
index 27cbe4435c2..27cbe4435c2 100644
--- a/tests/wpt/tests/css/printing/page-size-010-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-010-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-010-print.html b/tests/wpt/tests/css/css-page/page-size-010-print.html
index 21205e06a03..21205e06a03 100644
--- a/tests/wpt/tests/css/printing/page-size-010-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-010-print.html
diff --git a/tests/wpt/tests/css/printing/page-size-011-print-ref.html b/tests/wpt/tests/css/css-page/page-size-011-print-ref.html
index 945f105ef47..945f105ef47 100644
--- a/tests/wpt/tests/css/printing/page-size-011-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-size-011-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-size-011-print.html b/tests/wpt/tests/css/css-page/page-size-011-print.html
index 27476058066..27476058066 100644
--- a/tests/wpt/tests/css/printing/page-size-011-print.html
+++ b/tests/wpt/tests/css/css-page/page-size-011-print.html
diff --git a/tests/wpt/tests/css/printing/page-visibility-hidden-001-print-ref.html b/tests/wpt/tests/css/css-page/page-visibility-hidden-001-print-ref.html
index 24d3806eb99..24d3806eb99 100644
--- a/tests/wpt/tests/css/printing/page-visibility-hidden-001-print-ref.html
+++ b/tests/wpt/tests/css/css-page/page-visibility-hidden-001-print-ref.html
diff --git a/tests/wpt/tests/css/printing/page-visibility-hidden-001-print.html b/tests/wpt/tests/css/css-page/page-visibility-hidden-001-print.html
index 0de5c5ae825..0de5c5ae825 100644
--- a/tests/wpt/tests/css/printing/page-visibility-hidden-001-print.html
+++ b/tests/wpt/tests/css/css-page/page-visibility-hidden-001-print.html
diff --git a/tests/wpt/tests/css/printing/reference/blank-print-ref.html b/tests/wpt/tests/css/css-page/reference/blank-print-ref.html
index ad73420cdb9..ad73420cdb9 100644
--- a/tests/wpt/tests/css/printing/reference/blank-print-ref.html
+++ b/tests/wpt/tests/css/css-page/reference/blank-print-ref.html
diff --git a/tests/wpt/tests/css/printing/reference/filled-green-100px-square-print-ref.html b/tests/wpt/tests/css/css-page/reference/filled-green-100px-square-print-ref.html
index d4834d1358a..d4834d1358a 100644
--- a/tests/wpt/tests/css/printing/reference/filled-green-100px-square-print-ref.html
+++ b/tests/wpt/tests/css/css-page/reference/filled-green-100px-square-print-ref.html
diff --git a/tests/wpt/tests/css/printing/remote-origin-iframe-print-ref.html b/tests/wpt/tests/css/css-page/remote-origin-iframe-print-ref.html
index f632519cd6e..f632519cd6e 100644
--- a/tests/wpt/tests/css/printing/remote-origin-iframe-print-ref.html
+++ b/tests/wpt/tests/css/css-page/remote-origin-iframe-print-ref.html
diff --git a/tests/wpt/tests/css/printing/remote-origin-iframe-print.html b/tests/wpt/tests/css/css-page/remote-origin-iframe-print.html
index aba10a2a399..73b3878386f 100644
--- a/tests/wpt/tests/css/printing/remote-origin-iframe-print.html
+++ b/tests/wpt/tests/css/css-page/remote-origin-iframe-print.html
@@ -14,6 +14,6 @@
<p>The word "PASS" should be seen below.</p>
<script>
const iframe = document.createElement("iframe");
- iframe.src = get_host_info().REMOTE_ORIGIN + "/css/printing/resources/iframe-with-abspos.html";
+ iframe.src = get_host_info().REMOTE_ORIGIN + "/css/css-page/resources/iframe-with-abspos.html";
document.body.appendChild(iframe);
</script>
diff --git a/tests/wpt/tests/css/printing/resources/iframe-with-abspos.html b/tests/wpt/tests/css/css-page/resources/iframe-with-abspos.html
index 27de3aad5e0..27de3aad5e0 100644
--- a/tests/wpt/tests/css/printing/resources/iframe-with-abspos.html
+++ b/tests/wpt/tests/css/css-page/resources/iframe-with-abspos.html
diff --git a/tests/wpt/tests/css/printing/resources/mq-frame-100px.html b/tests/wpt/tests/css/css-page/resources/mq-frame-100px.html
index ed823c09e89..ed823c09e89 100644
--- a/tests/wpt/tests/css/printing/resources/mq-frame-100px.html
+++ b/tests/wpt/tests/css/css-page/resources/mq-frame-100px.html
diff --git a/tests/wpt/tests/css/printing/root-element-display-none-print.html b/tests/wpt/tests/css/css-page/root-element-display-none-print.html
index d6802f62251..d6802f62251 100644
--- a/tests/wpt/tests/css/printing/root-element-display-none-print.html
+++ b/tests/wpt/tests/css/css-page/root-element-display-none-print.html
diff --git a/tests/wpt/tests/css/printing/subpixel-page-size-001-print-ref.html b/tests/wpt/tests/css/css-page/subpixel-page-size-001-print-ref.html
index cf59f2be374..cf59f2be374 100644
--- a/tests/wpt/tests/css/printing/subpixel-page-size-001-print-ref.html
+++ b/tests/wpt/tests/css/css-page/subpixel-page-size-001-print-ref.html
diff --git a/tests/wpt/tests/css/printing/subpixel-page-size-001-print.html b/tests/wpt/tests/css/css-page/subpixel-page-size-001-print.html
index 9054cd4513f..9054cd4513f 100644
--- a/tests/wpt/tests/css/printing/subpixel-page-size-001-print.html
+++ b/tests/wpt/tests/css/css-page/subpixel-page-size-001-print.html
diff --git a/tests/wpt/tests/css/printing/subpixel-page-size-002-print-ref.html b/tests/wpt/tests/css/css-page/subpixel-page-size-002-print-ref.html
index 4ed4bdb5ff3..4ed4bdb5ff3 100644
--- a/tests/wpt/tests/css/printing/subpixel-page-size-002-print-ref.html
+++ b/tests/wpt/tests/css/css-page/subpixel-page-size-002-print-ref.html
diff --git a/tests/wpt/tests/css/printing/subpixel-page-size-002-print.html b/tests/wpt/tests/css/css-page/subpixel-page-size-002-print.html
index 463cb21f25d..463cb21f25d 100644
--- a/tests/wpt/tests/css/printing/subpixel-page-size-002-print.html
+++ b/tests/wpt/tests/css/css-page/subpixel-page-size-002-print.html
diff --git a/tests/wpt/tests/css/css-paint-api/parse-input-arguments-018.https.html b/tests/wpt/tests/css/css-paint-api/parse-input-arguments-018.https.html
index 1554cc64451..a07bb2d6196 100644
--- a/tests/wpt/tests/css/css-paint-api/parse-input-arguments-018.https.html
+++ b/tests/wpt/tests/css/css-paint-api/parse-input-arguments-018.https.html
@@ -22,11 +22,15 @@ should never be called. In other words, there should be no red painted in the re
<div id="canvas-geometry" class="container"></div>
<script id="code" type="text/worklet">
-function generateRandString(length) {
- var text = "";
- var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
- for (var i = 0; i < length; i++)
- text += possible.charAt(Math.floor(Math.random() * possible.length));
+function generateRandomIdentifier(length) {
+ const firstChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ const nthChars = firstChars + "0123456789";
+ // Pick a letter for the first character so that the string is a valid
+ // identifier.
+ var text = firstChars.charAt(Math.floor(Math.random() * firstChars.length));
+ for (var i = 0; i < length - 1; i++) {
+ text += nthChars.charAt(Math.floor(Math.random() * nthChars.length));
+ }
return text;
}
@@ -39,7 +43,7 @@ try {
// the input properties here. We make the string length 100 to make sure
// that it is veryyyyyyyyyyyy unlikely that two strings will be the same
// when running this test.
- var current_str = generateRandString(100);
+ var current_str = generateRandomIdentifier(100);
return [current_str];
}
// The paint function here should never be called because the inputArguments
diff --git a/tests/wpt/tests/css/css-scoping/host-defined.html b/tests/wpt/tests/css/css-scoping/host-defined.html
new file mode 100644
index 00000000000..9e9776754a3
--- /dev/null
+++ b/tests/wpt/tests/css/css-scoping/host-defined.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Shadow host is considered, but does not match :defined</title>
+<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org">
+<link rel="help" href="https://crbug.com/332587538">
+<link rel="help" href="https://drafts.csswg.org/selectors/#featureless">
+<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
+<p>Test passes if there is a filled green square.</p>
+<div id="host"></div>
+<script>
+ host.attachShadow({mode: "open"}).innerHTML = `
+ <style>
+ :host div {
+ width: 100px;
+ height: 100px;
+ background-color: red;
+ }
+ :not(:defined) div {
+ background-color: green;
+ }
+ </style>
+ <div></div>
+ `;
+</script>
diff --git a/tests/wpt/tests/css/css-scroll-snap-2/resources/common.js b/tests/wpt/tests/css/css-scroll-snap-2/resources/common.js
index e29f9bc5b3e..a3591d48ed2 100644
--- a/tests/wpt/tests/css/css-scroll-snap-2/resources/common.js
+++ b/tests/wpt/tests/css/css-scroll-snap-2/resources/common.js
@@ -17,9 +17,17 @@ function assertSnapEvent(evt, expected_ids) {
"snap event supplied expected target in inline axis");
}
-async function test_snap_event(test, test_data, event_type) {
+async function snap_test_setup(test, scroller, event_type) {
checkSnapEventSupport(event_type);
- await waitForScrollReset(test, test_data.scroller);
+ await waitForScrollReset(test, scroller);
+ await waitForCompositorCommit();
+ test.add_cleanup(async () => {
+ await waitForScrollReset(test, scroller);
+ });
+}
+
+async function test_snap_event(test, test_data, event_type) {
+ await snap_test_setup(test, test_data.scroller, event_type);
let listener = test_data.scroller ==
document.scrollingElement ? document : test_data.scroller;
@@ -69,6 +77,20 @@ function waitForEventsUntil(event_target, event_type, wait_until) {
});
}
+function waitForOnSnapchanging(event_target) {
+ return new Promise(resolve => {
+ let result = null;
+ const listener = (evt) => {
+ result = evt;
+ };
+ event_target.onsnapchanging = listener;
+ waitForScrollendEventNoTimeout(event_target).then(() => {
+ event_target.onsnapchanging = null;
+ resolve(result);
+ });
+ });
+}
+
// Proxy a wait for a snap event. We want to avoid having a test
// timeout in the event of an expected snap event not firing in a particular
// test case as that would cause the entire file to fail.
diff --git a/tests/wpt/tests/css/css-scroll-snap-2/resources/programmatic-scroll-common.js b/tests/wpt/tests/css/css-scroll-snap-2/resources/programmatic-scroll-common.js
new file mode 100644
index 00000000000..8257b98fe33
--- /dev/null
+++ b/tests/wpt/tests/css/css-scroll-snap-2/resources/programmatic-scroll-common.js
@@ -0,0 +1,26 @@
+// Helper functions for snapchanged-on-programmatic-* tests.
+
+// Utility function to test that onsnapchanging is triggered for
+// snapchanging-on-programmatic-* tests which set up a similar layout in which
+// the |scroller| has 3 snap targets that form a vertical column along
+// |scroller|'s middle. onsnapchanging should be triggered by conducting a
+// programmatic scroll to the top of snap_target.
+async function test_programmatic_scroll_onsnapchanging(test,
+ scroller,
+ event_target,
+ snap_target) {
+ await snap_test_setup(test, scroller, "snapchanging");
+ const expected_snap_targets = { block: snap_target, inline: null };
+
+ // Scroll and wait for a snapchanging event.
+ const snapchanging_promise = waitForOnSnapchanging(event_target);
+ scroller.scrollTo(0, snap_target.offsetTop);
+ const snapchanging_event = await snapchanging_promise;
+
+ // Assert that snapchanging fired and indicated that snap_target would
+ // be snapped to.
+ assertSnapEvent(snapchanging_event, expected_snap_targets);
+ assert_equals(scroller.scrollLeft, 0, "scrollLeft is zero");
+ assert_equals(scroller.scrollTop, snap_target.offsetTop,
+ "snapped to snap_target");
+}
diff --git a/tests/wpt/tests/css/css-scroll-snap-2/resources/user-scroll-common.js b/tests/wpt/tests/css/css-scroll-snap-2/resources/user-scroll-common.js
index 6587aebd920..820f1438160 100644
--- a/tests/wpt/tests/css/css-scroll-snap-2/resources/user-scroll-common.js
+++ b/tests/wpt/tests/css/css-scroll-snap-2/resources/user-scroll-common.js
@@ -68,4 +68,33 @@ async function test_no_snapchanged(t, scroller, delta) {
async function test_no_snapchanging(t, scroller, delta) {
await test_no_snap_event(t, scroller, delta, "snapchanging");
-} \ No newline at end of file
+}
+
+// Utility function to test that onsnapchanging is triggered for
+// snapchanging-on-user-* tests which set up a similar layout in which
+// the |scroller| has 3 snap targets that form a vertical column along
+// |scroller|'s middle. onsnapchanging should be triggered by touch-dragging
+// |scroller|'s content so that |snap_target|'s top aligns to |snap_target|.
+async function test_user_scroll_onsnapchanging(test, scroller, event_target,
+ snap_target) {
+ await snap_test_setup(test, scroller, "snapchanging");
+
+ // Compute touch positions to drag the top of snap_target to the top of
+ // the scroller.
+ const scroller_middle = Math.round(scroller.clientWidth / 2);
+ const start_pos = { x: scroller_middle, y: snap_target.offsetTop };
+ const end_pos = { x: scroller_middle, y: 0 };
+ const expected_snap_targets = { block: snap_target, inline: null };
+
+ // Scroll and wait for a snapchanging event.
+ const snapchanging_promise = waitForOnSnapchanging(event_target);
+ await snap_event_touch_scroll_helper(start_pos, end_pos);
+ const snapchanging_event = await snapchanging_promise;
+
+ // Assert that snapchanging fired and indicated that snap_target would
+ // be snapped to.
+ assertSnapEvent(snapchanging_event, expected_snap_targets);
+ assert_equals(scroller.scrollLeft, 0, "scrollLeft is zero");
+ assert_equals(scroller.scrollTop, snap_target.offsetTop,
+ "snapped to snap_target");
+}
diff --git a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html
index 5d75715edb7..b714a6cfb51 100644
--- a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html
+++ b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html
@@ -9,6 +9,7 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="/dom/events/scrolling/scroll_support.js"></script>
<script src="/css/css-scroll-snap-2/resources/common.js"></script>
+ <script src="/css/css-scroll-snap-2/resources/programmatic-scroll-common.js"></script>
ß <script src="/web-animations/testcommon.js"></script>
</head>
@@ -86,6 +87,12 @@
" snap targets.");
promise_test(async (t) => {
+ await test_programmatic_scroll_onsnapchanging(t, scroller, document,
+ snap_area_2);
+ }, "programmatic scroll triggers Document.snapchanging when scrolling a " +
+ "snap container");
+
+ promise_test(async (t) => {
checkSnapEventSupport("snapchanging");
await waitForScrollReset(t, scroller);
await waitForCompositorCommit();
diff --git a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html
index 25cdc44c662..6e7b0126f72 100644
--- a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html
+++ b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html
@@ -9,6 +9,7 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="/dom/events/scrolling/scroll_support.js"></script>
<script src="/css/css-scroll-snap-2/resources/common.js"></script>
+ <script src="/css/css-scroll-snap-2/resources/programmatic-scroll-common.js"></script>
<script src="/web-animations/testcommon.js"></script>
</head>
@@ -93,6 +94,12 @@
" snap targets.");
promise_test(async (t) => {
+ await test_programmatic_scroll_onsnapchanging(t, scroller, scroller,
+ snap_area_2);
+ }, "programmatic scroll triggers Element.onsnapchanging when scrolling a " +
+ "snap container");
+
+ promise_test(async (t) => {
checkSnapEventSupport("snapchanging");
await waitForScrollReset(t, scroller);
await waitForCompositorCommit();
@@ -107,7 +114,7 @@
assert_equals(evt, null, "no snap event since scroller is back to top");
assert_equals(scroller.scrollTop, 0, "scroller snaps back to the top");
assert_equals(scroller.scrollLeft, 0, "scroller snaps back to the left");
- });
+ }, "snapchanging should not fire since the snap target doesn't change.");
</script>
</body>
diff --git a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html
index e1a1e8aff6c..815c3c0922a 100644
--- a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html
+++ b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html
@@ -159,6 +159,11 @@
await test_snap_event(t, test_data, "snapchanging");
}, "keyboard scroll triggers snapchanging.");
+ promise_test(async (t) => {
+ await test_user_scroll_onsnapchanging(t, scroller, document,
+ snap_area_2);
+ }, "Document.onsnapchanging fires when scrolling a snap container.");
+
// Touch scroll test: peek at snap_area_2 and then drag back to
// snap_area_1.
promise_test(async (t) => {
diff --git a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html
index 35ee8a51965..27f52efc71d 100644
--- a/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html
+++ b/tests/wpt/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html
@@ -9,6 +9,7 @@
<script src="/dom/events/scrolling/scroll_support.js"></script>
<script src="/css/css-scroll-snap-2/resources/common.js"></script>
<script src="/css/css-scroll-snap-2/resources/user-scroll-common.js"></script>
+ <script src="/web-animations/testcommon.js"></script>
</head>
<body>
@@ -180,6 +181,11 @@
assertSnapEvent(evts[1], { block: snap_area_1, inline: null });
}, "snapchanging fires as scroll moves through different snap targets.");
+ promise_test(async (t) => {
+ await test_user_scroll_onsnapchanging(t, scroller, scroller,
+ snap_area_2);
+ }, "Element.onsnapchanging fires when scrolling a snap container.");
+
// snapchanging doesn't fire test.
promise_test(async (t) => {
test_no_snapchanging(t, scroller, 10);
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-001.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-001.html
index c0569909344..eff86675312 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-001.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-001.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-color auto on the root defers to ::-webkit-scrollbar</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-002.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-002.html
index 70bfa5586d9..1dfc63d68e3 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-002.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-002.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-color non-auto on the root overrides ::-webkit-scrollbar</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-003.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-003.html
index 3fb42bedeaf..1dc3c64f79a 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-003.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-003.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-color on scrollable areas correctly interacts with ::-webkit-scrollbar on container</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-004.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-004.html
index 5932cc5d4e1..cff00c634f7 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-004.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-004.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-005.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-005.html
index 52d0027fba6..d8ddc43c1c8 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-005.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-005.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar on scrollable area</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-006.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-006.html
index 11f0de750bf..55947c984c1 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-006.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-006.html
@@ -4,7 +4,7 @@
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
<link rel="match" href="scrollbar-color-006-ref.html" />
<link rel="mismatch" href="scrollbar-color-006-mis-ref.html" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<style>
.container {
scrollbar-gutter: stable;
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-007.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-007.html
index 75358fdf68b..0ef07089de8 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-007.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-007.html
@@ -4,7 +4,7 @@
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
<link rel="match" href="scrollbar-color-007-ref.html" />
<link rel="mismatch" href="scrollbar-color-007-mis-ref.html" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<style>
.container {
scrollbar-gutter: stable;
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-008.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-008.html
index 576193814cc..01e3848603e 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-008.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-008.html
@@ -4,7 +4,7 @@
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
<link rel="match" href="scrollbar-color-008-ref.html" />
<link rel="mismatch" href="scrollbar-color-008-mis-ref.html" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<style>
body {
scrollbar-color: yellow blue;
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-009.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-009.html
index 312bbc731b7..6b17c19c15c 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-009.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-009.html
@@ -4,7 +4,7 @@
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
<link rel="match" href="scrollbar-color-009-ref.html" />
<link rel="mismatch" href="scrollbar-color-009-mis-ref.html" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<style>
:root {
scrollbar-color: yellow blue;
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-010.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-010.html
index 9f560613e06..cc78504ba73 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-010.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-010.html
@@ -4,7 +4,7 @@
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
<link rel="match" href="scrollbar-color-010-ref.html" />
<link rel="mismatch" href="scrollbar-color-010-mis-ref.html" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<style>
:root {
scrollbar-color: yellow blue;
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-dynamic-8.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-dynamic-8.html
index 4926fadaca1..8f1cd13a2fa 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-color-dynamic-8.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-color-dynamic-8.html
@@ -2,7 +2,7 @@
<html class="reftest-wait">
<title>Dynamically set scrollbar-colors when starts ::-webkit-scrollbar and ensure scrollbars update</title>
<link rel="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" />
<link rel="match" href="scrollbar-color-dynamic-8-ref.html" />
<script src="/common/reftest-wait.js"></script>
<style>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-010.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-010.html
index ea117030673..3c28dff498c 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-010.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-010.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-width auto on the root defers to ::-webkit-scrollbar on the root</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-011.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-011.html
index a5f60dc28b0..e9c22c83a9a 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-011.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-011.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-width thin on the root overrides ::-webkit-scrollbar on the root</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-012.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-012.html
index a685d6c05b8..eefd5538b84 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-012.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-012.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-width none on the root overrides ::-webkit-scrollbar on the root</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-013.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-013.html
index f6460a4797a..4a8a9182e54 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-013.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-013.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-width thin on the root overridess ::-webkit-scrollbar</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-014.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-014.html
index b3702015d52..b8ca3cbb949 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-014.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-014.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-width thin on the body doesn't override ::-webkit-scrollbar on root</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-015.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-015.html
index 88a6af27bad..7fc1ae120ab 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-015.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-015.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-width on scrollable areas correctly interacts with ::-webkit-scrollbar</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-016.tentative.html b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-016.html
index 7d64131743f..29b4e47628e 100644
--- a/tests/wpt/tests/css/css-scrollbars/scrollbar-width-016.tentative.html
+++ b/tests/wpt/tests/css/css-scrollbars/scrollbar-width-016.html
@@ -2,7 +2,7 @@
<meta charset="utf-8">
<title>CSS Scrollbars: scrollbar-width on scrollable areas correctly interacts with ::-webkit-scrollbar on container</title>
<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" />
-<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" />
+<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
diff --git a/tests/wpt/tests/css/css-sizing/aspect-ratio/WEB_FEATURES.yml b/tests/wpt/tests/css/css-sizing/aspect-ratio/WEB_FEATURES.yml
new file mode 100644
index 00000000000..65e2142a16b
--- /dev/null
+++ b/tests/wpt/tests/css/css-sizing/aspect-ratio/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: aspect-ratio
+ files: "**"
diff --git a/tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.ogv b/tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.ogv
deleted file mode 100644
index 29903c0a817..00000000000
--- a/tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.webm b/tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.webm
new file mode 100644
index 00000000000..74af43afebe
--- /dev/null
+++ b/tests/wpt/tests/css/css-sizing/aspect-ratio/support/2x2-green.webm
Binary files differ
diff --git a/tests/wpt/tests/css/css-text/line-breaking/line-breaking-029.html b/tests/wpt/tests/css/css-text/line-breaking/line-breaking-029.html
new file mode 100644
index 00000000000..c390d2272d7
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/line-breaking/line-breaking-029.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<html>
+<meta charset="utf-8">
+<title>CSS Text — line breaking around Break After and Exclamation</title>
+<meta name=assert content="When ‘white-space’ allows wrapping, line breaking behavior defined for IS and QU line-breaking classes in [UAX14] must be honored.">
+<link rel=help href="https://www.w3.org/TR/css-text-3/#line-breaking">
+<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1880362">
+<link rel=author title="Makoto Kato" href="mailto:m_kato@ga2.so-net.ne.jp">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.test > div {
+ font-family: monospace;
+ font-size: 25px;
+ width: 1ch;
+ line-height: 30px;
+}
+</style>
+<body>
+
+<div class="test">
+ <div id="nonbreakable1">1.&#x201D;</div>
+ <div id="nonbreakable2">a.&#x201D;</div>
+ <div id="nonbreakable3">1.&#x0022;</div>
+ <div id="nonbreakable4">a.&#x0022;</div>
+</div>
+
+<script>
+test(function() {
+ assert_true(document.getElementById('nonbreakable1').offsetHeight <= 35);
+}, "U+0x0031 (NU), U+0x002E (IS) and U+0x201D (QU)");
+test(function() {
+ assert_true(document.getElementById('nonbreakable2').offsetHeight <= 35);
+}, "U+0x0041 (AL), U+0x002E (IS) and U+0x201D (QU)");
+test(function() {
+ assert_true(document.getElementById('nonbreakable3').offsetHeight <= 35);
+}, "U+0x0031 (NU), U+0x002E (IS) and U+0x0022 (QU)");
+test(function() {
+ assert_true(document.getElementById('nonbreakable4').offsetHeight <= 35);
+}, "U+0x0041 (AL), U+0x002E (IS) and U+0x0022 (QU)");
+</script>
+
+<div id='log'></div>
+
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-text/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-text/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..e06782e8f5b
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/parsing/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: text-spacing-trim
+ files:
+ - text-spacing-trim-*
diff --git a/tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control-ref.html b/tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control-ref.html
new file mode 100644
index 00000000000..c30c5a0178b
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="ja">
+ <head>
+ <meta charset="utf-8">
+ <title>text-align: justify for a bidi control + a CJK ideograph</title>
+ <style>
+ p { text-align: justify; width: 3.9em; }
+ </style>
+ </head>
+ <body>
+ <p>東京都東京都東京都</p>
+ </body>
+</html>
diff --git a/tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control.html b/tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control.html
new file mode 100644
index 00000000000..934e947b758
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/text-align/text-align-justify-bidi-control.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="ja">
+ <head>
+ <meta charset="utf-8">
+ <title>text-align: justify for a bidi control + a CJK ideograph</title>
+ <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-align-last-property">
+ <link rel="help" href="https://crbug.com/331680200">
+ <link rel="match" href="text-align-justify-bidi-control-ref.html">
+ <style>
+ p { text-align: justify; width: 3.9em; }
+ </style>
+ </head>
+ <body>
+ <p>東&#x2066;京都東京&#x2069;都東京都</p>
+ </body>
+</html>
diff --git a/tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br-ref.html b/tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br-ref.html
new file mode 100644
index 00000000000..efe5481e824
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="ja">
+ <head>
+ <title>text-align-last: justify for &lt;br></title>
+ <meta charset="utf-8">
+ <style>
+ .left { float: left; display: inline-block; }
+ .right { float: right; display: inline-block; }
+ </style>
+ </head>
+ <body>
+ <p><span class="left">東<br>京</span><span class="right">京<br>城</span></p>
+ </body>
+</html>
diff --git a/tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br.html b/tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br.html
new file mode 100644
index 00000000000..5685da088b8
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/text-align/text-align-last-justify-br.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="ja">
+ <head>
+ <meta charset="utf-8">
+ <title>text-align-last: justify for &lt;br></title>
+ <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-align-last-property">
+ <link rel="help" href="https://crbug.com/331729346">
+ <link rel="match" href="text-align-last-justify-br-ref.html">
+ <style>
+ p {
+ text-align-last: justify;
+ }
+ </style>
+ </head>
+ <body>
+ <p>東京<br>京城</p>
+ </body>
+</html>
diff --git a/tests/wpt/tests/css/css-text/text-spacing-trim/WEB_FEATURES.yml b/tests/wpt/tests/css/css-text/text-spacing-trim/WEB_FEATURES.yml
new file mode 100644
index 00000000000..329fa18e2a8
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/text-spacing-trim/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: text-spacing-trim
+ files: "**"
diff --git a/tests/wpt/tests/css/css-text/white-space/text-wrap-balance-right-to-left.html b/tests/wpt/tests/css/css-text/white-space/text-wrap-balance-right-to-left.html
new file mode 100644
index 00000000000..b9d50684d11
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/white-space/text-wrap-balance-right-to-left.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-text-4/#text-wrap">
+<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#container {
+ width: 20ch;
+}
+.balance {
+ text-wrap: balance;
+}
+</style>
+<div id="container" dir="rtl" lang="AR"></div>
+<script>
+const container = document.getElementById('container');
+for (const text of [
+ 'ينبغي تحقيق التوازن',
+ 'يجب ألا تؤدي موازنة التفاف النص إلى تغيير عدد الأسطر',
+ 'يجب ألا تؤدي موازنة التفاف النص إلى تغيير عدد الأسطر لهذه الفقرة النصية',
+ ]) {
+ const normal = document.createElement('div');
+ const balance = document.createElement('div');
+ normal.textContent = text;
+ balance.textContent = text;
+ balance.classList.add('balance');
+ container.appendChild(normal);
+ container.appendChild(balance);
+ test(() => {
+ assert_equals(normal.offsetHeight, balance.offsetHeight);
+ });
+}
+</script>
diff --git a/tests/wpt/tests/css/css-text/white-space/text-wrap-balance-top-to-bottom.html b/tests/wpt/tests/css/css-text/white-space/text-wrap-balance-top-to-bottom.html
new file mode 100644
index 00000000000..0c0f6b55caa
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/white-space/text-wrap-balance-top-to-bottom.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-text-4/#text-wrap">
+<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#container {
+ height: 15ch;
+ writing-mode: vertical-rl;
+}
+.balance {
+ text-wrap: balance;
+}
+</style>
+<div id="container"></div>
+<script>
+const container = document.getElementById('container');
+for (const text of [
+ '平衡应该',
+ '平衡文本换行不应改变行数',
+ '平衡文本换行不应改变该文本段落的行数',
+ ]) {
+ const normal = document.createElement('div');
+ const balance = document.createElement('div');
+ normal.textContent = text;
+ balance.textContent = text;
+ balance.classList.add('balance');
+ container.appendChild(normal);
+ container.appendChild(balance);
+ test(() => {
+ assert_equals(normal.offsetWidth, balance.offsetWidth);
+ });
+}
+</script>
diff --git a/tests/wpt/tests/css/css-transforms/WEB_FEATURES.yml b/tests/wpt/tests/css/css-transforms/WEB_FEATURES.yml
new file mode 100644
index 00000000000..ca13ab5ae1b
--- /dev/null
+++ b/tests/wpt/tests/css/css-transforms/WEB_FEATURES.yml
@@ -0,0 +1,6 @@
+features:
+- name: transforms3d
+ files:
+ - "*3d*"
+ - backface-visibility-*
+ - perspective-*
diff --git a/tests/wpt/tests/css/css-transforms/animation/WEB_FEATURES.yml b/tests/wpt/tests/css/css-transforms/animation/WEB_FEATURES.yml
new file mode 100644
index 00000000000..831086f99e9
--- /dev/null
+++ b/tests/wpt/tests/css/css-transforms/animation/WEB_FEATURES.yml
@@ -0,0 +1,5 @@
+features:
+- name: transforms3d
+ files:
+ - backface-visibility-*
+ - perspective-*
diff --git a/tests/wpt/tests/css/css-transforms/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-transforms/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..831086f99e9
--- /dev/null
+++ b/tests/wpt/tests/css/css-transforms/parsing/WEB_FEATURES.yml
@@ -0,0 +1,5 @@
+features:
+- name: transforms3d
+ files:
+ - backface-visibility-*
+ - perspective-*
diff --git a/tests/wpt/tests/css/css-transitions/parsing/starting-style-parsing.html b/tests/wpt/tests/css/css-transitions/parsing/starting-style-parsing.html
new file mode 100644
index 00000000000..bd147a630a6
--- /dev/null
+++ b/tests/wpt/tests/css/css-transitions/parsing/starting-style-parsing.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>@starting-style: parsing</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#at-ruledef-starting-style">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<main id=main></main>
+<script>
+ function test_valid(actual, expected) {
+ if (expected === undefined)
+ expected = actual;
+ test(t => {
+ t.add_cleanup(() => main.replaceChildren());
+ let style = document.createElement('style');
+ style.textContent = `${actual}{}`;
+ main.append(style);
+ assert_equals(style.sheet.rules.length, 1);
+ let rule = style.sheet.rules[0];
+ assert_equals(rule.cssText, `${expected} {\n}`);
+ }, `${actual} is valid`);
+ }
+
+ function test_invalid(actual) {
+ test(t => {
+ t.add_cleanup(() => main.replaceChildren());
+ let style = document.createElement('style');
+ style.textContent = `${actual}{}`;
+ main.append(style);
+ assert_equals(style.sheet.rules.length, 0);
+ }, `${actual} is not valid`);
+ }
+
+ test_valid('@starting-style');
+
+ test_invalid('@starting-style div');
+ test_invalid('@starting-style ()');
+ test_invalid('@starting-style ( {}');
+ test_invalid('@starting-style }');
+</script>
diff --git a/tests/wpt/tests/css/css-transitions/starting-style-adjustment.html b/tests/wpt/tests/css/css-transitions/starting-style-adjustment.html
new file mode 100644
index 00000000000..addc795e721
--- /dev/null
+++ b/tests/wpt/tests/css/css-transitions/starting-style-adjustment.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>CSS Transitions Test: Style adjustments for @starting-style</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#defining-before-change-style-the-starting-style-rule">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/css-transitions/support/helper.js"></script>
+<style>
+legend {
+ transition: display 1s step-end allow-discrete;
+}
+@starting-style {
+ legend { display:inline; }
+}
+</style>
+<body>
+<legend></legend>
+<script>
+promise_test(async t => {
+ await waitForAnimationFrames(1);
+ assert_equals(document.getAnimations().length, 0, "No transitions");
+}, "The display property in <legend> @starting-style should be blockified so no transition should start");
+</script>
+</body>
diff --git a/tests/wpt/tests/css/css-ui/WEB_FEATURES.yml b/tests/wpt/tests/css/css-ui/WEB_FEATURES.yml
new file mode 100644
index 00000000000..00334021116
--- /dev/null
+++ b/tests/wpt/tests/css/css-ui/WEB_FEATURES.yml
@@ -0,0 +1,7 @@
+features:
+- name: accent-color
+ files:
+ - accent-color-*
+- name: appearance
+ files:
+ - appearance-*
diff --git a/tests/wpt/tests/css/css-ui/animation/WEB_FEATURES.yml b/tests/wpt/tests/css/css-ui/animation/WEB_FEATURES.yml
new file mode 100644
index 00000000000..07cdf85b209
--- /dev/null
+++ b/tests/wpt/tests/css/css-ui/animation/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: accent-color
+ files:
+ - accent-color-*
diff --git a/tests/wpt/tests/css/css-ui/resize-014.html b/tests/wpt/tests/css/css-ui/resize-014.html
index fdbd77e38ae..c3e292438b5 100644
--- a/tests/wpt/tests/css/css-ui/resize-014.html
+++ b/tests/wpt/tests/css/css-ui/resize-014.html
@@ -18,8 +18,5 @@ video { resize: both; }
<source
src="support/test.mp4"
type="video/mp4">
- <source
- src="support/test.ogv"
- type="video/ogg">
&lt;video&gt; is not supported. This test is non conclusive.
</video>
diff --git a/tests/wpt/tests/css/css-ui/support/test.ogv b/tests/wpt/tests/css/css-ui/support/test.ogv
deleted file mode 100644
index 50d59dfb38b..00000000000
--- a/tests/wpt/tests/css/css-ui/support/test.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/css/css-values/WEB_FEATURES.yml b/tests/wpt/tests/css/css-values/WEB_FEATURES.yml
new file mode 100644
index 00000000000..ca3c0ff91b9
--- /dev/null
+++ b/tests/wpt/tests/css/css-values/WEB_FEATURES.yml
@@ -0,0 +1,17 @@
+features:
+- name: abs-sign
+ files:
+ - signs-abs-*
+- name: cap
+ files:
+ - cap-*
+- name: ic
+ files:
+ - ic-*
+- name: lh
+ files:
+ - lh-*
+- name: round-mod-rem
+ files:
+ - round-function.html
+ - round-mod-rem-*
diff --git a/tests/wpt/tests/css/css-values/container-progress-computed.tentative.html b/tests/wpt/tests/css/css-values/container-progress-computed.tentative.html
new file mode 100644
index 00000000000..9ab537cad6c
--- /dev/null
+++ b/tests/wpt/tests/css/css-values/container-progress-computed.tentative.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func">
+<link rel="author" title="sakhapov@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/numeric-testcommon.js"></script>
+<div id="out-of-scope-container"></div>
+<div id="extra-container">
+ <div id="outer-container">
+ <div id="inner-container">
+ <div id=target></div>
+ </div>
+ </div>
+</div>
+<style>
+:root {
+ font-size: 10px;
+}
+#out-of-scope-container {
+ container: my-container-3 / size;
+ width: 1px;
+ height: 1px;
+}
+#extra-container {
+ container: my-container-2 / size;
+ width: 5051px;
+ height: 1337px;
+}
+#outer-container {
+ container: my-container / size;
+ width: 322px;
+ height: 228px;
+}
+#inner-container {
+ container-type: size;
+ width: 228px;
+ height: 322px;
+}
+#target {
+ font-size: 10px;
+}
+</style>
+<script>
+
+let width = window.innerWidth;
+let height = window.innerHeight;
+
+let extraWidth = 5051;
+let extraHeight = 1337;
+let innerWidth = 228;
+let innerHeight = 322;
+let outerWidth = 322;
+let outerHeight = 228;
+
+// Identity tests
+test_math_used('container-progress(height from 0px to 1px)', innerHeight, {type:'number'});
+test_math_used('container-progress(width of my-container from 0px to 1px)', outerWidth, {type:'number'});
+
+// Nestings
+test_math_used('container-progress(height from container-progress(height from 0px to 1px) * 1px to container-progress(height from 0px to 1px) * 1px)', '0', {type:'number'});
+test_math_used('container-progress(height from container-progress(height from 0px to 1px) * 0.5px to container-progress(height from 0px to 1px) * 1px)', '1', {type:'number'});
+test_math_used('container-progress(height from container-progress(width of my-container from 0px to 1px) * 1px to container-progress(height of my-container-2 from 0px to 1px) * 1px)', (innerHeight - outerWidth) / (extraHeight - outerWidth), {type:'number'});
+
+// General calculations
+test_math_used('calc(container-progress(width from 0px to 50px) * 10px + 100px)', (innerWidth / 50 * 10 + 100) + 'px');
+test_math_used('calc(container-progress(height from 10px to sign(50px - 500em) * 10px))', (innerHeight - 10) / (-10 - 10), {type:'number'});
+test_math_used('calc(container-progress(width of my-container from 0px to 50px) * 10px + 100px)', (outerWidth / 50 * 10 + 100) + 'px');
+test_math_used('calc(container-progress(height of my-container from 10px to sign(50px - 500em) * 10px))', (outerHeight - 10) / (-10 - 10), {type:'number'});
+
+// Fallback
+test_math_used('container-progress(width of non-existing-container from 0px to 1px)', width, {type:'number'});
+test_math_used('container-progress(height of non-existing-container from 0px to 1px)', height, {type:'number'});
+test_math_used('container-progress(width of out-of-scope-container from 0px to 1px)', width, {type:'number'});
+test_math_used('container-progress(height of out-of-scope-container from 0px to 1px)', height, {type:'number'});
+
+// Type checking
+test_math_used('calc(container-progress(width from 0px to 1px) * 1px)', innerWidth + 'px');
+test_math_used('calc(container-progress(height of my-container from 0px to 1px) * 1s)', outerHeight + 's', {type:'time'});
+test_math_used('calc(container-progress(width of my-container-2 from 0px to 1px) * 1deg)', extraWidth + 'deg', {type:'angle', approx:0.001});
+</script>
diff --git a/tests/wpt/tests/css/css-values/container-progress-invalid.tentative.html b/tests/wpt/tests/css/css-values/container-progress-invalid.tentative.html
new file mode 100644
index 00000000000..a78fd344262
--- /dev/null
+++ b/tests/wpt/tests/css/css-values/container-progress-invalid.tentative.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func">
+<link rel="author" title="sakhapov@chromuim.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/parsing-testcommon.js"></script>
+<script>
+function test_invalid_number(value) {
+ test_invalid_value('opacity', value);
+}
+function test_invalid_length(value) {
+ // 'letter-spacing' accepts <length> only, not <percentage> or any mixes.
+ test_invalid_value('letter-spacing', value);
+}
+
+// Syntax checking
+test_invalid_number('container-progress()');
+test_invalid_number('container-progress( )');
+test_invalid_number('container-progress(,)');
+test_invalid_number('container-progress(1 from )');
+test_invalid_number('container-progress(1)');
+test_invalid_number('container-progress(50% to 0)');
+test_invalid_number('container-progress(0 from 1 to)');
+test_invalid_number('container-progress(from to)');
+test_invalid_number('container-progress(from 1 to 0)');
+test_invalid_number('container-progress(3 of 2 from 1 to 0)');
+test_invalid_number('container-progress(width of 2 from 1 to 0)');
+test_invalid_number('container-progress(from 1 to 0 1)');
+test_invalid_number('container-progress(from 1 0)');
+test_invalid_number('container-progress(0 from to 0)');
+test_invalid_number('container-progress(to to to to to)');
+test_invalid_number('container-progress(0, from, 10, to 200)');
+test_invalid_number('container-progress(0, from, 10, to, 200)');
+test_invalid_number('container-progress(0, from 10, to 200)');
+test_invalid_number('container-progress(0, 10, 200)');
+
+// General tests
+test_invalid_number('container-progress(height from 0 to 8');
+test_invalid_number('container-progress(height container from 0 to 8');
+test_invalid_number('container-progress(height of from 0 to 8');
+test_invalid_number('container-progress(depth from 0px to 8px');
+test_invalid_number('container-progress(width of 10 from 0px to 8px');
+test_invalid_number('container-progress(height of 10 from 0px to 8px');
+test_invalid_number('container-progress(height of name from 0deg to 8deg');
+test_invalid_number('container-progress(height of name from 0 to 8px');
+test_invalid_number('container-progress(10px from 0px to 8px');
+test_invalid_number('container-progress(depth of name from 0px to 8px');
+test_invalid_number('container-progress(width from 0deg to 8deg');
+test_invalid_number('container-progress(5 from 0deg to 8deg');
+test_invalid_number('container-progress(5 from 0% to 8deg');
+test_invalid_number('container-progress(height from 0% to sign(10px)');
+test_invalid_number('container-progress(5% from 0px to 10px');
+test_invalid_length('calc(1px * container-progress(10deg from 0 to 10))');
+test_invalid_length('calc(1px * container-progress(10 from 0px to 10))');
+</script>
diff --git a/tests/wpt/tests/css/css-values/container-progress-serialize.tentative.html b/tests/wpt/tests/css/css-values/container-progress-serialize.tentative.html
new file mode 100644
index 00000000000..181054c6533
--- /dev/null
+++ b/tests/wpt/tests/css/css-values/container-progress-serialize.tentative.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func">
+<link rel="author" title="sakhapov@chromuim.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../support/serialize-testcommon.js"></script>
+<div id="outer-container">
+ <div id="inner-container">
+ <div id=target></div>
+ </div>
+</div>
+<style>
+:root {
+ font-size: 10px;
+}
+#outer-container {
+ container: my-container / size;
+ width: 322px;
+ height: 228px;
+}
+#inner-container {
+ container-type: size;
+ width: 228px;
+ height: 322px;
+}
+#target {
+ font-size: 10px;
+}
+</style>
+<script>
+function test_serialization(t,s,c) {
+ test_specified_serialization('opacity', t, s);
+ test_specified_serialization('transform', `scale(${t})`, `scale(${s})`);
+ test_computed_serialization('opacity', t, c);
+ test_computed_serialization('transform', `scale(${t})`, `matrix(${c}, 0, 0, ${c}, 0, 0)`);
+}
+
+test_serialization(
+ 'calc(container-progress(width from 0px to 1px) / 1000)',
+ 'calc(container-progress(width from 0px to 1px) / 1000)',
+ '0.228',
+);
+test_serialization(
+ 'calc(0.1 * container-progress(height of my-container from 0px to 10em))',
+ 'calc(0.1 * container-progress(height of my-container from 0px to 10em))',
+ '0.228',
+);
+</script>
diff --git a/tests/wpt/tests/css/css-values/media-progress-computed.tentative.html b/tests/wpt/tests/css/css-values/media-progress-computed.tentative.html
index 3594d4cfbf4..b2b9c6662a5 100644
--- a/tests/wpt/tests/css/css-values/media-progress-computed.tentative.html
+++ b/tests/wpt/tests/css/css-values/media-progress-computed.tentative.html
@@ -1,4 +1,5 @@
<!DOCTYPE html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="help" href="https://drafts.csswg.org/css-values-5/#media-progress-func">
<link rel="author" title="sakhapov@chromium.org">
<script src="/resources/testharness.js"></script>
@@ -9,26 +10,33 @@
#target {
font-size: 10px;
}
+:root {
+ width: 100vw;
+ height: 100vh;
+}
</style>
<script>
-
-let width = window.innerWidth;
-let height = window.innerHeight;
+// innerWidth and innerHeight have lossy precision, see
+// https://github.com/w3c/csswg-drafts/issues/5260.
+let { width, height } = document.documentElement.getBoundingClientRect();
// Identity tests
- test_math_used('media-progress(height from 0px to 1px)', height, {type:'number'});
+// NOTE(emilio): We provide custom messages so that the test name doesn't
+// depend on the viewport size (since in testharness.js files the viewport size is
+// not guaranteed to be fixed, unlike in reftests).
+test_math_used('media-progress(height from 0px to 1px)', height, { type:'number', 'msg': 'media-progress() identity check' });
// Nestings
test_math_used('media-progress(height from media-progress(height from 0px to 1px) * 1px to media-progress(height from 0px to 1px) * 1px)', '0', {type:'number'});
test_math_used('media-progress(height from media-progress(height from 0px to 1px) * 0.5px to media-progress(height from 0px to 1px) * 1px)', '1', {type:'number'});
-// General calculations
-test_math_used('calc(media-progress(width from 0px to 50px) * 10px + 100px)', (width / 50 * 10 + 100) + 'px');
-test_math_used('calc(media-progress(height from 10px to sign(50px - 500em) * 10px))', (height - 10) / (-10 - 10), {type:'number'});
+// General calculations.
+test_math_used('calc(media-progress(width from 0px to 50px) * 10px + 100px)', (width / 50 * 10 + 100) + 'px', { msg: 'media-progress() with length product' });
+test_math_used('calc(media-progress(height from 10px to sign(50px - 500em) * 10px))', (height - 10) / (-10 - 10), { type:'number', msg: 'media-progress with complex to calculation' });
// Type checking
-test_math_used('calc(media-progress(width from 0px to 1px) * 1px)', width + 'px');
-test_math_used('calc(media-progress(height from 0px to 1px) * 1s)', height + 's', {type:'time'});
-test_math_used('calc(media-progress(width from 0px to 1px) * 1deg)', width + 'deg', {type:'angle', approx:0.001});
+test_math_used('calc(media-progress(width from 0px to 1px) * 1px)', width + 'px', { msg: 'media-progress() as length' });
+test_math_used('calc(media-progress(height from 0px to 1px) * 1s)', height + 's', { type:'time', msg: 'media-progress() as time' });
+test_math_used('calc(media-progress(width from 0px to 1px) * 1deg)', width + 'deg', { type:'angle', approx:0.001, msg: 'media-progress() as angle' });
</script>
diff --git a/tests/wpt/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html b/tests/wpt/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html
index 5a2624d09da..14bd768f76c 100644
--- a/tests/wpt/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html
+++ b/tests/wpt/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html
@@ -60,9 +60,7 @@ for (const fn of functionPseudoElements) {
test_invalid_selector(`${fn}(foo bar)`);
test_invalid_selector_combinations(`${fn}(foo bar)`);
- // Test function with selector arguments.
- test_invalid_selector(`${fn}(.foo)`);
- test_invalid_selector_combinations(`${fn}(.foo)`);
+ // Test function with selector arguments.
test_invalid_selector(`${fn}(#bar)`);
test_invalid_selector_combinations(`${fn}(#bar)`);
}
diff --git a/tests/wpt/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html b/tests/wpt/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html
new file mode 100644
index 00000000000..2dc7d00cd18
--- /dev/null
+++ b/tests/wpt/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html class=reftest-wait>
+<title>View transition classes: selector should match with wildcard</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/">
+<link rel="author" href="mailto:nrosenthal@chromium.org">
+<link rel="match" href="pseudo-with-classes-ref.html">
+<script src="/common/rendering-utils.js"></script>
+<script src="/common/reftest-wait.js"></script>
+
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ position: absolute;
+ }
+
+ #target {
+ background: green;
+ view-transition-name: target;
+ view-transition-class: cls;
+ }
+
+ ::view-transition-group(*) {
+ animation-duration: 1s;
+ }
+
+ ::view-transition-new(.cls),
+ ::view-transition-old(.cls) {
+ left: 100px;
+ }
+</style>
+<div id=target></div>
+
+<script>
+ failIfNot(document.startViewTransition, "Missing document.startViewTransition");
+
+ window.addEventListener("load", () => {
+ document.startViewTransition().ready.then(takeScreenshot);
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-view-transitions/update-callback-timeout.html b/tests/wpt/tests/css/css-view-transitions/update-callback-timeout.html
new file mode 100644
index 00000000000..9e96e97e117
--- /dev/null
+++ b/tests/wpt/tests/css/css-view-transitions/update-callback-timeout.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <title>View transitions: Transition has implementation-defined timeout.</title>
+ <link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
+ <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/">
+ <meta name="timeout" content="long">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ promise_test(async t => {
+ assert_implements(document.startViewTransition, "Missing document.startViewTransition");
+ const transition = document.startViewTransition(() => {
+ return new Promise(() => {});
+ });
+ transition.updateCallbackDone.then(() => {
+ assert_unreached();
+ });
+ transition.finished.then(() => {
+ assert_unreached();
+ });
+ await promise_rejects_dom(t, "TimeoutError", transition.ready);
+ }, "View transition should have an implementation-defined timeout on the update callback");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/cssom-view/WEB_FEATURES.yml b/tests/wpt/tests/css/cssom-view/WEB_FEATURES.yml
new file mode 100644
index 00000000000..a545dbadcb8
--- /dev/null
+++ b/tests/wpt/tests/css/cssom-view/WEB_FEATURES.yml
@@ -0,0 +1,5 @@
+features:
+- name: scroll-into-view
+ files:
+ - scrollIntoView-*
+ - scrollintoview.html
diff --git a/tests/wpt/tests/css/filter-effects/WEB_FEATURES.yml b/tests/wpt/tests/css/filter-effects/WEB_FEATURES.yml
new file mode 100644
index 00000000000..a882be3079a
--- /dev/null
+++ b/tests/wpt/tests/css/filter-effects/WEB_FEATURES.yml
@@ -0,0 +1,7 @@
+features:
+- name: backdrop-filter
+ files:
+ - backdrop-filter-*
+ - backdrop-filters-*
+ - css-backdrop-filters-*
+ - repaint-added-backdrop-filter.html
diff --git a/tests/wpt/tests/css/filter-effects/animation/WEB_FEATURES.yml b/tests/wpt/tests/css/filter-effects/animation/WEB_FEATURES.yml
new file mode 100644
index 00000000000..cd7e6695d4d
--- /dev/null
+++ b/tests/wpt/tests/css/filter-effects/animation/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: backdrop-filter
+ files:
+ - backdrop-filter-*
diff --git a/tests/wpt/tests/css/filter-effects/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/filter-effects/parsing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..cd7e6695d4d
--- /dev/null
+++ b/tests/wpt/tests/css/filter-effects/parsing/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: backdrop-filter
+ files:
+ - backdrop-filter-*
diff --git a/tests/wpt/tests/css/motion/animation/offset-path-interpolation-008.html b/tests/wpt/tests/css/motion/animation/offset-path-interpolation-008.html
index 0632ee7a823..30d789f4eb8 100644
--- a/tests/wpt/tests/css/motion/animation/offset-path-interpolation-008.html
+++ b/tests/wpt/tests/css/motion/animation/offset-path-interpolation-008.html
@@ -155,6 +155,110 @@
{at: 1.5, expect: 'shape(from -10px calc(2.5% + 7.5px), hline to 25px, vline by calc(15% - 80px), hline by 19.88px, close, vline by 10px)'},
]);
+ test_no_interpolation({
+ property: 'offset-path',
+ from: 'shape(from 10px 10px, move to 10% 10%)',
+ to: 'path("M10 10 z")',
+ });
+
+ test_no_interpolation({
+ property: 'offset-path',
+ from: 'path("M10 10 M10 10")',
+ to: 'shape(from 10px 10px, close)',
+ });
+
+ test_no_interpolation({
+ property: 'offset-path',
+ from: 'path("M10 10 h 5")',
+ to: 'shape(from 10px 10px, hline to 5px)',
+ });
+
+ test_interpolation({
+ property: 'offset-path',
+ from: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)',
+ to: 'path("M 15 15 H 25 V -15 Z")',
+ }, [
+ {at: -0.3, expect: 'shape(from 2px 2px, hline to -1px, vline to -2px, close)'},
+ {at: 0, expect: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)'},
+ {at: 0.5, expect: 'shape(from 10px 10px, hline to 15px, vline to -10px, close)'},
+ {at: 1, expect: 'shape(from 15px 15px, hline to 25px, vline to -15px, close)'},
+ {at: 1.5, expect: 'shape(from 20px 20px, hline to 35px, vline to -20px, close)'},
+ ]);
+
+ test_interpolation({
+ property: 'offset-path',
+ from: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)',
+ to: 'path("M 15 15 Q 10 60 20 0 C 30 40 -5 100 20 30")',
+ }, [
+ {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, curve to calc(13% - 6px) 13px via calc(0% - 3px) 86px, curve to calc(39% - 6px) 17px via calc(26% - 9px) 53px calc(32.5% + 1.5px) 61px)'},
+ {at: 0, expect: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)'},
+ {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, curve to calc(5% + 10px) 5px via calc(0% + 5px) 70px, curve to calc(15% + 10px) 25px via calc(10% + 15px) 45px calc(12.5% - 2.5px) 85px)'},
+ {at: 1, expect: 'shape(from calc(0% + 15px) 15px, curve to calc(0% + 20px) 0px via calc(0% + 10px) 60px, curve to calc(0% + 20px) 30px via calc(0% + 30px) 40px calc(0% - 5px) 100px)'},
+ {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, curve to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, curve to calc(-15% + 30px) 35px via calc(-10% + 45px) 35px calc(-12.5% - 7.5px) 115px)'},
+ ]);
+
+ test_interpolation({
+ property: 'offset-path',
+ from: 'path("M 5 5 q 0 80 10 10 c 20 50 25 70 30 20")',
+ to: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)',
+ }, [
+ {at: -0.3, expect: 'shape(from calc(-4.5% + 6.5px) 2px, curve by calc(-6% + 13px) 13px via -3% 86px, curve by calc(-6% + 39px) 17px via calc(-9% + 26px) 53px calc(1.5% + 32.5px) 61px)'},
+ {at: 0, expect: 'shape(from calc(0% + 5px) 5px, curve by calc(0% + 10px) 10px via 0% 80px, curve by calc(0% + 30px) 20px via calc(0% + 20px) 50px calc(0% + 25px) 70px)'},
+ {at: 0.5, expect: 'shape(from calc(7.5% + 2.5px) 10px, curve by calc(10% + 5px) 5px via 5% 70px, curve by calc(10% + 15px) 25px via calc(15% + 10px) 45px calc(-2.5% + 12.5px) 85px)'},
+ {at: 1, expect: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)'},
+ {at: 1.5, expect: 'shape(from calc(22.5% - 2.5px) 20px, curve by calc(30% - 5px) -5px via 15% 50px, curve by calc(30% - 15px) 35px via calc(45% - 10px) 35px calc(-7.5% - 12.5px) 115px)'},
+ ]);
+
+ test_interpolation({
+ property: 'offset-path',
+ from: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)',
+ to: 'path("M 15 15 S 10 60 20 0 T 20 30")',
+ }, [
+ {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, smooth to calc(13% - 6px) 13px via calc(0% - 3px) 86px, smooth to calc(39% - 6px) 17px)'},
+ {at: 0, expect: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)'},
+ {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, smooth to calc(5% + 10px) 5px via calc(0% + 5px) 70px, smooth to calc(15% + 10px) 25px)'},
+ {at: 1, expect: 'shape(from calc(0% + 15px) 15px, smooth to calc(0% + 20px) 0px via calc(0% + 10px) 60px, smooth to calc(0% + 20px) 30px)'},
+ {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, smooth to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, smooth to calc(-15% + 30px) 35px)'},
+ ]);
+
+ test_interpolation({
+ property: 'offset-path',
+ from: 'path("M 5 5 s 0 80 10 10 t 30 20")',
+ to: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)',
+ }, [
+ {at: -0.3, expect: 'shape(from 2px 2px, smooth by 7px 13px via -3px 86px, smooth by 33px 17px)'},
+ {at: 0, expect: 'shape(from 5px 5px, smooth by 10px 10px via 0px 80px, smooth by 30px 20px)'},
+ {at: 0.5, expect: 'shape(from 10px 10px, smooth by 15px 5px via 5px 70px, smooth by 25px 25px)'},
+ {at: 1, expect: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)'},
+ {at: 1.5, expect: 'shape(from 20px 20px, smooth by 25px -5px via 15px 50px, smooth by 15px 35px)'},
+ ]);
+
+ test_interpolation({
+ property: 'offset-path',
+ from: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)',
+ to: 'path("M 15 15 A 20,30 0 0,0 5,-25 a 20,20 270 0,1 25,-15 A 10,5 0 0,0 25 20")',
+ }, [
+ {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, arc to calc(19.5% - 1.5px) -12px of 7px 17px, arc by calc(19.5% - 7.5px) -2px of 33px cw large rotate -42deg, arc to calc(32.5% - 7.5px) 20px of 10px 5px)'},
+ {at: 0, expect: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)'},
+ {at: 0.3, expect: 'shape(from calc(3.5% + 4.5px) 8px, arc to calc(10.5% + 1.5px) -18px of 13px 23px, arc by calc(10.5% + 7.5px) -8px of 27px cw large rotate 102deg, arc to calc(17.5% + 7.5px) 20px of 10px 5px)'},
+ {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, arc to calc(7.5% + 2.5px) -20px of 15px 25px, arc by calc(7.5% + 12.5px) -10px of 25px cw large rotate 150deg, arc to calc(12.5% + 12.5px) 20px of 10px 5px)'},
+ {at: 1, expect: 'shape(from calc(0% + 15px) 15px, arc to calc(0% + 5px) -25px of 20px 30px, arc by calc(0% + 25px) -15px of 20px cw rotate 270deg, arc to calc(0% + 25px) 20px of 10px 5px)'},
+ {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, arc to calc(-7.5% + 7.5px) -30px of 25px 35px, arc by calc(-7.5% + 37.5px) -20px of 15px cw rotate 390deg, arc to calc(-12.5% + 37.5px) 20px of 10px 5px)'},
+ ]);
+
+ test_interpolation({
+ property: 'offset-path',
+ from: 'path("M 5 5 A 10,20 0 0,0 15,-15 a 30,30 30 1,1 15,-5 A 10,5 0 0,0 25 20")',
+ to: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px cw rotate 270deg small, arc to 25px 20px of 10px 5px small cw)'
+ }, [
+ {at: -0.3, expect: 'shape(from 2px 2px, arc to 18px -12px of 7px 17px ccw small, arc by 12px -2px of 33px 33px rotate -42deg cw large , arc to 25px 20px of 10px 5px ccw small)'},
+ {at: 0, expect: 'shape(from 5px 5px, arc to 15px -15px of 10px 20px, arc by 15px -5px of 30px cw rotate 30deg large, arc to 25px 20px of 10px 5px small)'},
+ {at: 0.3, expect: 'shape(from 8px 8px, arc to 12px -18px of 13px 23px ccw small, arc by 18px -8px of 27px 27px rotate 102deg cw large, arc to 25px 20px of 10px 5px ccw small )'},
+ {at: 0.5, expect: 'shape(from 10px 10px, arc to 10px -20px of 15px 25px ccw small, arc by 20px -10px of 25px rotate 150deg cw large, arc to 25px 20px of 10px 5px cw small)'},
+ {at: 1, expect: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px rotate 270deg cw small, arc to 25px 20px of 10px 5px cw small)'},
+ {at: 1.5, expect: 'shape(from 20px 20px, arc to 0px -30px of 25px 35px ccw small, arc by 30px -20px of 15px rotate 390deg cw small, arc to 25px 20px of 10px 5px cw small)'},
+ ]);
+
</script>
</body>
</html>
diff --git a/tests/wpt/tests/css/selectors/WEB_FEATURES.yml b/tests/wpt/tests/css/selectors/WEB_FEATURES.yml
index a5574bde7b8..47cf05a2c0f 100644
--- a/tests/wpt/tests/css/selectors/WEB_FEATURES.yml
+++ b/tests/wpt/tests/css/selectors/WEB_FEATURES.yml
@@ -2,3 +2,6 @@ features:
- name: focus-visible
files:
- focus-visible-*
+- name: has
+ files:
+ - has-*
diff --git a/tests/wpt/tests/css/selectors/dir-pseudo-on-input-element.html b/tests/wpt/tests/css/selectors/dir-pseudo-on-input-element.html
index 25f7a080d7e..b1427bf42d6 100644
--- a/tests/wpt/tests/css/selectors/dir-pseudo-on-input-element.html
+++ b/tests/wpt/tests/css/selectors/dir-pseudo-on-input-element.html
@@ -1,14 +1,10 @@
<!DOCTYPE html>
-<html>
-<head>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#the-directionality">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
-</head>
<body>
<script>
-
test(() => {
const input = document.createElement('input');
input.type = 'tel';
@@ -195,9 +191,17 @@ for (const type of ['date', 'time', 'number', 'range', 'color', 'checkbox', 'rad
input.removeAttribute('dir');
assert_true(input.matches(':dir(ltr)'));
assert_false(input.matches(':dir(rtl)'));
+
+ let rtlParent = document.createElement("div");
+ rtlParent.dir = "rtl";
+ input.dir = "auto";
+ rtlParent.appendChild(input);
+ document.body.appendChild(rtlParent); // Just for good measure.
+ assert_true(input.matches(':dir(ltr)'));
+ assert_false(input.matches(':dir(rtl)'));
+ rtlParent.remove();
}, `input element whose type attribute is in the ${type} state`);
}
</script>
-</body>
</html>
diff --git a/tests/wpt/tests/css/selectors/invalidation/WEB_FEATURES.yml b/tests/wpt/tests/css/selectors/invalidation/WEB_FEATURES.yml
new file mode 100644
index 00000000000..4eaa2f39310
--- /dev/null
+++ b/tests/wpt/tests/css/selectors/invalidation/WEB_FEATURES.yml
@@ -0,0 +1,6 @@
+features:
+- name: has
+ files:
+ - has-*
+ - "*-in-has.*"
+ - "*-in-has-*"
diff --git a/tests/wpt/tests/css/selectors/invalidation/is-where-pseudo-containing-hard-pseudo.html b/tests/wpt/tests/css/selectors/invalidation/is-where-pseudo-containing-hard-pseudo.html
new file mode 100644
index 00000000000..416aacca8c0
--- /dev/null
+++ b/tests/wpt/tests/css/selectors/invalidation/is-where-pseudo-containing-hard-pseudo.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<title>CSS Selectors Invalidation: :is and :where selectors containing "hard" selectors</title>
+<link rel="author" title="David Shin" href="dshin@mozilla.com">
+<link rel="help" href="https://drafts.csswg.org/selectors/#logical-combination">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1874042">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.container {
+ color: grey;
+}
+
+#subject1:is(.other-match, :has(.descendant)) {
+ color: red;
+}
+
+#subject1:is(.parent > .other-match, .parent > :has(.descendant)) {
+ color: orangered;
+}
+
+#subject2:where(.other-match, :has(.descendant)) {
+ color: darkred;
+}
+
+#subject2:where(.parent > .other-match, .parent > :has(.descendant)) {
+ color: pink;
+}
+
+#subject3:is(.other-match, :nth-child(1000 of .another-match)) {
+ color: green;
+}
+
+#subject3:is(.parent > .other-match, .parent > :nth-child(1000 of .another-match)) {
+ color: lightgreen;
+}
+
+#subject4:where(.other-match, :nth-child(1000 of .another-match)) {
+ color: darkgreen;
+}
+
+#subject4:where(.parent > .other-match, .parent > :nth-child(1000 of .another-match)) {
+ color: yellowgreen;
+}
+</style>
+<div id="par">
+ <div id="subject1" class="container"></div>
+ <div id="subject2" class="container"></div>
+ <div id="subject3" class="container another-match"></div>
+ <div id="subject4" class="container another-match"></div>
+</div>
+<script>
+const colors = {
+ grey: "rgb(128, 128, 128)",
+ red: "rgb(255, 0, 0)",
+ orangered: "rgb(255, 69, 0)",
+ darkred: "rgb(139, 0, 0)",
+ pink: "rgb(255, 192, 203)",
+ green: "rgb(0, 128, 0)",
+ lightgreen: "rgb(144, 238, 144)",
+ darkgreen: "rgb(0, 100, 0)",
+ yellowgreen: "rgb(154, 205, 50)"
+};
+
+function testClassChange(subject, before, after, afterParent) {
+ const cls = "other-match";
+ const parentCls = "parent";
+ const beforeColor = colors[before];
+
+ test(() => {
+ assert_equals(getComputedStyle(subject).color, beforeColor);
+ }, subject.id + " initial color is " + before);
+
+ subject.classList.add(cls);
+ const afterColor = colors[after];
+ test(() => {
+ assert_equals(getComputedStyle(subject).color, afterColor);
+ }, subject.id + " is " + after + " when ." + cls + " added");
+
+ par.classList.add(parentCls);
+ const afterParentColor = colors[afterParent];
+ test(() => {
+ assert_equals(getComputedStyle(subject).color, afterParentColor);
+ }, subject.id + " is " + afterParent + " when ." + parentCls + " added to parent");
+
+ par.classList.remove(parentCls);
+ test(() => {
+ assert_equals(getComputedStyle(subject).color, afterColor);
+ }, subject.id + " is " + afterParent + " when ." + parentCls + " removed from parent");
+
+ subject.classList.remove(cls);
+ test(() => {
+ assert_equals(getComputedStyle(subject).color, beforeColor);
+ }, subject.id + " is " + after + " when ." + cls + " removed");
+}
+
+testClassChange(subject1, "grey", "red", "orangered");
+testClassChange(subject2, "grey", "darkred", "pink");
+testClassChange(subject3, "grey", "green", "lightgreen");
+testClassChange(subject4, "grey", "darkgreen", "yellowgreen");
+</script>
diff --git a/tests/wpt/tests/css/selectors/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/selectors/parsing/WEB_FEATURES.yml
index 0da21b5f1ca..261019c003d 100644
--- a/tests/wpt/tests/css/selectors/parsing/WEB_FEATURES.yml
+++ b/tests/wpt/tests/css/selectors/parsing/WEB_FEATURES.yml
@@ -2,3 +2,7 @@ features:
- name: focus-visible
files:
- parse-focus-visible.html
+- name: has
+ files:
+ - parse-has.html
+ - parse-has-*
diff --git a/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html b/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html
index 58e002c52ca..dd830b77679 100644
--- a/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html
+++ b/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html
@@ -62,7 +62,7 @@ testReflectBooleanAttributeWithDependentAttributes(
);
testReflectAttribute(
- 'src', 'src', '/media/video.ogv',
+ 'src', 'src', '/media/video.webm',
'/media/movie_5.mp4', 'src on HTMLMediaElement in video', 'video',
HTMLVideoElement
);
diff --git a/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html b/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html
index f7d567ebcb9..8168fd1c2e7 100644
--- a/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html
+++ b/tests/wpt/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html
@@ -22,7 +22,7 @@ function getParentElement(id) {
}
testReflectAttributeWithParentNode(
- 'src', 'src', '/media/video.ogv',
+ 'src', 'src', '/media/video.webm',
'/media/white.mp4', 'src on HTMLSourceElement', 'source',
() => getParentElement('video'), HTMLSourceElement
);
diff --git a/tests/wpt/tests/device-posture/WEB_FEATURES.yml b/tests/wpt/tests/device-posture/WEB_FEATURES.yml
new file mode 100644
index 00000000000..7bafe104300
--- /dev/null
+++ b/tests/wpt/tests/device-posture/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: device-posture
+ files: "**"
diff --git a/tests/wpt/tests/document-policy/experimental-features/resources/video.ogv b/tests/wpt/tests/document-policy/experimental-features/resources/video.ogv
deleted file mode 100644
index c9ee910fc66..00000000000
--- a/tests/wpt/tests/document-policy/experimental-features/resources/video.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/document-policy/experimental-features/resources/video.webm b/tests/wpt/tests/document-policy/experimental-features/resources/video.webm
new file mode 100644
index 00000000000..10a1ae5d071
--- /dev/null
+++ b/tests/wpt/tests/document-policy/experimental-features/resources/video.webm
Binary files differ
diff --git a/tests/wpt/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html b/tests/wpt/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html
index d7bb7252490..4e5fbc65558 100644
--- a/tests/wpt/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html
+++ b/tests/wpt/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html
@@ -60,7 +60,7 @@ for (var test of test_cases) {
}));
var expected_width = test.expected_width;
var expected_height = test.expected_height;
- video.setAttribute("src", "/document-policy/experimental-features/resources/video.ogv");
+ video.setAttribute("src", "/document-policy/experimental-features/resources/video.webm");
if (typeof test.attribute !== "undefined") {
video.setAttribute(test.attribute, test.value);
}
diff --git a/tests/wpt/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html b/tests/wpt/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html
index a9b7ba633e7..fa4a987751d 100644
--- a/tests/wpt/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html
+++ b/tests/wpt/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html
@@ -7,29 +7,53 @@
<div id="div">hello</div>
<script>
let scriptRan = false;
-let computedStyleDuringInsertion = null;
+let computedStyleInPreScript = null;
+let computedStyleInPostScript = null;
test(() => {
const div = document.getElementById("div");
+
+ // 1. Gets inserted *before* the `<meta>` tag. Cannot observe the meta tag's
+ // effect, because this script runs before the meta tag's post-insertion steps
+ // run, and the meta tag's post-insertion steps is where the default style
+ // sheet actually changes.
+ const preScript = document.createElement("script");
+ preScript.textContent = `
+ computedStyleInPreScript = getComputedStyle(div).display;
+ scriptRan = true;
+ `;
+
+ // 2. The `<meta>` tag itself.
const meta = document.createElement("meta");
meta.httpEquiv = "default-style";
meta.content = "alternative";
- const script = document.createElement("script");
- script.textContent = `
- computedStyleDuringInsertion = getComputedStyle(div).display;
+
+ // 3. Gets inserted *after* the `<meta>` tag. Observes the meta tag's effect,
+ // because this script runs after the meta tag's post-insertion steps, which
+ // has the script-observable change to the default style sheet.
+ const postScript = document.createElement("script");
+ postScript.textContent = `
+ computedStyleInPostScript = getComputedStyle(div).display;
scriptRan = true;
`;
+
const df = document.createDocumentFragment();
- df.appendChild(script);
- df.appendChild(meta);
- assert_equals(getComputedStyle(div).display, "block", "div has block display");
+ df.append(preScript, meta, postScript);
+
+ assert_equals(getComputedStyle(div).display, "block",
+ "div still has block display before meta insertion");
assert_false(scriptRan, "script has not run before insertion");
+
document.head.appendChild(df);
assert_true(scriptRan, "script has run after insertion");
- assert_equals(computedStyleDuringInsertion, "none",
- "display: none; style was applied during DOM insertion, before " +
- "later-inserted script runs");
+ assert_equals(computedStyleInPreScript, "block",
+ "display: none; style was NOT applied during DOM insertion steps, " +
+ "before earlier-inserted script post-insertion steps run");
+ assert_equals(computedStyleInPostScript, "none",
+ "display: none; style WAS applied during DOM post-insertion steps, " +
+ "before later-inserted script runs");
assert_equals(getComputedStyle(div).display, "none",
"style remains display: none; after insertion");
+
}, "Inserting <meta> that uses alternate stylesheets, applies the style " +
- "during DOM insertion, and before script runs as a result of any atomic insertions");
+ "during DOM post-insertion steps");
</script>
diff --git a/tests/wpt/tests/dom/observable/tentative/observable-first.any.js b/tests/wpt/tests/dom/observable/tentative/observable-first.any.js
new file mode 100644
index 00000000000..7c99066dc22
--- /dev/null
+++ b/tests/wpt/tests/dom/observable/tentative/observable-first.any.js
@@ -0,0 +1,114 @@
+promise_test(async () => {
+ const results = [];
+
+ const source = new Observable(subscriber => {
+ subscriber.addTeardown(() => results.push('teardown'));
+ subscriber.next(1);
+ results.push(subscriber.active ? 'active' : 'inactive');
+ results.push(subscriber.signal.aborted ? 'aborted' : 'not aborted')
+
+ // Ignored.
+ subscriber.next(2);
+ subscriber.complete();
+ });
+
+ const value = await source.first();
+
+ assert_array_equals(results, ['teardown', 'inactive', 'aborted']);
+ assert_equals(value, 1,
+ "Promise resolves with the first value from the source Observable");
+}, "first(): Promise resolves with the first value from the source Observable");
+
+promise_test(async () => {
+ const error = new Error("error from source");
+ const source = new Observable(subscriber => {
+ subscriber.error(error);
+ });
+
+ let rejection;
+ try {
+ await source.first();
+ } catch (e) {
+ rejection = e;
+ }
+
+ assert_equals(rejection, error, "Promise rejects with source Observable error");
+}, "first(): Promise rejects with the error emitted from the source Observable");
+
+promise_test(async () => {
+ const source = new Observable(subscriber => {
+ subscriber.complete();
+ });
+
+ let rejection;
+ try {
+ await source.first();
+ } catch (e) {
+ rejection = e;
+ }
+
+ assert_true(rejection instanceof RangeError,
+ "Upon complete(), first() Promise rejects with RangeError");
+ assert_equals(rejection.message, "No values in Observable");
+}, "first(): Promise rejects with RangeError when source Observable " +
+ "completes without emitting any values");
+
+promise_test(async () => {
+ const source = new Observable(subscriber => {});
+
+ const controller = new AbortController();
+ const promise = source.first({ signal: controller.signal });
+
+ controller.abort();
+
+ let rejection;
+ try {
+ await promise;
+ } catch (e) {
+ rejection = e;
+ }
+
+ assert_true(rejection instanceof DOMException,
+ "Promise rejects with a DOMException for abortion");
+ assert_equals(rejection.name, "AbortError",
+ "Rejected with 'AbortError' DOMException");
+ assert_equals(rejection.message, "signal is aborted without reason");
+}, "first(): Aborting a signal rejects the Promise with an AbortError DOMException");
+
+promise_test(async () => {
+ const results = [];
+
+ const source = new Observable(subscriber => {
+ results.push("source subscribe");
+ subscriber.addTeardown(() => results.push("source teardown"));
+ subscriber.signal.addEventListener("abort", () => results.push("source abort"));
+ results.push("before source next 1");
+ subscriber.next(1);
+ results.push("after source next 1");
+ });
+
+ results.push("calling first");
+ const promise = source.first();
+
+ assert_array_equals(results, [
+ "calling first",
+ "source subscribe",
+ "before source next 1",
+ "source teardown",
+ "source abort",
+ "after source next 1"
+ ], "Array values after first() is called");
+
+ const firstValue = await promise;
+ results.push(`first resolved with: ${firstValue}`);
+
+ assert_array_equals(results, [
+ "calling first",
+ "source subscribe",
+ "before source next 1",
+ "source teardown",
+ "source abort",
+ "after source next 1",
+ "first resolved with: 1",
+ ], "Array values after Promise is awaited");
+}, "first(): Lifecycle");
diff --git a/tests/wpt/tests/dom/observable/tentative/observable-last.any.js b/tests/wpt/tests/dom/observable/tentative/observable-last.any.js
new file mode 100644
index 00000000000..cd39a3700a2
--- /dev/null
+++ b/tests/wpt/tests/dom/observable/tentative/observable-last.any.js
@@ -0,0 +1,113 @@
+promise_test(async () => {
+ const source = new Observable(subscriber => {
+ // Never exposed to the `last()` promise.
+ subscriber.next(1);
+
+ subscriber.next(2);
+ subscriber.complete();
+ });
+
+ const value = await source.last();
+
+ assert_equals(value, 2);
+}, "last(): Promise resolves to last value");
+
+promise_test(async () => {
+ const error = new Error("error from source");
+ const source = new Observable(subscriber => {
+ subscriber.error(error);
+ });
+
+ let rejection = null;
+ try {
+ await source.last();
+ } catch (e) {
+ rejection = e;
+ }
+
+ assert_equals(rejection, error);
+}, "last(): Promise rejects with emitted error");
+
+promise_test(async () => {
+ const source = new Observable(subscriber => {
+ subscriber.complete();
+ });
+
+ let rejection = null;
+ try {
+ await source.last();
+ } catch (e) {
+ rejection = e;
+ }
+
+ assert_true(rejection instanceof RangeError,
+ "Promise rejects with RangeError");
+ assert_equals(rejection.message, "No values in Observable");
+}, "last(): Promise rejects with RangeError when source Observable " +
+ "completes without emitting any values");
+
+promise_test(async () => {
+ const source = new Observable(subscriber => {});
+
+ const controller = new AbortController();
+ const promise = source.last({ signal: controller.signal });
+
+ controller.abort();
+
+ let rejection = null;
+ try {
+ await promise;
+ } catch (e) {
+ rejection = e;
+ }
+
+ assert_true(rejection instanceof DOMException,
+ "Promise rejects with a DOMException for abortion");
+ assert_equals(rejection.name, "AbortError",
+ "Rejected with 'AbortError' DOMException");
+ assert_equals(rejection.message, "signal is aborted without reason");
+}, "last(): Aborting a signal rejects the Promise with an AbortError DOMException");
+
+promise_test(async () => {
+ const results = [];
+ const source = new Observable(subscriber => {
+ results.push("source subscribe");
+ subscriber.addTeardown(() => results.push("source teardown"));
+ subscriber.signal.addEventListener("abort", () => results.push("source abort"));
+ results.push("before source next 1");
+ subscriber.next(1);
+ results.push("after source next 1");
+ results.push("before source complete");
+ subscriber.complete();
+ results.push("after source complete");
+ });
+
+ results.push("calling last");
+ const promise = source.last();
+
+ assert_array_equals(results, [
+ "calling last",
+ "source subscribe",
+ "before source next 1",
+ "after source next 1",
+ "before source complete",
+ "source teardown",
+ "source abort",
+ "after source complete",
+ ], "Array values after last() is called");
+
+ const lastValue = await promise;
+ results.push(`last resolved with: ${lastValue}`);
+
+ assert_array_equals(results, [
+ "calling last",
+ "source subscribe",
+ "before source next 1",
+ "after source next 1",
+ "before source complete",
+ "source teardown",
+ "source abort",
+ "after source complete",
+ "last resolved with: 1",
+ ], "Array values after Promise is awaited");
+}, "last(): Lifecycle");
diff --git a/tests/wpt/tests/dom/observable/tentative/observable-switchMap.any.js b/tests/wpt/tests/dom/observable/tentative/observable-switchMap.any.js
new file mode 100644
index 00000000000..836a39a68e0
--- /dev/null
+++ b/tests/wpt/tests/dom/observable/tentative/observable-switchMap.any.js
@@ -0,0 +1,252 @@
+test(() => {
+ const source = createTestSubject();
+ const inner1 = createTestSubject();
+ const inner2 = createTestSubject();
+
+ const result = source.switchMap((value, index) => {
+ if (value === 1) {
+ return inner1;
+ }
+ if (value === 2) {
+ return inner2;
+ }
+ throw new Error("invalid ");
+ });
+
+ const results = [];
+
+ result.subscribe({
+ next: v => results.push(v),
+ error: e => results.push(e),
+ complete: () => results.push("complete"),
+ });
+
+ assert_equals(source.subscriberCount(), 1,
+ "source observable is subscribed to");
+
+ source.next(1);
+ assert_equals(inner1.subscriberCount(), 1,
+ "inner1 observable is subscribed to");
+
+ inner1.next("1a");
+ assert_array_equals(results, ["1a"]);
+
+ inner1.next("1b");
+ assert_array_equals(results, ["1a", "1b"]);
+
+ source.next(2);
+ assert_equals(inner1.subscriberCount(), 0,
+ "inner1 observable is unsubscribed from");
+ assert_equals(inner2.subscriberCount(), 1,
+ "inner2 observable is subscribed to");
+
+ inner2.next("2a");
+ assert_array_equals(results, ["1a", "1b", "2a"]);
+
+ inner2.next("2b");
+ assert_array_equals(results, ["1a", "1b", "2a", "2b"]);
+
+ inner2.complete();
+ assert_array_equals(results, ["1a", "1b", "2a", "2b"]);
+
+ source.complete();
+ assert_array_equals(results, ["1a", "1b", "2a", "2b", "complete"]);
+}, "switchMap(): result subscribes to one inner observable at a time, " +
+ "unsubscribing from the previous active one when a new one replaces it");
+
+test(() => {
+ const source = createTestSubject();
+ const inner = createTestSubject();
+
+ const result = source.switchMap(() => inner);
+
+ const results = [];
+
+ result.subscribe({
+ next: v => results.push(v),
+ error: e => results.push(e),
+ complete: () => results.push("complete"),
+ });
+
+ assert_equals(source.subscriberCount(), 1,
+ "source observable is subscribed to");
+ assert_equals(inner.subscriberCount(), 0,
+ "inner observable is not subscribed to");
+
+ source.next(1);
+ assert_equals(inner.subscriberCount(), 1,
+ "inner observable is subscribed to");
+
+ inner.next("a");
+ assert_array_equals(results, ["a"]);
+
+ inner.next("b");
+ assert_array_equals(results, ["a", "b"]);
+
+ source.complete();
+ assert_array_equals(results, ["a", "b"],
+ "Result observable does not complete when source observable completes, " +
+ "because inner is still active");
+
+ inner.next("c");
+ assert_array_equals(results, ["a", "b", "c"]);
+
+ inner.complete();
+ assert_array_equals(results, ["a", "b", "c", "complete"],
+ "Result observable completes when inner observable completes, because " +
+ "source is already complete");
+}, "switchMap(): result does not complete when the source observable " +
+ "completes, if the inner observable is still active");
+
+test(() => {
+ const source = createTestSubject();
+
+ const e = new Error('thrown from mapper');
+ const result = source.switchMap(() => {
+ throw e;
+ });
+
+ const results = [];
+
+ result.subscribe({
+ next: v => results.push(v),
+ error: e => results.push(e),
+ complete: () => results.push("complete"),
+ });
+
+ assert_equals(source.subscriberCount(), 1,
+ "source observable is subscribed to");
+
+ source.next(1);
+ assert_array_equals(results, [e]);
+ assert_equals(source.subscriberCount(), 0,
+ "source observable is unsubscribed from");
+}, "switchMap(): result emits an error if Mapper callback throws an error");
+
+test(() => {
+ const source = createTestSubject();
+ const inner = createTestSubject();
+
+ const result = source.switchMap(() => inner);
+
+ const results = [];
+
+ result.subscribe({
+ next: v => results.push(v),
+ error: e => results.push(e),
+ complete: () => results.push("complete"),
+ });
+
+ source.next(1);
+ inner.next("a");
+ assert_array_equals(results, ["a"]);
+
+ const e = new Error('error from source');
+ source.error(e);
+ assert_array_equals(results, ["a", e],
+ "switchMap result emits an error if the source emits an error");
+ assert_equals(inner.subscriberCount(), 0,
+ "inner observable is unsubscribed from");
+ assert_equals(source.subscriberCount(), 0,
+ "source observable is unsubscribed from");
+}, "switchMap(): result emits an error if the source observable emits an " +
+ "error");
+
+test(() => {
+ const source = createTestSubject();
+ const inner = createTestSubject();
+
+ const result = source.switchMap(() => inner);
+
+ const results = [];
+
+ result.subscribe({
+ next: v => results.push(v),
+ error: e => results.push(e),
+ complete: () => results.push("complete"),
+ });
+
+ source.next(1);
+ inner.next("a");
+ assert_array_equals(results, ["a"]);
+
+ const e = new Error("error from inner");
+ inner.error(e);
+ assert_array_equals(results, ["a", e],
+ "result emits an error if the inner observable emits an error");
+ assert_equals(inner.subscriberCount(), 0,
+ "inner observable is unsubscribed from");
+ assert_equals(source.subscriberCount(), 0,
+ "source observable is unsubscribed from");
+}, "switchMap(): result emits an error if the inner observable emits an error");
+
+test(() => {
+ const results = [];
+ const source = new Observable(subscriber => {
+ subscriber.next(1);
+ subscriber.addTeardown(() => {
+ results.push('source teardown');
+ });
+ subscriber.signal.onabort = e => {
+ results.push('source onabort');
+ };
+ });
+
+ const inner = new Observable(subscriber => {
+ subscriber.addTeardown(() => {
+ results.push('inner teardown');
+ });
+ subscriber.signal.onabort = () => {
+ results.push('inner onabort');
+ };
+ });
+
+ const result = source.switchMap(() => inner);
+
+ const ac = new AbortController();
+ result.subscribe({
+ next: v => results.push(v),
+ error: e => results.error(e),
+ complete: () => results.complete("complete"),
+ }, {signal: ac.signal});
+
+ ac.abort();
+ assert_array_equals(results, [
+ "source teardown",
+ "source onabort",
+ "inner teardown",
+ "inner onabort",
+ ], "Unsubscription order is correct");
+}, "switchMap(): should unsubscribe in the correct order when user aborts " +
+ "the subscription");
+
+// A helper function to create an Observable that can be externally controlled
+// and examined for testing purposes.
+function createTestSubject() {
+ const subscribers = new Set();
+ const subject = new Observable(subscriber => {
+ subscribers.add(subscriber);
+ subscriber.addTeardown(() => subscribers.delete(subscriber));
+ });
+
+ subject.next = value => {
+ for (const subscriber of Array.from(subscribers)) {
+ subscriber.next(value);
+ }
+ };
+ subject.error = error => {
+ for (const subscriber of Array.from(subscribers)) {
+ subscriber.error(error);
+ }
+ };
+ subject.complete = () => {
+ for (const subscriber of Array.from(subscribers)) {
+ subscriber.complete();
+ }
+ };
+ subject.subscriberCount = () => {
+ return subscribers.size;
+ };
+
+ return subject;
+}
diff --git a/tests/wpt/tests/editing/crashtests/caret-display-list-002.html b/tests/wpt/tests/editing/crashtests/caret-display-list-002.html
new file mode 100644
index 00000000000..23f0496efd6
--- /dev/null
+++ b/tests/wpt/tests/editing/crashtests/caret-display-list-002.html
@@ -0,0 +1,35 @@
+<html class="test-wait reftest-wait">
+<style>
+#a {
+ float: none;
+}
+#c {
+ transition-delay: 1s;
+}
+* {
+ border-style: solid inset dashed;
+ translate: 0px 0px;
+ perspective: 0em;
+ display: inline;
+ float: left;
+ contain: size layout paint;
+}
+</style>
+<script>
+function func_0() {
+ a.focus()
+ b.cellSpacing = "73px"
+ document.execCommand("indent", false)
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ document.documentElement.className = "";
+ }));
+}
+document.addEventListener("DOMContentLoaded", () => {
+ window.ontransitionend = func_0
+ document.execCommand("outdent", false)
+})
+</script>
+<details ontoggle="func_0()" open="">
+<summary id="a" contenteditable="true">
+<table id="b">
+<tr id="c">
diff --git a/tests/wpt/tests/event-timing/interactionid-orphan-pointerup.html b/tests/wpt/tests/event-timing/interactionid-orphan-pointerup.html
new file mode 100644
index 00000000000..9c4a0ecf584
--- /dev/null
+++ b/tests/wpt/tests/event-timing/interactionid-orphan-pointerup.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<meta charset=utf-8 />
+<title>Event Timing: interactionId-orphan-pointerup.</title>
+<button id='testButtonId'>Orphan Pointerup</button>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-actions.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=resources/event-timing-test-utils.js></script>
+
+<script>
+ let observedEntries = [];
+ const map = new Map();
+ const events = ['pointerup'];
+
+ promise_test(async t => {
+ assert_implements(window.PerformanceEventTiming, 'Event Timing is not supported.');
+
+ const callback = (entryList) => { observedEntries = observedEntries.concat(entryList.getEntries().filter(filterAndAddToMap(events, map))); };
+ const readyToResolve = () => { return observedEntries.length >= 1; };
+ const observerPromise = createPerformanceObserverPromise(['event'], callback, readyToResolve);
+
+ await interactAndObserve('orphan-pointerup', document.getElementById('testButtonId'), observerPromise);
+ assert_equals(map.get('pointerup'), 0, 'Should have a trivial interactionId for orphan pointerup event.');
+ }, "Event Timing: Orphan pointerup should not be measured as an interaction.");
+
+</script>
+
+</html> \ No newline at end of file
diff --git a/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js b/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js
index 9670d8d26f8..7b281a4e0e4 100644
--- a/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js
+++ b/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js
@@ -357,6 +357,13 @@ async function pointerdown(target) {
.send();
}
+async function pointerup(target) {
+ const actions = new test_driver.Actions();
+ return actions.addPointer("mousePointer", "mouse")
+ .pointerMove(0, 0, { origin: target })
+ .pointerUp()
+ .send();
+}
async function auxPointerdown(target) {
const actions = new test_driver.Actions();
return actions.addPointer("mousePointer", "mouse")
@@ -443,6 +450,11 @@ async function interactAndObserve(interactionType, target, observerPromise) {
interactionPromise = Promise.all([auxPointerdown(target), pointerdown(target)]);
break;
}
+ case 'orphan-pointerup': {
+ addListeners(target, ['pointerup']);
+ interactionPromise = pointerup(target);
+ break;
+ }
}
return Promise.all([interactionPromise, observerPromise]);
}
diff --git a/tests/wpt/tests/fenced-frame/notify-event-iframe.https.html b/tests/wpt/tests/fenced-frame/notify-event-iframe.https.html
index e91723523ec..1f8e1b8a322 100644
--- a/tests/wpt/tests/fenced-frame/notify-event-iframe.https.html
+++ b/tests/wpt/tests/fenced-frame/notify-event-iframe.https.html
@@ -21,8 +21,10 @@
fencedframe.element.addEventListener('fencedtreeclick', () => notified = true);
await fencedframe.execute(async (frame_type) => {
- window.addEventListener('message', (event) => {
- window.click_error = event.data;
+ window.click_error = new Promise((resolve, reject) => {
+ window.addEventListener('message', (event) => {
+ resolve(event.data);
+ });
});
let iframe = null;
@@ -54,9 +56,10 @@
await multiClick(10, 10, fencedframe.element);
// Ensure the correct exception was thrown.
- await fencedframe.execute(() => {
- assert_equals(window.click_error.name, 'SecurityError');
- assert_equals(window.click_error.message,
+ await fencedframe.execute(async () => {
+ let err = await window.click_error;
+ assert_equals(err.name, 'SecurityError');
+ assert_equals(err.message,
"Failed to execute 'notifyEvent' on 'Fence': notifyEvent is only available in fenced frame roots.");
});
@@ -74,8 +77,10 @@
}, "Test that fenced frame notifyEvent() fails in a nested cross-origin iframe.");
promise_test(async (t) => {
- window.addEventListener('message', (event) => {
- window.click_error = event.data;
+ window.click_error = new Promise((resolve, reject) => {
+ window.addEventListener('message', (event) => {
+ resolve(event.data);
+ });
});
const urn_iframe = await attachIFrameContext(
@@ -95,8 +100,9 @@
await multiClick(10, 10, urn_iframe.element);
- assert_equals(window.click_error.name, 'SecurityError');
- assert_equals(window.click_error.message,
+ let err = await window.click_error
+ assert_equals(err.name, 'SecurityError');
+ assert_equals(err.message,
"Failed to execute 'notifyEvent' on 'Fence': notifyEvent is only available in fenced frame roots.");
}, "Test that notifyEvent() fails in a URN iframe.");
</script>
diff --git a/tests/wpt/tests/fetch/api/request/destination/resources/dummy_video.ogv b/tests/wpt/tests/fetch/api/request/destination/resources/dummy_video.ogv
deleted file mode 100644
index de99616eceb..00000000000
--- a/tests/wpt/tests/fetch/api/request/destination/resources/dummy_video.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/fetch/api/request/request-bad-port.any.js b/tests/wpt/tests/fetch/api/request/request-bad-port.any.js
index b0684d4be0f..5c29823eaa4 100644
--- a/tests/wpt/tests/fetch/api/request/request-bad-port.any.js
+++ b/tests/wpt/tests/fetch/api/request/request-bad-port.any.js
@@ -72,6 +72,7 @@ var BLOCKED_PORTS_LIST = [
2049, // nfs
3659, // apple-sasl
4045, // lockd
+ 4190, // sieve
5060, // sip
5061, // sips
6000, // x11
@@ -81,6 +82,7 @@ var BLOCKED_PORTS_LIST = [
6667, // irc (default)
6668, // irc (alternate)
6669, // irc (alternate)
+ 6679, // osaut
6697, // irc+tls
10080, // amanda
];
diff --git a/tests/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data.tentative.https.html b/tests/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data.tentative.https.html
new file mode 100644
index 00000000000..b583834831f
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/dictionary-clear-site-data.tentative.https.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/compression-dictionary-util.js"></script>
+</head>
+<body>
+<script>
+
+compression_dictionary_promise_test(async (t) => {
+ const dict = await (await fetch(kRegisterDictionaryPath)).text();
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+ // Clear site data.
+ assert_equals(await clearSiteData(/*directive=*/'cache'), 'OK');
+ // Check if `available-dictionary` header is not available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {max_retry: 0}),
+ '"available-dictionary" header is not available');
+}, 'Clear-Site-Data with "cache" directive must unregister dictionary');
+
+compression_dictionary_promise_test(async (t) => {
+ const dict = await (await fetch(kRegisterDictionaryPath)).text();
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+ // Clear site data.
+ assert_equals(await clearSiteData(/*directive=*/'cookies'), 'OK');
+ // Check if `available-dictionary` header is not available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {max_retry: 0}),
+ '"available-dictionary" header is not available');
+}, 'Clear-Site-Data with "cookies" directive must unregister dictionary');
+
+compression_dictionary_promise_test(async (t) => {
+ const dict = await (await fetch(kRegisterDictionaryPath)).text();
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+ // Clear site data.
+ assert_equals(await clearSiteData(/*directive=*/'storage'), 'OK');
+ // Check if `available-dictionary` header is not available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {max_retry: 0}),
+ kDefaultDictionaryHashBase64);
+}, 'Clear-Site-Data with "storage" directive must not unregister dictionary');
+
+</script>
+</body>
diff --git a/tests/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html b/tests/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html
new file mode 100644
index 00000000000..cd206258160
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="./resources/compression-dictionary-util.js"></script>
+</head>
+<body>
+<script>
+
+compression_dictionary_promise_test(async (t) => {
+ const dict = await (await fetch(kRegisterDictionaryPath)).text();
+ assert_equals(dict, kDefaultDictionaryContent);
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+
+ // Check if the data compressed using Brotli with the dictionary can be
+ // decompressed.
+ const data_url = `${kCompressedDataPath}?content_encoding=br-d`;
+ assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData);
+}, 'Decompresion using Brotli with the dictionary works as expected');
+
+compression_dictionary_promise_test(async (t) => {
+ const dict = await (await fetch(kRegisterDictionaryPath)).text();
+ assert_equals(dict, kDefaultDictionaryContent);
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+
+ // Check if the data compressed using Zstandard with the dictionary can be
+ // decompressed.
+ const data_url = `${kCompressedDataPath}?content_encoding=zstd-d`;
+ assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData);
+}, 'Decompresion using Zstandard with the dictionary works as expected');
+
+compression_dictionary_promise_test(async (t) => {
+ const dict =
+ await (await fetch(getRemoteHostUrl(kRegisterDictionaryPath))).text();
+ assert_equals(dict, kDefaultDictionaryContent);
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {check_remote: true}),
+ kDefaultDictionaryHashBase64);
+
+ // Check if the data compressed using Brotli with the dictionary can be
+ // decompressed.
+ const data_url =
+ getRemoteHostUrl(`${kCompressedDataPath}?content_encoding=br-d`);
+ assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData);
+}, 'Decompresion of a cross origin resource works as expected');
+
+</script>
+</body>
diff --git a/tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html b/tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html
new file mode 100644
index 00000000000..71a9b1c0509
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/compression-dictionary-util.js"></script>
+</head>
+<body>
+<script>
+
+function addLinkRelDictionaryElement(url, crossOrigin) {
+ const link = document.createElement('link');
+ link.rel = 'dictionary';
+ link.href = url;
+ if (crossOrigin) {
+ link.crossOrigin = crossOrigin;
+ }
+ document.head.appendChild(link);
+}
+
+test(t => {
+ assert_true(document.createElement('link').relList.supports('dictionary'));
+ }, "Browser supports link element with dictionary rel.");
+
+compression_dictionary_promise_test(async (t) => {
+ const dict_token = token();
+ const url = `${kRegisterDictionaryPath}?save_header=${dict_token}`;
+ addLinkRelDictionaryElement(url);
+ // Wait for a while to ensure that the dictionary is fetched.
+ await new Promise(resolve => window.requestIdleCallback(resolve));
+ const headers = await waitUntilPreviousRequestHeaders(t, dict_token);
+ assert_true(headers !== undefined, 'Headers should be available');
+ assert_equals(headers['sec-fetch-mode'], 'cors');
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+ // Check if the data compressed using Brotli with the dictionary can be
+ // decompressed.
+ const data_url = `${kCompressedDataPath}?content_encoding=br-d`;
+ assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData);
+}, 'Fetch same origin dictionary using link element');
+
+compression_dictionary_promise_test(async (t) => {
+ const dict_token = token();
+ const url =
+ getRemoteHostUrl(`${kRegisterDictionaryPath}?save_header=${dict_token}`);
+ addLinkRelDictionaryElement(url, 'anonymous');
+ // Wait for a while to ensure that the dictionary is fetched.
+ await new Promise(resolve => window.requestIdleCallback(resolve));
+ const headers = await waitUntilPreviousRequestHeaders(
+ t, dict_token, /*check_remote=*/ true);
+ assert_true(headers !== undefined, 'Headers should be available');
+ assert_equals(headers['sec-fetch-mode'], 'cors');
+
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {check_remote: true}),
+ kDefaultDictionaryHashBase64);
+ // Check if the data compressed using Brotli with the dictionary can be
+ // decompressed.
+ const data_url =
+ getRemoteHostUrl(`${kCompressedDataPath}?content_encoding=br-d`);
+ assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData);
+}, 'Fetch cross origin dictionary using link element');
+
+</script>
+</body>
diff --git a/tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html b/tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html
new file mode 100644
index 00000000000..a3ffd8ba74a
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="./resources/compression-dictionary-util.js"></script>
+</head>
+<body>
+<script>
+
+async function addIframeWithLinkRelDictionaryHeader(dict_url) {
+ return new Promise((resolve) => {
+ const base_page_url = './resources/empty.html';
+ const page_url =
+ base_page_url + `?pipe=header(link,<${dict_url}>; rel="dictionary")`;
+ const iframe = document.createElement('iframe');
+ iframe.src = page_url;
+ iframe.addEventListener('load', () => {
+ resolve(iframe);
+ });
+ document.body.appendChild(iframe);
+ })
+}
+
+compression_dictionary_promise_test(async (t) => {
+ const dict_token = token();
+ const url = new URL(
+ `${kRegisterDictionaryPath}?save_header=${dict_token}`, location.href);
+ const iframe = await addIframeWithLinkRelDictionaryHeader(url.href);
+ t.add_cleanup(() => {
+ iframe.remove();
+ });
+ // Wait for a while to ensure that the dictionary is fetched.
+ await new Promise(resolve => window.requestIdleCallback(resolve));
+ const headers = await waitUntilPreviousRequestHeaders(t, dict_token);
+ assert_true(headers !== undefined, 'Headers should be available');
+ assert_equals(headers['sec-fetch-mode'], 'cors');
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+ // Check if the data compressed using Brotli with the dictionary can be
+ // decompressed.
+ const data_url = `${kCompressedDataPath}?content_encoding=br-d`;
+ assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData);
+}, 'Fetch same origin dictionary using link header');
+
+</script>
+</body>
diff --git a/tests/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html b/tests/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html
new file mode 100644
index 00000000000..7921b129466
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/compression-dictionary-util.js"></script>
+</head>
+<body>
+<script>
+
+compression_dictionary_promise_test(async (t) => {
+ const dict = await (await fetch(kRegisterDictionaryPath)).text();
+ assert_equals(dict, kDefaultDictionaryContent);
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+}, 'Simple dictionary registration and unregistration');
+
+compression_dictionary_promise_test(async (t) => {
+ const dict = await (await fetch(`${kRegisterDictionaryPath}?id=test`)).text();
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+ assert_equals((await checkHeaders())['dictionary-id'], '"test"');
+}, 'Dictionary registration with dictionary ID');
+
+compression_dictionary_promise_test(async (t) => {
+ // Registers a first dictionary.
+ const dictionary_path1 = `${kRegisterDictionaryPath}?id=id1`;
+ const dict1 = await (await fetch(dictionary_path1)).text();
+ // Wait until `available-dictionary` header is available.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(t, {}),
+ kDefaultDictionaryHashBase64);
+ // Check the `dictionary-id` header.
+ assert_equals((await checkHeaders())['dictionary-id'], '"id1"');
+
+ // Registers a second dictionary.
+ const kAlternativeDictionaryContent =
+ 'This is an alternative test dictionary.';
+ const dictionary_path2 =
+ `${kRegisterDictionaryPath}?content=${kAlternativeDictionaryContent}&id=id2`;
+ const expected_dictionary_header =
+ await calculateDictionaryHash(kAlternativeDictionaryContent);
+ const dict2 = await (await fetch(dictionary_path2)).text();
+ assert_equals(dict2, kAlternativeDictionaryContent);
+ // Wait until `available-dictionary` header is available.
+ // Note: Passing `expected_header` to ignore the old dictionary.
+ assert_equals(
+ await waitUntilAvailableDictionaryHeader(
+ t, {expected_header: expected_dictionary_header}),
+ expected_dictionary_header);
+ // Check the `dictionary-id` header.
+ assert_equals((await checkHeaders())['dictionary-id'], '"id2"');
+}, 'New dictionary registration overrides the existing one');
+
+</script>
+</body>
diff --git a/tests/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py b/tests/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py
new file mode 100644
index 00000000000..0db51bf797f
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/resources/clear-site-data.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ directive = request.GET.first(b"directive")
+ response.headers.set(b"Clear-Site-Data", b"\"" + directive + b"\"")
+ return b"OK"
diff --git a/tests/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py b/tests/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py
new file mode 100644
index 00000000000..4be4b555640
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/resources/compressed-data.py
@@ -0,0 +1,28 @@
+def main(request, response):
+ response.headers.set(b"Access-Control-Allow-Origin", b"*")
+ response.headers.set(b"Content-Type", b"text/plain")
+ response.headers.set(
+ b"Content-Dictionary",
+ b":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:")
+
+ # `br_d_data` and `zstd_d_data` are generated using the following commands:
+ #
+ # $ echo "This is a test dictionary." > /tmp/dict
+ # $ echo -n "This is compressed test data using a test dictionary" \
+ # > /tmp/data
+ # $ brotli -o /tmp/out.brd -D /tmp/dict /tmp/data
+ # $ xxd -p /tmp/out.brd | tr -d '\n' | sed 's/\(..\)/\\x\1/g'
+ br_d_data = b"\xa1\x98\x01\x80\x62\xa4\x4c\x1d\xdf\x12\x84\x8c\xae\xc2\xca\x60\x22\x07\x6e\x81\x05\x14\xc9\xb7\xc3\x44\x8e\xbc\x16\xe0\x15\x0e\xec\xc1\xee\x34\x33\x3e\x0d"
+ # $ zstd -o /tmp/out.zstdd -D /tmp/dict /tmp/data
+ # $ xxd -p /tmp/out.zstdd | tr -d '\n' | sed 's/\(..\)/\\x\1/g'
+ zstd_d_data = b"\x28\xb5\x2f\xfd\x24\x34\xf5\x00\x00\x98\x63\x6f\x6d\x70\x72\x65\x73\x73\x65\x64\x61\x74\x61\x20\x75\x73\x69\x6e\x67\x03\x00\x59\xf9\x73\x54\x46\x27\x26\x10\x9e\x99\xf2\xbc"
+
+ if b'content_encoding' in request.GET:
+ content_encoding = request.GET.first(b"content_encoding")
+ response.headers.set(b"Content-Encoding", content_encoding)
+ if content_encoding == b"br-d":
+ # Send the pre compressed file
+ response.content = br_d_data
+ if content_encoding == b"zstd-d":
+ # Send the pre compressed file
+ response.content = zstd_d_data
diff --git a/tests/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js b/tests/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js
new file mode 100644
index 00000000000..46d95041d8c
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js
@@ -0,0 +1,120 @@
+
+const kDefaultDictionaryContent = 'This is a test dictionary.\n';
+const kDefaultDictionaryHashBase64 =
+ ':U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:';
+const kRegisterDictionaryPath = './resources/register-dictionary.py';
+const kCompressedDataPath = './resources/compressed-data.py';
+const kExpectedCompressedData =
+ `This is compressed test data using a test dictionary`;
+const kCheckAvailableDictionaryHeaderMaxRetry = 5;
+const kCheckAvailableDictionaryHeaderRetryTimeout = 100;
+const kCheckPreviousRequestHeadersMaxRetry = 5;
+const kCheckPreviousRequestHeadersRetryTimeout = 250;
+
+// Gets the remote URL corresponding to `relative_path`.
+function getRemoteHostUrl(relative_path) {
+ const remote_origin = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ let result = new URL(relative_path, location.href);
+ result.protocol = remote_origin.protocol;
+ result.hostname = remote_origin.hostname;
+ result.port = remote_origin.port;
+ return result.href;
+}
+
+// Calculates the Structured Field Byte Sequence containing the SHA-256 hash of
+// the contents of the dictionary text.
+async function calculateDictionaryHash(dictionary_text) {
+ const encoded = (new TextEncoder()).encode(dictionary_text);
+ const digest = await crypto.subtle.digest('SHA-256', encoded)
+ return ':' + btoa(String.fromCharCode(...new Uint8Array(digest))) + ':';
+}
+
+// Checks the HTTP request headers which is sent to the server.
+async function checkHeaders(check_remote = false) {
+ let url = './resources/echo-headers.py';
+ if (check_remote) {
+ url = getRemoteHostUrl(url);
+ }
+ return await (await fetch(url)).json();
+}
+
+// Checks the "available-dictionary" header in the HTTP request headers.
+async function checkAvailableDictionaryHeader(check_remote = false) {
+ return (await checkHeaders(check_remote))['available-dictionary'];
+}
+
+// Waits until the "available-dictionary" header is available in the HTTP
+// request headers, and returns the header. If the header is not available after
+// the specified number of retries, returns an error message. If the
+// `expected_header` is specified, this method waits until the header is
+// available and matches the `expected_header`.
+async function waitUntilAvailableDictionaryHeader(test, {
+ max_retry = kCheckAvailableDictionaryHeaderMaxRetry,
+ expected_header = undefined,
+ check_remote = false
+}) {
+ for (let retry_count = 0; retry_count <= max_retry; retry_count++) {
+ const header = await checkAvailableDictionaryHeader(check_remote);
+ if (header) {
+ if (expected_header === undefined || header == expected_header) {
+ return header;
+ }
+ }
+ await new Promise(
+ (resolve) => test.step_timeout(
+ resolve, kCheckAvailableDictionaryHeaderRetryTimeout));
+ }
+ return '"available-dictionary" header is not available';
+}
+
+// Checks the HTTP request headers which was sent to the server with `token`
+// to register a dictionary.
+async function checkPreviousRequestHeaders(token, check_remote = false) {
+ let url = `./resources/register-dictionary.py?get_previous_header=${token}`;
+ if (check_remote) {
+ url = getRemoteHostUrl(url);
+ }
+ return await (await fetch(url)).json();
+}
+
+// Waits until the HTTP request headers which was sent to the server with
+// `token` to register a dictionary is available, and returns the header. If the
+// header is not available after the specified number of retries, returns
+// `undefined`.
+async function waitUntilPreviousRequestHeaders(
+ test, token, check_remote = false) {
+ for (let retry_count = 0; retry_count <= kCheckPreviousRequestHeadersMaxRetry;
+ retry_count++) {
+ const header =
+ (await checkPreviousRequestHeaders(token, check_remote))['headers'];
+ if (header) {
+ return header;
+ }
+ await new Promise(
+ (resolve) => test.step_timeout(
+ resolve, kCheckPreviousRequestHeadersRetryTimeout));
+ }
+ return undefined;
+}
+
+// Clears the site data for the specified directive by sending a request to
+// `./resources/clear-site-data.py` which returns `Clear-Site-Data` response
+// header.
+// Note: When `directive` is 'cache' or 'cookies' is specified, registered
+// compression dictionaries should be also cleared.
+async function clearSiteData(directive = 'cache') {
+ return await (await fetch(
+ `./resources/clear-site-data.py?directive=${directive}`))
+ .text();
+}
+
+// A utility test method that adds the `clearSiteData()` method to the
+// testharness cleanup function. This is intended to ensure that registered
+// dictionaries are cleared in tests and that registered dictionaries do not
+// interfere with subsequent tests.
+function compression_dictionary_promise_test(func, name, properties) {
+ promise_test(async (test) => {
+ test.add_cleanup(clearSiteData);
+ await func(test);
+ }, name, properties);
+}
diff --git a/tests/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py b/tests/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py
new file mode 100644
index 00000000000..aabd99eb101
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/resources/echo-headers.py
@@ -0,0 +1,10 @@
+import json
+
+def main(request, response):
+ response.headers.set(b"Access-Control-Allow-Origin", b"*")
+ headers = {}
+ for header in request.headers:
+ key = header.decode('utf-8')
+ value = request.headers.get(header).decode('utf-8')
+ headers[key] = value
+ return json.dumps(headers)
diff --git a/tests/wpt/tests/fetch/compression-dictionary/resources/empty.html b/tests/wpt/tests/fetch/compression-dictionary/resources/empty.html
new file mode 100644
index 00000000000..0e76edd65b7
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/resources/empty.html
@@ -0,0 +1 @@
+<!DOCTYPE html>
diff --git a/tests/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py b/tests/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py
new file mode 100644
index 00000000000..0bd57225ef1
--- /dev/null
+++ b/tests/wpt/tests/fetch/compression-dictionary/resources/register-dictionary.py
@@ -0,0 +1,37 @@
+import json
+
+def main(request, response):
+ response.headers.set(b"Access-Control-Allow-Origin", b"*")
+ match = b"/fetch/compression-dictionary/resources/*"
+ content = b"This is a test dictionary.\n"
+ if b"match" in request.GET:
+ match = request.GET.first(b"match")
+ if b"content" in request.GET:
+ content = request.GET.first(b"content")
+
+ token = request.GET.first(b"save_header", None)
+ if token is not None:
+ headers = {}
+ for header in request.headers:
+ key = header.decode('utf-8')
+ value = request.headers.get(header).decode('utf-8')
+ headers[key] = value
+ with request.server.stash.lock:
+ request.server.stash.put(token, json.dumps(headers))
+
+ previous_token = request.GET.first(b"get_previous_header", None)
+ if previous_token is not None:
+ result = {}
+ with request.server.stash.lock:
+ store = request.server.stash.take(previous_token)
+ if store is not None:
+ headers = json.loads(store)
+ result["headers"] = headers
+ return json.dumps(result)
+
+ options = b"match=\"" + match + b"\""
+ if b"id" in request.GET:
+ options += b", id=\"" + request.GET.first(b"id") + b"\""
+ response.headers.set(b"Use-As-Dictionary", options)
+ response.headers.set(b"Cache-Control", b"max-age=3600")
+ return content
diff --git a/tests/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js b/tests/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js
new file mode 100644
index 00000000000..43ea90a336c
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/bad-br-body.https.any.js
@@ -0,0 +1,12 @@
+// META: global=window
+
+[
+ "arrayBuffer",
+].forEach(method => {
+ promise_test(t => {
+ return fetch("resources/bad-br-body.py").then(res => {
+ assert_equals(res.status, 200);
+ return promise_rejects_js(t, TypeError, res[method]());
+ });
+ }, "Consuming the body of a resource with bad br content with " + method + "() should reject");
+});
diff --git a/tests/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js b/tests/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js
new file mode 100644
index 00000000000..1427dd73026
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/big-br-body.https.any.js
@@ -0,0 +1,55 @@
+// META: global=window,worker
+
+const EXPECTED_SIZE = 27000000;
+const EXPECTED_SHA256 = [
+ 74, 100, 37, 243, 147, 61, 116, 60, 241, 221, 126,
+ 18, 24, 71, 204, 28, 50, 62, 201, 130, 152, 225,
+ 217, 183, 10, 201, 143, 214, 102, 155, 212, 248,
+ ];
+
+promise_test(async () => {
+ const response = await fetch('resources/big.text.br');
+ assert_true(response.ok);
+ const arrayBuffer = await response.arrayBuffer();
+ assert_equals(arrayBuffer.byteLength, EXPECTED_SIZE,
+ 'uncompressed size should match');
+ const sha256 = await crypto.subtle.digest('SHA-256', arrayBuffer);
+ assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256,
+ 'digest should match');
+}, 'large br data should be decompressed successfully');
+
+promise_test(async () => {
+ const response = await fetch('resources/big.text.br');
+ assert_true(response.ok);
+ const reader = response.body.getReader({mode: 'byob'});
+ let offset = 0;
+ // Pre-allocate space for the output. The response body will be read
+ // chunk-by-chunk into this array.
+ let ab = new ArrayBuffer(EXPECTED_SIZE);
+ while (offset < EXPECTED_SIZE) {
+ // To stress the data pipe, we want to use a different size read each
+ // time. Unfortunately, JavaScript doesn't have a seeded random number
+ // generator, so this creates the possibility of making this test flaky if
+ // it doesn't work for some edge cases.
+ let size = Math.floor(Math.random() * 65535 + 1);
+ if (size + offset > EXPECTED_SIZE) {
+ size = EXPECTED_SIZE - offset;
+ }
+ const u8 = new Uint8Array(ab, offset, size);
+ const { value, done } = await reader.read(u8);
+ ab = value.buffer;
+ // Check that we got our original array back.
+ assert_equals(ab.byteLength, EXPECTED_SIZE,
+ 'backing array should be the same size');
+ assert_equals(offset, value.byteOffset, 'offset should match');
+ assert_less_than_equal(value.byteLength, size,
+ 'we should not have got more than we asked for');
+ offset = value.byteOffset + value.byteLength;
+ if (done) break;
+ }
+ assert_equals(offset, EXPECTED_SIZE,
+ 'we should have read the whole thing');
+ const sha256 = await crypto.subtle.digest('SHA-256', new Uint8Array(ab));
+ assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256,
+ 'digest should match');
+}, 'large br data should be decompressed successfully with byte stream');
diff --git a/tests/wpt/tests/fetch/content-encoding/br/br-body.https.any.js b/tests/wpt/tests/fetch/content-encoding/br/br-body.https.any.js
new file mode 100644
index 00000000000..2c2dbb5d293
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/br-body.https.any.js
@@ -0,0 +1,15 @@
+// META: global=window,worker
+
+const expectedDecompressedSize = 10500;
+[
+ "text",
+ "octetstream"
+].forEach(contentType => {
+ promise_test(async t => {
+ let response = await fetch(`resources/foo.${contentType}.br`);
+ assert_true(response.ok);
+ let arrayBuffer = await response.arrayBuffer()
+ let u8 = new Uint8Array(arrayBuffer);
+ assert_equals(u8.length, expectedDecompressedSize);
+ }, `fetched br data with content type ${contentType} should be decompressed.`);
+});
diff --git a/tests/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py b/tests/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py
new file mode 100644
index 00000000000..0710e7ffdee
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/resources/bad-br-body.py
@@ -0,0 +1,3 @@
+def main(request, response):
+ headers = [(b"Content-Encoding", b"br")]
+ return headers, b"not actually br"
diff --git a/tests/wpt/tests/fetch/content-encoding/br/resources/big.text.br b/tests/wpt/tests/fetch/content-encoding/br/resources/big.text.br
new file mode 100644
index 00000000000..b3a530d757d
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/resources/big.text.br
Binary files differ
diff --git a/tests/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers b/tests/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers
new file mode 100644
index 00000000000..aba00bd5d4a
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/resources/big.text.br.headers
@@ -0,0 +1,3 @@
+Content-type: text/plain
+Content-Encoding: br
+Cache-Control: no-store
diff --git a/tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br
new file mode 100644
index 00000000000..30cb2f7095e
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br
Binary files differ
diff --git a/tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers
new file mode 100644
index 00000000000..c0c19bc82af
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers
@@ -0,0 +1,2 @@
+Content-type: application/octet-stream
+Content-Encoding: br
diff --git a/tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br
new file mode 100644
index 00000000000..30cb2f7095e
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br
Binary files differ
diff --git a/tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers
new file mode 100644
index 00000000000..8c03b823e09
--- /dev/null
+++ b/tests/wpt/tests/fetch/content-encoding/br/resources/foo.text.br.headers
@@ -0,0 +1,2 @@
+Content-type: text/plain
+Content-Encoding: br
diff --git a/tests/wpt/tests/fledge/tentative/additional-bids.https.window.js b/tests/wpt/tests/fledge/tentative/additional-bids.https.window.js
index 0e1d22c2619..965f9a60c7e 100644
--- a/tests/wpt/tests/fledge/tentative/additional-bids.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/additional-bids.https.window.js
@@ -10,15 +10,8 @@
// This file contains tests for additional bids and negative targeting.
//
// TODO:
-// - test that an negatively targeted additional bid is suppressed.
-// - test that an incorrectly signed additional bid is not negative targeted.
-// - test that an missing-signature additional bid is not negative targeted.
-// - test that an additional bid with some correct signatures can be negative.
-// negative targeted for those negative interest groups whose signatures
-// match.
-// - test an additional bid with multiple negative interest groups.
-// - test that multiple negative interest groups with mismatched joining origins
-// is not negative targeted.
+// - test that an additional bid with some correct signatures can be negative
+// targeted for those negative interest groups whose signatures match.
// - test that additional bids can be fetched using an iframe navigation.
// - test that additional bids are not fetched using an iframe navigation for
// which the `adAuctionHeaders=true` attribute is not specified.
@@ -26,12 +19,19 @@
// `adAuctionHeaders: true` is not specified.
// - test that an additional bid with an incorrect auction nonce is not used
// included in an auction. Same for seller and top-level seller.
+// - lots of tests for different types of malformed additional bids, e.g.
+// missing fields, malformed signature, invalid currency code,
+// missing joining origin for multiple negative interest groups, etc.
// - test that correctly formatted additional bids are included in an auction
// when fetched alongside malformed additional bid headers by a Fetch
-// request.
+// request (both invalid headers and invalid additional bids)
+// - test that an additional bid is rejected if its from a buyer who is not
+// allowed to participate in the auction.
+// - test that an additional bid is rejected if its currency doesn't match the
+// buyer's associated per-buyer currency from the auction config.
// - test that correctly formatted additional bids are included in an auction
// when fetched alongside malformed additional bid headers by an iframe
-// navigation.
+// navigation (both invalid headers and invalid additional bids).
// - test that reportWin is not used for reporting an additional bid win.
// - test that additional bids can *not* be fetched from iframe subresource
// requests.
@@ -80,6 +80,9 @@
// for ad auction headers interception to associate it with this auction.
const SINGLE_SELLER_AUCTION_SELLER = window.location.origin;
+const ADDITIONAL_BID_SECRET_KEY = 'nWGxne/9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A=';
+const ADDITIONAL_BID_PUBLIC_KEY = '11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=';
+
// Single-seller auction with a single buyer who places a single additional
// bid. As the only bid, this wins.
subsetTest(promise_test, async test => {
@@ -88,12 +91,12 @@ subsetTest(promise_test, async test => {
const seller = SINGLE_SELLER_AUCTION_SELLER;
const buyer = OTHER_ORIGIN1;
- const additionalBid = createAdditionalBid(
+ const additionalBid = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer, 'horses', 1.99);
await runAdditionalBidTest(
test, uuid, [buyer], auctionNonce,
- fetchAdditionalBids(seller, [additionalBid]),
+ additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]),
/*highestScoringOtherBid=*/0,
/*winningAdditionalBidId=*/'horses');
}, 'single valid additional bid');
@@ -105,16 +108,17 @@ subsetTest(promise_test, async test => {
const seller = SINGLE_SELLER_AUCTION_SELLER;
const buyer1 = OTHER_ORIGIN1;
- const additionalBid1 = createAdditionalBid(
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
const buyer2 = OTHER_ORIGIN2;
- const additionalBid2 = createAdditionalBid(
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
await runAdditionalBidTest(
test, uuid, [buyer1, buyer2], auctionNonce,
- fetchAdditionalBids(seller, [additionalBid1, additionalBid2]),
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
/*highestScoringOtherBid=*/1.99,
/*winningAdditionalBidId=*/'planes');
}, 'two valid additional bids');
@@ -127,20 +131,241 @@ subsetTest(promise_test, async test => {
const seller = SINGLE_SELLER_AUCTION_SELLER;
const buyer1 = OTHER_ORIGIN1;
- const additionalBid1 = createAdditionalBid(
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
const buyer2 = OTHER_ORIGIN2;
- const additionalBid2 = createAdditionalBid(
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
-
await runAdditionalBidTest(
test, uuid, [buyer1, buyer2], auctionNonce,
Promise.all([
- fetchAdditionalBids(seller, [additionalBid1]),
- fetchAdditionalBids(seller, [additionalBid2])
+ additionalBidHelper.fetchAdditionalBids(seller, [additionalBid1]),
+ additionalBidHelper.fetchAdditionalBids(seller, [additionalBid2])
]),
/*highestScoringOtherBid=*/1.99,
/*winningAdditionalBidId=*/'planes');
}, 'two valid additional bids from two distinct Fetch requests');
+
+// Single-seller auction with a single additional bid. Because this additional
+// bid is filtered by negative targeting, this auction has no winner.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer = OTHER_ORIGIN1;
+ const additionalBid = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid, negativeInterestGroupName);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runBasicFledgeTestExpectingNoWinner(
+ test, uuid,
+ { interestGroupBuyers: [buyer],
+ auctionNonce: auctionNonce,
+ additionalBids: additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid])});
+}, 'one additional bid filtered by negative targeting, so auction has no ' +
+ 'winner');
+
+// Single-seller auction with a two buyers competing with additional bids.
+// The higher of these has a negative interest group specified, and that
+// negative interest group has been joined, so the lower bid wins.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid2, negativeInterestGroupName);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/0,
+ /*winningAdditionalBidId=*/'horses');
+}, 'higher additional bid is filtered by negative targeting, so ' +
+ 'lower additional bid win');
+
+// Same as above, except that the bid is missing a signature, so that the
+// negative targeting interest group is ignored, and the higher bid, which
+// would have otherwise been filtered by negative targeting, wins.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid2, negativeInterestGroupName);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'higher additional bid is filtered by negative targeting, but it is ' +
+ 'missing a signature, so it still wins');
+
+// Same as above, except that the bid is signed incorrectly, so that the
+// negative targeting interest group is ignored, and the higher bid, which
+// would have otherwise been filtered by negative targeting, wins.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid2, negativeInterestGroupName);
+ additionalBidHelper.incorrectlySignWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'higher additional bid is filtered by negative targeting, but it has an ' +
+ 'invalid signature, so it still wins');
+
+// A test of an additional bid with multiple negative interest groups.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName1 = 'already-owns-a-plane';
+ const negativeInterestGroupName2 = 'another-negative-interest-group';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroups(
+ additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2],
+ /*joiningOrigin=*/window.location.origin);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/0,
+ /*winningAdditionalBidId=*/'horses');
+}, 'higher additional bid is filtered by negative targeting by two negative ' +
+ 'interest groups, and since one is on the device, the lower bid wins');
+
+// Same as above, but with a mismatched joining origin.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName1 = 'already-owns-a-plane';
+ const negativeInterestGroupName2 = 'another-negative-interest-group';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroups(
+ additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2],
+ /*joiningOrigin=*/OTHER_ORIGIN1);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'higher additional bid is filtered by negative targeting by two negative ' +
+ 'interest groups, but because of a joining origin mismatch, it still wins');
+
+// Ensure that trusted seller signals are retrieved for additional bids.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const buyer = OTHER_ORIGIN1;
+ const additionalBid = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer, 'horses', 1.99);
+
+ let renderURL = createRenderURL(uuid);
+ await runBasicFledgeTestExpectingWinner(
+ test, uuid,
+ { interestGroupBuyers: [buyer],
+ auctionNonce: auctionNonce,
+ additionalBids: additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid]),
+ decisionLogicURL: createDecisionScriptURL(
+ uuid,
+ { scoreAd:
+ `if(!"${renderURL}" in trustedScoringSignals.renderURL) ` +
+ 'throw "missing trusted signals";'}),
+ trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL});
+}, 'trusted seller signals retrieved for additional bids');
diff --git a/tests/wpt/tests/fledge/tentative/auction-config.https.window.js b/tests/wpt/tests/fledge/tentative/auction-config.https.window.js
index 8fbdc95dfc5..5fa4fa252fd 100644
--- a/tests/wpt/tests/fledge/tentative/auction-config.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/auction-config.https.window.js
@@ -10,7 +10,9 @@
// META: variant=?21-25
// META: variant=?26-30
// META: variant=?31-35
-// META: variant=?36-last
+// META: variant=?36-40
+// META: variant=?40-45
+// META: variant=?46-last
"use strict;"
@@ -86,6 +88,14 @@ const EXPECT_NO_WINNER = auctionResult => {
assert_equals(auctionResult, null, 'Auction unexpected had a winner');
};
+// Expect a winner (FencedFrameConfig).
+const EXPECT_WINNER =
+ auctionResult => {
+ assert_true(
+ auctionResult instanceof FencedFrameConfig,
+ 'Auction did not return expected FencedFrameConfig');
+ }
+
// Expect an exception of the given type.
const EXPECT_EXCEPTION = exceptionType => auctionResult => {
assert_not_equals(auctionResult, null, "got null instead of expected error");
@@ -130,6 +140,50 @@ makeTest({
});
makeTest({
+ name: 'valid trustedScoringSignalsURL',
+ expect: EXPECT_WINNER,
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL should not have a fragment',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json#foo'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL with an empty fragment is not OK',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json#'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL should not have a query',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json?foo'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL with an empty query is not OK',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json?'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL should not have embedded credentials',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {
+ trustedScoringSignalsURL: (window.location.origin + '/resource.json')
+ .replace('https://', 'https://user:pass@')
+ }
+});
+
+makeTest({
name: 'trustedScoringSignalsURL is cross-origin with seller',
expect: EXPECT_EXCEPTION(TypeError),
auctionConfigOverrides: { trustedScoringSignalsURL: "https://example.com" },
@@ -420,9 +474,9 @@ subsetTest(promise_test, async test => {
let bid = browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
return {bid: bid, render: '${renderURL}'};`,
reportWin: `
- if (browserSignals.bid == 1)
+ if (browserSignals.bid === 1)
sendReportTo('${bidderReportURL1}');
- if (browserSignals.bid == 2)
+ if (browserSignals.bid === 2)
sendReportTo('${bidderReportURL2}');`
})
@@ -443,9 +497,9 @@ subsetTest(promise_test, async test => {
browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
return {desirability: desirability};`,
reportResult: `
- if (browserSignals.desirability == 1)
+ if (browserSignals.desirability === 1)
sendReportTo('${sellerReportURL1}');
- if (browserSignals.desirability == 2)
+ if (browserSignals.desirability === 2)
sendReportTo('${sellerReportURL2}');`
})
};
diff --git a/tests/wpt/tests/fledge/tentative/component-ads.https.window.js b/tests/wpt/tests/fledge/tentative/component-ads.https.window.js
index 7e98570b9ef..6b22585d578 100644
--- a/tests/wpt/tests/fledge/tentative/component-ads.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/component-ads.https.window.js
@@ -61,7 +61,7 @@ async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGr
`// "status" is passed to the beacon URL, to be verified by waitForObservedRequests().
let status = "ok";
const componentAds = window.fence.getNestedConfigs()
- if (componentAds.length != 40)
+ if (componentAds.length !== 40)
status = "unexpected getNestedConfigs() length";
for (let i of ${JSON.stringify(componentAdsToLoad)}) {
let fencedFrame = document.createElement("fencedframe");
@@ -144,7 +144,7 @@ subsetTest(promise_test, async test => {
const nestedConfigsLength = window.fence.getNestedConfigs().length
// "getNestedConfigs()" should return a list of 40 configs, to avoid leaking
// whether there were any component URLs to the page.
- if (nestedConfigsLength != 40)
+ if (nestedConfigsLength !== 40)
status = "unexpected getNestedConfigs() length: " + nestedConfigsLength;
window.fence.reportEvent({eventType: "beacon",
eventData: status,
diff --git a/tests/wpt/tests/fledge/tentative/component-auction.https.window.js b/tests/wpt/tests/fledge/tentative/component-auction.https.window.js
index c70532024ce..015c20a5c24 100644
--- a/tests/wpt/tests/fledge/tentative/component-auction.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/component-auction.https.window.js
@@ -685,7 +685,7 @@ subsetTest(promise_test, async test => {
auctionConfig.componentAuctions[0].decisionLogicURL =
createDecisionScriptURL(
uuid,
- { scoreAd: `if (browserSignals.renderURL != '${renderURL1}')
+ { scoreAd: `if (browserSignals.renderURL !== '${renderURL1}')
throw 'Wrong ad';`,
reportResult: `sendReportTo('${seller1ReportURL}');`}
);
@@ -696,7 +696,7 @@ subsetTest(promise_test, async test => {
decisionLogicURL: createDecisionScriptURL(
uuid,
{ origin: OTHER_ORIGIN1,
- scoreAd: `if (browserSignals.renderURL != '${renderURL2}')
+ scoreAd: `if (browserSignals.renderURL !== '${renderURL2}')
throw 'Wrong ad';`,
reportResult: `sendReportTo('${seller2ReportURL}');`}
)
diff --git a/tests/wpt/tests/fledge/tentative/cross-origin.https.window.js b/tests/wpt/tests/fledge/tentative/cross-origin.https.window.js
index a8cf93049fa..eed74c522f2 100644
--- a/tests/wpt/tests/fledge/tentative/cross-origin.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/cross-origin.https.window.js
@@ -356,7 +356,7 @@ subsetTest(promise_test, async test => {
throw "Wrong origin: " + interestGroup.owner;
if (!interestGroup.biddingLogicURL.startsWith("${bidderOrigin}"))
throw "Wrong origin: " + interestGroup.biddingLogicURL;
- if (interestGroup.ads[0].renderURL != "${renderURL}")
+ if (interestGroup.ads[0].renderURL !== "${renderURL}")
throw "Wrong renderURL: " + interestGroup.ads[0].renderURL;
if (browserSignals.seller !== "${sellerOrigin}")
throw "Wrong origin: " + browserSignals.seller;`,
diff --git a/tests/wpt/tests/fledge/tentative/currency.https.window.js b/tests/wpt/tests/fledge/tentative/currency.https.window.js
index 9a33d12148a..99943cecbf9 100644
--- a/tests/wpt/tests/fledge/tentative/currency.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/currency.https.window.js
@@ -501,7 +501,7 @@ subsetTest(promise_test, async test => {
topLevelSellerScriptParamsOverride: {
scoreAd: `
// scoreAd sees what's actually passed in.
- if (bid != 9)
+ if (bid !== 9)
throw 'Wrong bid';
if (browserSignals.bidCurrency !== '???')
throw 'Wrong currency';`
diff --git a/tests/wpt/tests/fledge/tentative/generate-bid-browser-signals.https.window.js b/tests/wpt/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
index 8687e3f296d..c7078ae08ad 100644
--- a/tests/wpt/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
@@ -939,7 +939,7 @@ subsetTest(promise_test, async test => {
if (!deepEquals(Object.keys(instance.exports), ["increment"]))
throw "Unexpected exports: " + JSON.stringify(instance.exports);
- if (instance.exports.increment(1) != 2)
+ if (instance.exports.increment(1) !== 2)
throw "Unexpected increment result: " + instance.exports.increment(1);` })
}
});
diff --git a/tests/wpt/tests/fledge/tentative/resources/additional-bids.py b/tests/wpt/tests/fledge/tentative/resources/additional-bids.py
index 060606b41df..721909a0458 100644
--- a/tests/wpt/tests/fledge/tentative/resources/additional-bids.py
+++ b/tests/wpt/tests/fledge/tentative/resources/additional-bids.py
@@ -13,6 +13,7 @@ with a value of b"?1"; this entrypoint otherwise returns a 400 response.
import json
import base64
+import fledge.tentative.resources.ed25519 as ed25519
import fledge.tentative.resources.fledge_http_server_util as fledge_http_server_util
@@ -20,6 +21,57 @@ class BadRequestError(Exception):
pass
+def _generate_signature(message, base64_encoded_secret_key):
+ """Returns a signature entry for a signed additional bid.
+
+ Args:
+ base64_encoded_secret_key: base64-encoded Ed25519 key with which to sign
+ the message. From this secret key, the public key can be deduced, which
+ becomes part of the signature entry.
+ message: The additional bid text (or other text if generating an invalid
+ signature) to sign.
+ """
+ secret_key = base64.b64decode(base64_encoded_secret_key.encode("utf-8"))
+ public_key = ed25519.publickey_unsafe(secret_key)
+ signature = ed25519.signature_unsafe(
+ message.encode("utf-8"), secret_key, public_key)
+ return {
+ "key": base64.b64encode(public_key).decode("utf-8"),
+ "signature": base64.b64encode(signature).decode("utf-8")
+ }
+
+
+def _sign_additional_bid(additional_bid_string,
+ secret_keys_for_valid_signatures,
+ secret_keys_for_invalid_signatures):
+ """Returns a signed additional bid given an additional bid and secret keys.
+
+ Args:
+ additional_bid_string: string representation of the additional bid
+ secret_keys_for_valid_signatures: a list of strings, each a base64-encoded
+ Ed25519 secret key with which to sign the additional bid
+ secret_keys_for_invalid_signatures: a list of strings, each a base64-encoded
+ Ed25519 secret key with which to incorrectly sign the additional bid
+ """
+ signatures = []
+ signatures.extend(
+ _generate_signature(additional_bid_string, secret_key)
+ for secret_key in secret_keys_for_valid_signatures)
+
+ # For invalid signatures, we use the correct secret key to sign a different
+ # message - the additional bid prepended by 'invalid' - so that the signature
+ # is a structually valid signature but can't be used to verify the additional
+ # bid.
+ signatures.extend(
+ _generate_signature("invalid" + additional_bid_string, secret_key)
+ for secret_key in secret_keys_for_invalid_signatures)
+
+ return json.dumps({
+ "bid": additional_bid_string,
+ "signatures": signatures
+ })
+
+
def main(request, response):
try:
if fledge_http_server_util.handle_cors_headers_and_preflight(request, response):
@@ -34,14 +86,16 @@ def main(request, response):
if not additional_bids:
raise BadRequestError("Missing 'additionalBids' parameter")
for additional_bid in json.loads(additional_bids):
- additional_bid_string = json.dumps(additional_bid)
+ # Each additional bid may have associated testMetadata. Remove this from
+ # the additional bid and use it to adjust the behavior of this handler.
+ test_metadata = additional_bid.pop("testMetadata", {})
auction_nonce = additional_bid.get("auctionNonce", None)
if not auction_nonce:
raise BadRequestError("Additional bid missing required 'auctionNonce' field")
- signed_additional_bid = json.dumps({
- "bid": additional_bid_string,
- "signatures": []
- })
+ signed_additional_bid = _sign_additional_bid(
+ json.dumps(additional_bid),
+ test_metadata.get("secretKeysForValidSignatures", []),
+ test_metadata.get("secretKeysForInvalidSignatures", []))
additional_bid_header_value = (auction_nonce.encode("utf-8") + b":" +
base64.b64encode(signed_additional_bid.encode("utf-8")))
response.headers.append(b"Ad-Auction-Additional-Bid", additional_bid_header_value)
diff --git a/tests/wpt/tests/fledge/tentative/resources/ed25519.py b/tests/wpt/tests/fledge/tentative/resources/ed25519.py
new file mode 100644
index 00000000000..53e548ab8e6
--- /dev/null
+++ b/tests/wpt/tests/fledge/tentative/resources/ed25519.py
@@ -0,0 +1,289 @@
+# ed25519.py - Optimized version of the reference implementation of Ed25519
+#
+# Written in 2011? by Daniel J. Bernstein <djb@cr.yp.to>
+# 2013 by Donald Stufft <donald@stufft.io>
+# 2013 by Alex Gaynor <alex.gaynor@gmail.com>
+# 2013 by Greg Price <price@mit.edu>
+#
+# To the extent possible under law, the author(s) have dedicated all copyright
+# and related and neighboring rights to this software to the public domain
+# worldwide. This software is distributed without any warranty.
+#
+# You should have received a copy of the CC0 Public Domain Dedication along
+# with this software. If not, see
+# <http://creativecommons.org/publicdomain/zero/1.0/>.
+#
+# Downloaded from https://raw.githubusercontent.com/pyca/ed25519/main/ed25519.py
+# on April 1, 2024.
+
+"""
+NB: This code is not safe for use with secret keys or secret data.
+The only safe use of this code is for verifying signatures on public messages.
+
+Functions for computing the public key of a secret key and for signing
+a message are included, namely publickey_unsafe and signature_unsafe,
+for testing purposes only.
+
+The root of the problem is that Python's long-integer arithmetic is
+not designed for use in cryptography. Specifically, it may take more
+or less time to execute an operation depending on the values of the
+inputs, and its memory access patterns may also depend on the inputs.
+This opens it to timing and cache side-channel attacks which can
+disclose data to an attacker. We rely on Python's long-integer
+arithmetic, so we cannot handle secrets without risking their disclosure.
+"""
+
+import hashlib
+
+
+__version__ = "1.0.dev0"
+
+
+b = 256
+q = 2**255 - 19
+l = 2**252 + 27742317777372353535851937790883648493
+
+
+def H(m):
+ return hashlib.sha512(m).digest()
+
+
+def pow2(x, p):
+ """== pow(x, 2**p, q)"""
+ while p > 0:
+ x = x * x % q
+ p -= 1
+ return x
+
+
+def inv(z):
+ r"""$= z^{-1} \mod q$, for z != 0"""
+ # Adapted from curve25519_athlon.c in djb's Curve25519.
+ z2 = z * z % q # 2
+ z9 = pow2(z2, 2) * z % q # 9
+ z11 = z9 * z2 % q # 11
+ z2_5_0 = (z11 * z11) % q * z9 % q # 31 == 2^5 - 2^0
+ z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % q # 2^10 - 2^0
+ z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % q # ...
+ z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 % q
+ z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 % q
+ z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 % q
+ z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 % q
+ z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % q # 2^250 - 2^0
+ return pow2(z2_250_0, 5) * z11 % q # 2^255 - 2^5 + 11 = q - 2
+
+
+d = -121665 * inv(121666) % q
+I = pow(2, (q - 1) // 4, q)
+
+
+def xrecover(y):
+ xx = (y * y - 1) * inv(d * y * y + 1)
+ x = pow(xx, (q + 3) // 8, q)
+
+ if (x * x - xx) % q != 0:
+ x = (x * I) % q
+
+ if x % 2 != 0:
+ x = q - x
+
+ return x
+
+
+By = 4 * inv(5)
+Bx = xrecover(By)
+B = (Bx % q, By % q, 1, (Bx * By) % q)
+ident = (0, 1, 1, 0)
+
+
+def edwards_add(P, Q):
+ # This is formula sequence 'addition-add-2008-hwcd-3' from
+ # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
+ (x1, y1, z1, t1) = P
+ (x2, y2, z2, t2) = Q
+
+ a = (y1 - x1) * (y2 - x2) % q
+ b = (y1 + x1) * (y2 + x2) % q
+ c = t1 * 2 * d * t2 % q
+ dd = z1 * 2 * z2 % q
+ e = b - a
+ f = dd - c
+ g = dd + c
+ h = b + a
+ x3 = e * f
+ y3 = g * h
+ t3 = e * h
+ z3 = f * g
+
+ return (x3 % q, y3 % q, z3 % q, t3 % q)
+
+
+def edwards_double(P):
+ # This is formula sequence 'dbl-2008-hwcd' from
+ # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
+ (x1, y1, z1, t1) = P
+
+ a = x1 * x1 % q
+ b = y1 * y1 % q
+ c = 2 * z1 * z1 % q
+ # dd = -a
+ e = ((x1 + y1) * (x1 + y1) - a - b) % q
+ g = -a + b # dd + b
+ f = g - c
+ h = -a - b # dd - b
+ x3 = e * f
+ y3 = g * h
+ t3 = e * h
+ z3 = f * g
+
+ return (x3 % q, y3 % q, z3 % q, t3 % q)
+
+
+def scalarmult(P, e):
+ if e == 0:
+ return ident
+ Q = scalarmult(P, e // 2)
+ Q = edwards_double(Q)
+ if e & 1:
+ Q = edwards_add(Q, P)
+ return Q
+
+
+# Bpow[i] == scalarmult(B, 2**i)
+Bpow = []
+
+
+def make_Bpow():
+ P = B
+ for i in range(253):
+ Bpow.append(P)
+ P = edwards_double(P)
+
+
+make_Bpow()
+
+
+def scalarmult_B(e):
+ """
+ Implements scalarmult(B, e) more efficiently.
+ """
+ # scalarmult(B, l) is the identity
+ e = e % l
+ P = ident
+ for i in range(253):
+ if e & 1:
+ P = edwards_add(P, Bpow[i])
+ e = e // 2
+ assert e == 0, e
+ return P
+
+
+def encodeint(y):
+ bits = [(y >> i) & 1 for i in range(b)]
+ return bytes(
+ [sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b // 8)]
+ )
+
+
+def encodepoint(P):
+ (x, y, z, t) = P
+ zi = inv(z)
+ x = (x * zi) % q
+ y = (y * zi) % q
+ bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
+ return bytes(
+ [sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b // 8)]
+ )
+
+
+def bit(h, i):
+ return (h[i // 8] >> (i % 8)) & 1
+
+
+def publickey_unsafe(sk):
+ """
+ Not safe to use with secret keys or secret data.
+
+ See module docstring. This function should be used for testing only.
+ """
+ h = H(sk)
+ a = 2 ** (b - 2) + sum(2**i * bit(h, i) for i in range(3, b - 2))
+ A = scalarmult_B(a)
+ return encodepoint(A)
+
+
+def Hint(m):
+ h = H(m)
+ return sum(2**i * bit(h, i) for i in range(2 * b))
+
+
+def signature_unsafe(m, sk, pk):
+ """
+ Not safe to use with secret keys or secret data.
+
+ See module docstring. This function should be used for testing only.
+ """
+ h = H(sk)
+ a = 2 ** (b - 2) + sum(2**i * bit(h, i) for i in range(3, b - 2))
+ r = Hint(bytes([h[j] for j in range(b // 8, b // 4)]) + m)
+ R = scalarmult_B(r)
+ S = (r + Hint(encodepoint(R) + pk + m) * a) % l
+ return encodepoint(R) + encodeint(S)
+
+
+def isoncurve(P):
+ (x, y, z, t) = P
+ return (
+ z % q != 0
+ and x * y % q == z * t % q
+ and (y * y - x * x - z * z - d * t * t) % q == 0
+ )
+
+
+def decodeint(s):
+ return sum(2**i * bit(s, i) for i in range(0, b))
+
+
+def decodepoint(s):
+ y = sum(2**i * bit(s, i) for i in range(0, b - 1))
+ x = xrecover(y)
+ if x & 1 != bit(s, b - 1):
+ x = q - x
+ P = (x, y, 1, (x * y) % q)
+ if not isoncurve(P):
+ raise ValueError("decoding point that is not on curve")
+ return P
+
+
+class SignatureMismatch(Exception):
+ pass
+
+
+def checkvalid(s, m, pk):
+ """
+ Not safe to use when any argument is secret.
+
+ See module docstring. This function should be used only for
+ verifying public signatures of public messages.
+ """
+ if len(s) != b // 4:
+ raise ValueError("signature length is wrong")
+
+ if len(pk) != b // 8:
+ raise ValueError("public-key length is wrong")
+
+ R = decodepoint(s[: b // 8])
+ A = decodepoint(pk)
+ S = decodeint(s[b // 8 : b // 4])
+ h = Hint(encodepoint(R) + pk + m)
+
+ (x1, y1, z1, t1) = P = scalarmult_B(S)
+ (x2, y2, z2, t2) = Q = edwards_add(R, scalarmult(A, h))
+
+ if (
+ not isoncurve(P)
+ or not isoncurve(Q)
+ or (x1 * z2 - x2 * z1) % q != 0
+ or (y1 * z2 - y2 * z1) % q != 0
+ ):
+ raise SignatureMismatch("signature does not pass verification")
diff --git a/tests/wpt/tests/fledge/tentative/resources/fledge-util.sub.js b/tests/wpt/tests/fledge/tentative/resources/fledge-util.sub.js
index 5819357e299..7be02e34ffd 100644
--- a/tests/wpt/tests/fledge/tentative/resources/fledge-util.sub.js
+++ b/tests/wpt/tests/fledge/tentative/resources/fledge-util.sub.js
@@ -177,7 +177,7 @@ async function waitForObservedRequestsIgnoreDebugOnlyReports(
function createBiddingScriptURL(params = {}) {
let origin = params.origin ? params.origin : new URL(BASE_URL).origin;
let url = new URL(`${origin}${RESOURCE_PATH}bidding-logic.sub.py`);
- // These checks use "==" to ignore null and not provided arguments, while
+ // These checks use "!=" to ignore null and not provided arguments, while
// treating '' as a valid argument.
if (params.generateBid != null)
url.searchParams.append('generateBid', params.generateBid);
@@ -213,7 +213,7 @@ function createDecisionScriptURL(uuid, params = {}) {
let origin = params.origin ? params.origin : new URL(BASE_URL).origin;
let url = new URL(`${origin}${RESOURCE_PATH}decision-logic.sub.py`);
url.searchParams.append('uuid', uuid);
- // These checks use "==" to ignore null and not provided arguments, while
+ // These checks use "!=" to ignore null and not provided arguments, while
// treating '' as a valid argument.
if (params.scoreAd != null)
url.searchParams.append('scoreAd', params.scoreAd);
@@ -230,8 +230,8 @@ function createDecisionScriptURL(uuid, params = {}) {
// be last. "signalsParams" also has no effect, but is used by
// trusted-scoring-signals.py to affect the response.
function createRenderURL(uuid, script, signalsParams, origin) {
- // These checks use "==" to ignore null and not provided arguments, while
- // treating '' as a valid argument.
+ // These checks use "==" and "!=" to ignore null and not provided
+ // arguments, while treating '' as a valid argument.
if (origin == null)
origin = new URL(BASE_URL).origin;
let url = new URL(`${origin}${RESOURCE_PATH}fenced-frame.sub.py`);
@@ -260,6 +260,15 @@ function createInterestGroupForOrigin(uuid, origin,
};
}
+// Waits for the join command to complete. Adds cleanup command to `test` to
+// leave the interest group when the test completes.
+async function joinInterestGroupWithoutDefaults(test, interestGroup,
+ durationSeconds = 60) {
+ await navigator.joinAdInterestGroup(interestGroup, durationSeconds);
+ test.add_cleanup(
+ async () => { await navigator.leaveAdInterestGroup(interestGroup); });
+}
+
// Joins an interest group that, by default, is owned by the current frame's
// origin, is named DEFAULT_INTEREST_GROUP_NAME, has a bidding script that
// issues a bid of 9 with a renderURL of "https://not.checked.test/${uuid}",
@@ -271,12 +280,33 @@ function createInterestGroupForOrigin(uuid, origin,
// interest group.
async function joinInterestGroup(test, uuid, interestGroupOverrides = {},
durationSeconds = 60) {
- let interestGroup = createInterestGroupForOrigin(uuid, window.location.origin,
- interestGroupOverrides);
-
- await navigator.joinAdInterestGroup(interestGroup, durationSeconds);
- test.add_cleanup(
- async () => { await navigator.leaveAdInterestGroup(interestGroup) });
+ await joinInterestGroupWithoutDefaults(
+ test, createInterestGroupForOrigin(
+ uuid, window.location.origin, interestGroupOverrides),
+ durationSeconds);
+}
+
+// Joins a negative interest group with the specified owner, name, and
+// additionalBidKey. Because these are the only valid fields for a negative
+// interest groups, this function doesn't expose an 'overrides' parameter.
+// Adds cleanup command to `test` to leave the interest group when the test
+// completes.
+async function joinNegativeInterestGroup(
+ test, owner, name, additionalBidKey) {
+ let interestGroup = {
+ owner: owner,
+ name: name,
+ additionalBidKey: additionalBidKey
+ };
+ if (owner !== window.location.origin) {
+ let iframe = await createIframe(test, owner, 'join-ad-interest-group');
+ await runInFrame(
+ test, iframe,
+ `await joinInterestGroupWithoutDefaults(` +
+ `test_instance, ${JSON.stringify(interestGroup)})`);
+ } else {
+ await joinInterestGroupWithoutDefaults(test_instance, interestGroup);
+ }
}
// Similar to joinInterestGroup, but leaves the interest group instead.
@@ -487,6 +517,17 @@ async function runReportTest(test, uuid, codeToInsert, expectedReportURLs,
await waitForObservedRequests(uuid, expectedReportURLs);
}
+// Helper function for running a standard test of the additional bid and
+// negative targeting features. This helper verifies that the auction produces a
+// winner. It takes the following arguments:
+// - test/uuid: the test object and uuid from the test case (see generateUuid)
+// - buyers: array of strings, each a domain for a buyer participating in this
+// auction
+// - actionNonce: string, the auction nonce for this auction, typically
+// retrieved from a prior call to navigator.createAuctionNonce
+// - highestScoringOtherBid: the amount of the second-highest bid,
+// or zero if there's no second-highest bid
+// - winningAdditionalBidId: the label of the winning bid
async function runAdditionalBidTest(test, uuid, buyers, auctionNonce,
additionalBidsPromise,
highestScoringOtherBid,
@@ -516,7 +557,7 @@ async function runInFrame(test, child_window, script, param) {
let promise = new Promise(function(resolve, reject) {
function WaitForMessage(event) {
- if (event.data.messageUuid != messageUuid)
+ if (event.data.messageUuid !== messageUuid)
return;
receivedResponse = event.data;
if (event.data.result === 'success') {
@@ -548,7 +589,7 @@ async function createFrame(test, origin, is_iframe = true, permissions = null) {
`${origin}${RESOURCE_PATH}subordinate-frame.sub.html?uuid=${frameUuid}`;
let promise = new Promise(function(resolve, reject) {
function WaitForMessage(event) {
- if (event.data.messageUuid != frameUuid)
+ if (event.data.messageUuid !== frameUuid)
return;
if (event.data.result === 'load complete') {
resolve();
@@ -662,79 +703,130 @@ function directFromSellerSignalsValidatorCode(uuid, expectedSellerSignals,
return {
// Seller worklets
scoreAd:
- `if (directFromSellerSignals === null ||
+ `if (directFromSellerSignals == null ||
directFromSellerSignals.sellerSignals !== ${expectedSellerSignals} ||
directFromSellerSignals.auctionSignals !== ${expectedAuctionSignals} ||
- Object.keys(directFromSellerSignals).length != 2) {
+ Object.keys(directFromSellerSignals).length !== 2) {
throw 'Failed to get expected directFromSellerSignals in scoreAd(): ' +
JSON.stringify(directFromSellerSignals);
}`,
reportResultSuccessCondition:
- `directFromSellerSignals !== null &&
+ `directFromSellerSignals != null &&
directFromSellerSignals.sellerSignals === ${expectedSellerSignals} &&
directFromSellerSignals.auctionSignals === ${expectedAuctionSignals} &&
- Object.keys(directFromSellerSignals).length == 2`,
+ Object.keys(directFromSellerSignals).length === 2`,
reportResult:
`sendReportTo("${createSellerReportURL(uuid)}");`,
// Bidder worklets
generateBid:
- `if (directFromSellerSignals === null ||
+ `if (directFromSellerSignals == null ||
directFromSellerSignals.perBuyerSignals !== ${expectedPerBuyerSignals} ||
directFromSellerSignals.auctionSignals !== ${expectedAuctionSignals} ||
- Object.keys(directFromSellerSignals).length != 2) {
+ Object.keys(directFromSellerSignals).length !== 2) {
throw 'Failed to get expected directFromSellerSignals in generateBid(): ' +
JSON.stringify(directFromSellerSignals);
}`,
reportWinSuccessCondition:
- `directFromSellerSignals !== null &&
+ `directFromSellerSignals != null &&
directFromSellerSignals.perBuyerSignals === ${expectedPerBuyerSignals} &&
directFromSellerSignals.auctionSignals === ${expectedAuctionSignals} &&
- Object.keys(directFromSellerSignals).length == 2`,
+ Object.keys(directFromSellerSignals).length === 2`,
reportWin:
`sendReportTo("${createBidderReportURL(uuid)}");`,
};
}
-// Creates an additional bid with the given parameters. This additional bid
-// specifies a biddingLogicURL that provides an implementation of
-// reportAdditionalBidWin that triggers a sendReportTo() to the bidder report
-// URL of the winning additional bid. Additional bids are described in more
-// detail at
-// https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids.
-function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount,
- additionalBidOverrides = {}) {
- return {
- interestGroup: {
- name: interestGroupName,
- biddingLogicURL: createBiddingScriptURL(
- {
- origin: buyer,
- reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");`
- }),
- owner: buyer
- },
- bid: {
- ad: ['metadata'],
- bid: bidAmount,
- render: createRenderURL(uuid)
- },
- auctionNonce: auctionNonce,
- seller: seller,
- ...additionalBidOverrides
+let additionalBidHelper = function() {
+ // Creates an additional bid with the given parameters. This additional bid
+ // specifies a biddingLogicURL that provides an implementation of
+ // reportAdditionalBidWin that triggers a sendReportTo() to the bidder report
+ // URL of the winning additional bid. Additional bids are described in more
+ // detail at
+ // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids.
+ function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount,
+ additionalBidOverrides = {}) {
+ return {
+ interestGroup: {
+ name: interestGroupName,
+ biddingLogicURL: createBiddingScriptURL(
+ {
+ origin: buyer,
+ reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");`
+ }),
+ owner: buyer
+ },
+ bid: {
+ ad: ['metadata'],
+ bid: bidAmount,
+ render: createRenderURL(uuid)
+ },
+ auctionNonce: auctionNonce,
+ seller: seller,
+ ...additionalBidOverrides
+ };
}
-}
-// Fetch some number of additional bid from a seller and verify that the
-// 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context.
-// The `additionalBids` parameter is a list of additional bids.
-async function fetchAdditionalBids(seller, additionalBids) {
- const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`);
- url.searchParams.append('additionalBids', JSON.stringify(additionalBids));
- const response = await fetch(url.href, {adAuctionHeaders: true});
+ // Gets the testMetadata for an additional bid, initializing it if needed.
+ function getAndMaybeInitializeTestMetadata(additionalBid) {
+ if (additionalBid.testMetadata === undefined) {
+ additionalBid.testMetadata = {};
+ }
+ return additionalBid.testMetadata;
+ }
- assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text());
- assert_false(
- response.headers.has('Ad-Auction-Additional-Bid'),
- 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.');
-}
+ // Tells the additional bid endpoint to correctly sign the additional bid with
+ // the given secret keys before returning that as a signed additional bid.
+ function signWithSecretKeys(additionalBid, secretKeys) {
+ getAndMaybeInitializeTestMetadata(additionalBid).
+ secretKeysForValidSignatures = secretKeys;
+ }
+
+ // Tells the additional bid endpoint to incorrectly sign the additional bid with
+ // the given secret keys before returning that as a signed additional bid. This
+ // is used for testing the behavior when the auction encounters an invalid
+ // signature.
+ function incorrectlySignWithSecretKeys(additionalBid, secretKeys) {
+ getAndMaybeInitializeTestMetadata(additionalBid).
+ secretKeysForInvalidSignatures = secretKeys;
+ }
+
+ // Adds a single negative interest group to an additional bid, as described at:
+ // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#622-how-additional-bids-specify-their-negative-interest-groups
+ function addNegativeInterestGroup(additionalBid, negativeInterestGroup) {
+ additionalBid["negativeInterestGroup"] = negativeInterestGroup;
+ }
+
+ // Adds multiple negative interest groups to an additional bid, as described at:
+ // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#622-how-additional-bids-specify-their-negative-interest-groups
+ function addNegativeInterestGroups(additionalBid, negativeInterestGroups,
+ joiningOrigin) {
+ additionalBid["negativeInterestGroups"] = {
+ joiningOrigin: joiningOrigin,
+ interestGroupNames: negativeInterestGroups
+ };
+ }
+
+ // Fetch some number of additional bid from a seller and verify that the
+ // 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context.
+ // The `additionalBids` parameter is a list of additional bids.
+ async function fetchAdditionalBids(seller, additionalBids) {
+ const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`);
+ url.searchParams.append('additionalBids', JSON.stringify(additionalBids));
+ const response = await fetch(url.href, {adAuctionHeaders: true});
+
+ assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text());
+ assert_false(
+ response.headers.has('Ad-Auction-Additional-Bid'),
+ 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.');
+ }
+
+ return {
+ createAdditionalBid: createAdditionalBid,
+ signWithSecretKeys: signWithSecretKeys,
+ incorrectlySignWithSecretKeys: incorrectlySignWithSecretKeys,
+ addNegativeInterestGroup: addNegativeInterestGroup,
+ addNegativeInterestGroups: addNegativeInterestGroups,
+ fetchAdditionalBids: fetchAdditionalBids
+ };
+}();
diff --git a/tests/wpt/tests/fledge/tentative/resources/worklet-helpers.js b/tests/wpt/tests/fledge/tentative/resources/worklet-helpers.js
index 2147a026aec..0bac1b99a96 100644
--- a/tests/wpt/tests/fledge/tentative/resources/worklet-helpers.js
+++ b/tests/wpt/tests/fledge/tentative/resources/worklet-helpers.js
@@ -11,10 +11,10 @@ function deepEquals(a, b) {
return a === b;
let aKeys = Object.keys(a);
- if (aKeys.length != Object.keys(b).length)
+ if (aKeys.length !== Object.keys(b).length)
return false;
for (let key of aKeys) {
- if (a.hasOwnProperty(key) != b.hasOwnProperty(key) ||
+ if (a.hasOwnProperty(key) !== b.hasOwnProperty(key) ||
!deepEquals(a[key], b[key])) {
return false;
}
diff --git a/tests/wpt/tests/fledge/tentative/tie.https.window.js b/tests/wpt/tests/fledge/tentative/tie.https.window.js
index 48d6e95e5c4..c87d10f2016 100644
--- a/tests/wpt/tests/fledge/tentative/tie.https.window.js
+++ b/tests/wpt/tests/fledge/tentative/tie.https.window.js
@@ -98,7 +98,7 @@ promise_test(async test => {
auctionConfigOverrides.decisionLogicURL =
createDecisionScriptURL(
uuid,
- {scoreAd: `if (browserSignals.renderURL == "${winningAdURL}")
+ {scoreAd: `if (browserSignals.renderURL === "${winningAdURL}")
return 0;`});
// Add an abort controller, so can cancel extra auctions.
diff --git a/tests/wpt/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html b/tests/wpt/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html
index fd4cd665cc2..35844bc91fb 100644
--- a/tests/wpt/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html
+++ b/tests/wpt/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html
@@ -15,33 +15,41 @@ const outerIFrame = document.querySelector('iframe');
setup({ explicit_done: true });
function runTest() {
test(() => {
- assert_true(outerIFrame.contentDocument.activeElement === outerIFrame.contentDocument.body,
+ assert_equals(outerIFrame.contentDocument.activeElement, outerIFrame.contentDocument.body,
"Initially, the activeElement of the outer iframe should be <body>");
const innerIFrame = outerIFrame.contentDocument.createElement("iframe");
window.onmessage = function() {
- // We receive an message when the innerIFrame is ready and its input is focused.
- // outerIframe is the ancestor of inner iframe, so the activeElement of
- // it should be the inner iframe.
- assert_true(outerIFrame.contentDocument.activeElement === innerIFrame,
- "The activeElement of the outer iframe should be the inner iframe");
-
- // Now we focus the input in the top level
- document.querySelector("input").focus();
-
// Wait for a bit to let whatever the code that might change the focus to run
window.requestAnimationFrame(function() {
window.requestAnimationFrame(function() {
window.requestAnimationFrame(function() {
- // Since inner iframe lost its focus, the activeElement of outer iframe
- // should be cleared as well, hence <body> should be focused.
- assert_true(outerIFrame.contentDocument.activeElement === outerIFrame.contentDocument.body,
- "The activeElement of the outer iframe should be reverted back to <body>");
- assert_true(document.activeElement === document.querySelector("input"),
- "The activeElement of the top-level document should the input");
- done();
- })
+
+ // We receive an message when the innerIFrame is ready and its input is focused.
+ // outerIframe is the ancestor of inner iframe, so the activeElement of
+ // it should be the inner iframe.
+ assert_equals(outerIFrame.contentDocument.activeElement, innerIFrame,
+ "The activeElement of the outer iframe should be the inner iframe");
+
+ // Now we focus the input in the top level
+ document.querySelector("input").focus();
+
+ // Wait for a bit to let whatever the code that might change the focus to run
+ window.requestAnimationFrame(function() {
+ window.requestAnimationFrame(function() {
+ window.requestAnimationFrame(function() {
+ // Since inner iframe lost its focus, the activeElement of outer iframe
+ // should be cleared as well, hence <body> should be focused.
+ assert_equals(outerIFrame.contentDocument.activeElement, outerIFrame.contentDocument.body,
+ "The activeElement of the outer iframe should be reverted back to <body>");
+ assert_equals(document.activeElement, document.querySelector("input"),
+ "The activeElement of the top-level document should the input");
+ done();
+ });
+ });
+ });
+ });
});
});
}
diff --git a/tests/wpt/tests/fonts/math/stretchy.woff b/tests/wpt/tests/fonts/math/stretchy.woff
index eb67181e1ea..ac7d908f6e2 100644
--- a/tests/wpt/tests/fonts/math/stretchy.woff
+++ b/tests/wpt/tests/fonts/math/stretchy.woff
Binary files differ
diff --git a/tests/wpt/tests/fs/script-tests/FileSystemWritableFileStream-write.js b/tests/wpt/tests/fs/script-tests/FileSystemWritableFileStream-write.js
index 0b12def74c9..b0485035b91 100644
--- a/tests/wpt/tests/fs/script-tests/FileSystemWritableFileStream-write.js
+++ b/tests/wpt/tests/fs/script-tests/FileSystemWritableFileStream-write.js
@@ -362,3 +362,13 @@ directory_test(async (t, root) => {
const newStream = await handle.createWritable({mode: 'exclusive'});
await newStream.close();
}, 'an errored writable stream releases its lock');
+
+directory_test(async (t, root) => {
+ const handle = await createFileWithContents(t, 'file.txt', 'contents', root);
+ const stream = await handle.createWritable({mode: 'exclusive'});
+
+ const writer = stream.getWriter();
+
+ await promise_rejects_js(t, TypeError, writer.write(null), 'write with null data');
+ await promise_rejects_js(t, TypeError, writer.write("foo"), 'write with text data');
+}, 'an errored writable stream should reject the next write call');
diff --git a/tests/wpt/tests/html-media-capture/WEB_FEATURES.yml b/tests/wpt/tests/html-media-capture/WEB_FEATURES.yml
new file mode 100644
index 00000000000..e794b88c3e5
--- /dev/null
+++ b/tests/wpt/tests/html-media-capture/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: html-media-capture
+ files: "**"
diff --git a/tests/wpt/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html b/tests/wpt/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html
index 5f3e95cce40..a3badfcb5ea 100644
--- a/tests/wpt/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html
+++ b/tests/wpt/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html
@@ -22,8 +22,7 @@ promise_test(async () => {
+ 'VIDEO%26PartialContent';
let v = document.createElement("video");
- if (v.canPlayType("video/ogv") == "")
- frame.src += "%26mp4";
+ frame.src += "%26mp4";
document.body.appendChild(frame);
await new Promise(resolve => frame.onload = resolve);
diff --git a/tests/wpt/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html b/tests/wpt/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
index 05984a47f82..8fc6377f837 100644
--- a/tests/wpt/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
+++ b/tests/wpt/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
@@ -77,63 +77,6 @@ _addTest(function(canvas, ctx) {
{name: 'dropShadow', floodOpacity: [20]});
ctx.filter = new CanvasFilter(
{name: 'dropShadow', floodOpacity: '30'});
- // dx
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: '30'});
- // dy
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: '30'});
- // floodOpacity
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: '30'});
// stdDeviation
ctx.filter = new CanvasFilter(
{name: 'dropShadow', stdDeviation: 10});
diff --git a/tests/wpt/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html b/tests/wpt/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html
index df5e0c7dc3e..0c3b6a8ce9f 100644
--- a/tests/wpt/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html
+++ b/tests/wpt/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html
@@ -77,63 +77,6 @@ _addTest(function(canvas, ctx) {
{name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer();
ctx.beginLayer({filter:
{name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer();
- // dx
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: '30'}}); ctx.endLayer();
- // dy
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: '30'}}); ctx.endLayer();
- // floodOpacity
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer();
// stdDeviation
ctx.beginLayer({filter:
{name: 'dropShadow', stdDeviation: 10}}); ctx.endLayer();
diff --git a/tests/wpt/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html b/tests/wpt/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html
index 9283fd39b7d..5d755cfc498 100644
--- a/tests/wpt/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html
+++ b/tests/wpt/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html
@@ -16,8 +16,9 @@
<ul id="d"></ul>
<script>
-var t = async_test("Tests that layers ignore the global context filter.");
-_addTest(function(canvas, ctx) {
+test(t => {
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d');
ctx.globalAlpha = 0.5;
ctx.globalCompositeOperation = 'xor';
@@ -51,6 +52,6 @@ _addTest(function(canvas, ctx) {
_assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
_assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
-});
+}, "Tests that layers ignore the global context filter.");
</script>
diff --git a/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js b/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js
index 20953417ccf..1a181a1cc1a 100644
--- a/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -21,6 +20,5 @@ promise_test(async t => {
ctx.drawImage(bitmap, 0, 0);
_assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js b/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js
index c6ab163b65b..cb8f1413a2d 100644
--- a/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.globalAlpha = 0.01;
ctx.fillRect(0, 0, 100, 50);
_assertPixelApprox(canvas, 50,25, 2,253,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
index b2f860bc606..e91416b0369 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
_assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
_assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
index f725acf0118..a852f59e550 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -29,6 +28,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
_assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
_assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
index b2d6f7d860d..ca5b6c223cb 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
_assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
_assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
index 380d526f9b4..aff6c21108f 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
_assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
_assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
index 76e3e8baaf1..a3c14535c53 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -30,6 +29,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
_assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
_assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
index d3525435fa1..0c060f70084 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -21,6 +20,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 99,0, 0,255,0,255, 2);
_assertPixelApprox(canvas, 0,49, 0,255,0,255, 2);
_assertPixelApprox(canvas, 99,49, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
index aa15efb7059..7d7f064b268 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 80,20, 0,255,0,255, 2);
_assertPixelApprox(canvas, 20,30, 0,255,0,255, 2);
_assertPixelApprox(canvas, 80,30, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
index 958a2431b6d..6854a06d9fa 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -20,6 +19,5 @@ promise_test(async t => {
ctx.drawImage(bitmap, 0, 0);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
index dc695b5459e..1b2b0f83eda 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -15,6 +14,5 @@ promise_test(async t => {
const blob = await response.blob();
await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(blob), 'The source image could not be decoded.');
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
index fea2a7bac70..ce4231c2ac9 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
ctx.clip();
ctx.drawImage(bitmap, 0, 0);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
index 0b17673b689..e9d0d9c77a0 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -20,6 +19,5 @@ promise_test(async t => {
ctx.drawImage(bitmap, 0, 0);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
index 1efc17d04d0..18ecb11aacb 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -16,6 +15,5 @@ promise_test(async t => {
const bitmap = await createImageBitmap(blob);
ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
index ce5cf5efadc..36c4b274724 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Negative destination width/height represents the correct rectangle");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
index c82b9ba1bec..f774deef61c 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Negative dimensions do not affect the direction of the image");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
index f92d15774d7..804a09f699e 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 51,48, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Negative source width/height represents the correct rectangle");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
index a56f09e0b2d..e9ca406fd24 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -319,6 +318,5 @@ promise_test(async t => {
ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, Infinity);
ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, Infinity);
_assertPixel(canvas, 50,25, 0,255,0,255);
- t.done();
}, "drawImage() with Infinity/NaN is ignored");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
index 47a77834c05..5728d424d3e 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -21,6 +20,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 45,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 55,25, 0,255,0,255, 2);
- t.done();
}, "Stretched images do not get pixels wrapping around the edges");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
index 3259ff02580..b18d021074e 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -20,6 +19,5 @@ promise_test(async t => {
ctx.drawImage(bitmap, 0, 0);
ctx.fill();
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
index 4a4cd07be95..1ba832155ce 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -20,6 +19,5 @@ promise_test(async t => {
ctx.drawImage(bitmap, 0, 0);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
index a80bfff5621..61b70b0c475 100644
--- a/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -20,6 +19,5 @@ promise_test(async t => {
ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50);
ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
- t.done();
}, "drawImage with zero-sized source rectangle draws nothing without exception");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
index 14cfcbc9939..cc2dc301cad 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -24,6 +23,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js
index 56a8a7d23cd..382214a49e4 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -20,6 +19,5 @@ promise_test(async t => {
var img = await createImageBitmap(blob);
var pattern = ctx.createPattern(img, 'no-repeat');
_assert(pattern.thisImplementsCanvasPattern, "pattern.thisImplementsCanvasPattern");
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
index d9004fc028b..ff2acc6851c 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.fillRect(0, 0, 100, 50);
_assertPixel(canvas, 50,25, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
index 9ac4e1605c7..a093e61d25a 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
index 6c4a1409b7d..3b4365d6e79 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
index d2fdd86022a..3314efdd3a6 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -29,6 +28,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
index 584a5d6cfdd..6120bd0549c 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -29,6 +28,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
index 0b5fef95ce0..b029f90a372 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -31,6 +30,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
index b303b2d813b..b3e61d53e93 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -31,6 +30,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "Image patterns do not get flipped when painted");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
index 37f4c04228c..872acb1c206 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
index efbd1c58469..009b072b22d 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -26,6 +25,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
index db707ecb0a6..21e33490038 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
index e58cddccfeb..39c6037ad01 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
index 4a173af0b40..2b4739fbb4b 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -26,6 +25,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
index e776f7a008a..0b93221e078 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
index 57c2102cc83..9dec7cea47b 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -31,6 +30,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,25, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
index e40f6aa2075..65cfe4e5edb 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
index 6a53b5dc4d2..ee717dcabc4 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
index d59abb6e075..e35777a8db2 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -31,6 +30,5 @@ promise_test(async t => {
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 50,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
index c4feba10ca2..531ff55637a 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
index 5aefc0dab3e..c2ed94a4bbb 100644
--- a/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -24,6 +23,5 @@ promise_test(async t => {
_assertPixel(canvas, 98,1, 0,255,0,255);
_assertPixel(canvas, 1,48, 0,255,0,255);
_assertPixel(canvas, 98,48, 0,255,0,255);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
index cdd501ba8e9..4a4ce5a59a9 100644
--- a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
+++ b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html
@@ -78,63 +78,6 @@ t.step(function() {
{name: 'dropShadow', floodOpacity: [20]});
ctx.filter = new CanvasFilter(
{name: 'dropShadow', floodOpacity: '30'});
- // dx
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: '30'});
- // dy
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: '30'});
- // floodOpacity
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: '30'});
// stdDeviation
ctx.filter = new CanvasFilter(
{name: 'dropShadow', stdDeviation: 10});
diff --git a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js
index 1d56865e687..b51af5757cd 100644
--- a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js
@@ -74,63 +74,6 @@ t.step(function() {
{name: 'dropShadow', floodOpacity: [20]});
ctx.filter = new CanvasFilter(
{name: 'dropShadow', floodOpacity: '30'});
- // dx
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dx: '30'});
- // dy
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', dy: '30'});
- // floodOpacity
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: 10});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: -1});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: 0.5});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: null});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: true});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: false});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: []});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: [20]});
- ctx.filter = new CanvasFilter(
- {name: 'dropShadow', floodOpacity: '30'});
// stdDeviation
ctx.filter = new CanvasFilter(
{name: 'dropShadow', stdDeviation: 10});
diff --git a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html
index 10392dea5ac..45d81a6f2b0 100644
--- a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html
+++ b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html
@@ -78,63 +78,6 @@ t.step(function() {
{name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer();
ctx.beginLayer({filter:
{name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer();
- // dx
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: '30'}}); ctx.endLayer();
- // dy
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: '30'}}); ctx.endLayer();
- // floodOpacity
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer();
// stdDeviation
ctx.beginLayer({filter:
{name: 'dropShadow', stdDeviation: 10}}); ctx.endLayer();
diff --git a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js
index 86b8c56af69..65fc7512154 100644
--- a/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js
@@ -74,63 +74,6 @@ t.step(function() {
{name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer();
ctx.beginLayer({filter:
{name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer();
- // dx
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dx: '30'}}); ctx.endLayer();
- // dy
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', dy: '30'}}); ctx.endLayer();
- // floodOpacity
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: 10}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: -1}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: 0.5}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: null}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: true}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: false}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: []}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer();
- ctx.beginLayer({filter:
- {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer();
// stdDeviation
ctx.beginLayer({filter:
{name: 'dropShadow', stdDeviation: 10}}); ctx.endLayer();
diff --git a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html
index 867553b45e0..cebdfd5f27e 100644
--- a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html
+++ b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html
@@ -10,13 +10,7 @@
<script>
-var t = async_test("Tests that layers ignore the global context filter.");
-var t_pass = t.done.bind(t);
-var t_fail = t.step_func(function(reason) {
- throw reason;
-});
-t.step(function() {
-
+test(t => {
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -51,7 +45,6 @@ t.step(function() {
_assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
_assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
_assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
- t.done();
-});
+}, "Tests that layers ignore the global context filter.");
</script>
diff --git a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js
index 5c2bb73e8f8..a566cccf14b 100644
--- a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js
@@ -6,13 +6,7 @@
importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
-var t = async_test("Tests that layers ignore the global context filter.");
-var t_pass = t.done.bind(t);
-var t_fail = t.step_func(function(reason) {
- throw reason;
-});
-t.step(function() {
-
+test(t => {
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -47,6 +41,5 @@ t.step(function() {
_assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10");
_assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20");
_assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30");
- t.done();
-});
+}, "Tests that layers ignore the global context filter.");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js
index be0f4f2cdee..8361e19108c 100644
--- a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(200, 200);
var ctx = canvas.getContext('2d');
@@ -18,6 +17,5 @@ promise_test(async t => {
// Calling again inside a layer should throw.
ctx.beginLayer();
await promise_rejects_dom(t, 'InvalidStateError', canvas.convertToBlob());
- t.done();
}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js
index b286b7a9e75..d64f6938647 100644
--- a/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(200, 200);
var ctx = canvas.getContext('2d');
@@ -18,6 +17,5 @@ promise_test(async t => {
// Calling again inside a layer should throw.
ctx.beginLayer();
await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(canvas));
- t.done();
}, "Check that exceptions are thrown for operations that are malformed while layers are open.");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js
index 827bd2ce652..dee89275641 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -21,6 +20,5 @@ promise_test(async t => {
ctx.drawImage(img, 0, -50);
_assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
- t.done();
}, "Shadows are drawn correctly for partially-transparent images");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js
index 2d02232c683..7aa17da784e 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -21,6 +20,5 @@ promise_test(async t => {
ctx.drawImage(img, 0, -50);
_assertPixel(canvas, 50,25, 0,255,0,255);
- t.done();
}, "Shadows are drawn for images");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js
index 2c10381f98a..3c1cf872ec2 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Shadows are drawn correctly for scaled images");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js
index 94affdfb6ab..c357a3a3aba 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 50,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Shadows are not drawn for areas outside image source rectangles");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js
index c5421d70426..5440944e931 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -21,6 +20,5 @@ promise_test(async t => {
ctx.drawImage(img, 0, -50);
_assertPixel(canvas, 50,25, 0,255,0,255);
- t.done();
}, "Shadows are not drawn for transparent images");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js
index 071c584fa84..0554c037504 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixel(canvas, 25,25, 0,255,0,255);
_assertPixel(canvas, 50,25, 0,255,0,255);
_assertPixel(canvas, 75,25, 0,255,0,255);
- t.done();
}, "Shadows are not drawn for transparent parts of images");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js
index 41a826a158f..9d5e5b07a9d 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
ctx.fillRect(0, -50, 100, 50);
_assertPixelApprox(canvas, 50,25, 127,0,127,255, 2);
- t.done();
}, "Shadows are drawn correctly for partially-transparent fill patterns");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js
index 7a8f7e84796..d2db2427c5c 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
ctx.fillRect(0, -50, 100, 50);
_assertPixel(canvas, 50,25, 0,255,0,255);
- t.done();
}, "Shadows are drawn for fill patterns");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js
index 8414db6d08c..af079851e62 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
ctx.fillRect(0, -50, 100, 50);
_assertPixel(canvas, 50,25, 0,255,0,255);
- t.done();
}, "Shadows are not drawn for transparent fill patterns");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js
index 726a42d6cd4..1cf4f3ee36b 100644
--- a/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixel(canvas, 25,25, 0,255,0,255);
_assertPixel(canvas, 50,25, 0,255,0,255);
_assertPixel(canvas, 75,25, 0,255,0,255);
- t.done();
}, "Shadows are not drawn for transparent parts of fill patterns");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js
index 44f093cdac2..c11c8437e24 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textAlign center is the center of the em squares (not the bounding box)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js
index 4bd0f5a3147..f5c57694700 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textAlign end with ltr is the right edge");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js
index b3e62d85380..90e21f41dc2 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textAlign end with rtl is the left edge");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js
index c7dc0bed42b..0f929696322 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textAlign left is the left of the first em square (not the bounding box)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js
index 58fe85e0b76..594192812a0 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textAlign right is the right of the last em square (not the bounding box)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js
index c0793d9af6f..32e6fc0f20f 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textAlign start with ltr is the left edge");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js
index ed706c287a1..67a196daf75 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -28,6 +27,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textAlign start with rtl is the right edge");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js
index a98a5863d92..a0ab72915fd 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js
index 30f60c574b9..e1713888341 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textBaseline bottom is the bottom of the em square (not the bounding box)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js
index 638506a2792..906fdbbf27e 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js
index 7c682fb5f14..76ac1d9da2f 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js
index c373ab6c729..c288ab4c866 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textBaseline middle is the middle of the em square (not the bounding box)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js
index effc4418c34..e7d0b167c02 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 5,45, 0,255,0,255, 2);
_assertPixelApprox(canvas, 95,45, 0,255,0,255, 2);
- t.done();
}, "textBaseline top is the top of the em square (not the bounding box)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js
index adcb45faaae..a314870f115 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -24,6 +23,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "fillText handles maxWidth based on line size, not bounding box size");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js
index 8df519b7d1c..e225e703519 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -24,6 +23,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "fillText works on @font-face fonts");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js
index 628cb395069..c35c82d71f0 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -24,6 +23,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "@font-face fonts should work even if they are not used in the page");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js
index 7567c095580..fb1f7980a6c 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -27,6 +26,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js
index 02257f0a06e..a09a0f88d7e 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -24,6 +23,5 @@ promise_test(async t => {
_assertPixelApprox(canvas, 95,5, 0,255,0,255, 2);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js
index ef09925c551..96bd4159c71 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.fillText('E EE', -100, 37.5);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "U+0020 is rendered the correct size (1em wide)");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js
index 9a9afa1e745..e9cd9dc8292 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
ctx.fillText('EE ', 100, 37.5);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
- t.done();
}, "Space characters at the end of a line are NOT collapsed");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js
index a12c2f9a484..a37094e28c8 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.fillText('E\x0b EE', -150, 37.5);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Non-space characters are not converted to U+0020 and collapsed");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js
index bbf9e684be7..1f090cb4262 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.fillText('E \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dEE', 0, 37.5);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
- t.done();
}, "Space characters are converted to U+0020, and are NOT collapsed");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js
index e5ab7b67f4a..02551b7de71 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.fillText('E EE', 0, 37.5);
_assertPixelApprox(canvas, 25,25, 0,255,0,255, 2);
_assertPixelApprox(canvas, 75,25, 255,0,0,255, 2);
- t.done();
}, "Space characters are converted to U+0020, and are NOT collapsed");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js
index 20fec7801f8..bd6e523283f 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.fillText(' EE', 0, 37.5);
_assertPixelApprox(canvas, 25,25, 255,0,0,255, 2);
_assertPixelApprox(canvas, 75,25, 0,255,0,255, 2);
- t.done();
}, "Space characters at the start of a line are NOT collapsed");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js
index c3d18ff8786..aaec2e7e8e0 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -41,6 +40,5 @@ promise_test(async t => {
_assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200");
_assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85");
_assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37");
- t.done();
}, "Testing actualBoundingBox");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js
index 4b02f06f2dd..3bf2d9f719c 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -32,6 +31,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('Hello').advances[2], tm.advances[2], "ctx.measureText('Hello').advances[\""+(2)+"\"]", "tm.advances[\""+(2)+"\"]");
_assertSame(ctx.measureText('Hello').advances[3], tm.advances[3], "ctx.measureText('Hello').advances[\""+(3)+"\"]", "tm.advances[\""+(3)+"\"]");
_assertSame(ctx.measureText('Hello').advances[4], tm.advances[4], "ctx.measureText('Hello').advances[\""+(4)+"\"]", "tm.advances[\""+(4)+"\"]");
- t.done();
}, "Testing width advances");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js
index ddb48f4fe02..6e7b064ac84 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
_assertSame(Math.abs(ctx.measureText('ABCD').alphabeticBaseline), 0, "Math.abs(ctx.measureText('ABCD').alphabeticBaseline)", "0");
_assertSame(ctx.measureText('ABCD').ideographicBaseline, 6.25, "ctx.measureText('ABCD').ideographicBaseline", "6.25");
_assertSame(ctx.measureText('ABCD').hangingBaseline, 25, "ctx.measureText('ABCD').hangingBaseline", "25");
- t.done();
}, "Testing baselines");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js
index 09ee7873092..cbcb5ec0045 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('ABCD').emHeightAscent, 20, "ctx.measureText('ABCD').emHeightAscent", "20");
_assertSame(ctx.measureText('ABCD').emHeightDescent, 20, "ctx.measureText('ABCD').emHeightDescent", "20");
_assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
- t.done();
}, "Testing emHeights with reduced ascent metric");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js
index a2f09782c98..4b62bd56652 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightAscent", "40");
_assertSame(ctx.measureText('ABCD').emHeightDescent, 0, "ctx.measureText('ABCD').emHeightDescent", "0");
_assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
- t.done();
}, "Testing emHeights with zero descent metric");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js
index 0385d3d737d..163394ff1b2 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -25,6 +24,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('ABCD').emHeightAscent, 30, "ctx.measureText('ABCD').emHeightAscent", "30");
_assertSame(ctx.measureText('ABCD').emHeightDescent, 10, "ctx.measureText('ABCD').emHeightDescent", "10");
_assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40");
- t.done();
}, "Testing emHeights");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js
index dea67ff8f67..6ea01b14963 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 10, "ctx.measureText('ABCD').fontBoundingBoxAscent", "10");
_assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
- t.done();
}, "Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js
index 99a886d015c..c8175eec3ce 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
_assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 0, "ctx.measureText('ABCD').fontBoundingBoxDescent", "0");
- t.done();
}, "Testing fontBoundingBox for OffscreenCanvas with zero descent metric");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js
index 255bcd108aa..080a4d45b82 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10");
_assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 40, "ctx.measureText('ABCD').fontBoundingBoxAscent", "40");
_assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
- t.done();
}, "Testing fontBoundingBox for font ahem");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js
index b2cde04c0da..5c28b8f6889 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
_assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30");
_assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10");
- t.done();
}, "Testing fontBoundingBox measurements");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js
index 866ad8b3c5c..f653f20f8e6 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -22,6 +21,5 @@ promise_test(async t => {
ctx.font = '100px CanvasTest';
_assertSame(ctx.measureText('A').width, 100, "ctx.measureText('A').width", "100");
- t.done();
}, "The width of character is same as font used");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js
index 6fb89c5ba73..932b82e129d 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -17,6 +16,5 @@ promise_test(async t => {
await self.fonts.ready;
ctx.font = '50px CanvasTest';
_assertSame(ctx.measureText("").width, 0, "ctx.measureText(\"\").width", "0");
- t.done();
}, "The empty string has zero width");
done();
diff --git a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js
index d832feec5fc..3f0e6d65dad 100644
--- a/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js
+++ b/tests/wpt/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js
@@ -7,7 +7,6 @@ importScripts("/resources/testharness.js");
importScripts("/html/canvas/resources/canvas-tests.js");
promise_test(async t => {
-
var canvas = new OffscreenCanvas(100, 50);
var ctx = canvas.getContext('2d');
@@ -23,6 +22,5 @@ promise_test(async t => {
_assertSame(ctx.measureText(' AB').width, 150, "ctx.measureText(' AB').width", "150");
_assertSame(ctx.measureText('AB ').width, 150, "ctx.measureText('AB ').width", "150");
- t.done();
}, "Space characters are converted to U+0020 and NOT collapsed");
done();
diff --git a/tests/wpt/tests/html/canvas/tools/gentestutilsunion.py b/tests/wpt/tests/html/canvas/tools/gentestutilsunion.py
index 8f6afb1cbde..57077f60572 100644
--- a/tests/wpt/tests/html/canvas/tools/gentestutilsunion.py
+++ b/tests/wpt/tests/html/canvas/tools/gentestutilsunion.py
@@ -29,7 +29,8 @@
#
# * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
-from typing import Any, DefaultDict, List, Mapping, Optional, Set, Tuple
+from typing import Any, DefaultDict, FrozenSet, List, Mapping, MutableMapping
+from typing import Optional, Set, Tuple
import re
import collections
@@ -243,6 +244,7 @@ def _expand_test_code(code: str) -> str:
_TestParams = Mapping[str, Any]
+_MutableTestParams = MutableMapping[str, Any]
class _CanvasType(str, enum.Enum):
@@ -251,8 +253,10 @@ class _CanvasType(str, enum.Enum):
WORKER = 'Worker'
-def _get_enabled_canvas_types(test: _TestParams) -> Set[_CanvasType]:
- return {_CanvasType(t) for t in test.get('canvas_types', _CanvasType)}
+class _TemplateType(str, enum.Enum):
+ REFERENCE = 'reference'
+ HTML_REFERENCE = 'html_reference'
+ TESTHARNESS = 'testharness'
@dataclasses.dataclass
@@ -277,16 +281,16 @@ def _validate_test(test: _TestParams):
f'Invalid canvas size "{test["size"]}" in test {test["name"]}. '
'Expected an array with two numbers.')
- if 'test_type' in test and test['test_type'] != 'promise':
- raise InvalidTestDefinitionError(
- f'Test {test["name"]}\' test_type is invalid, it only accepts '
- '"promise" now for creating promise test type in the template '
- 'file.')
+ if test['template_type'] == _TemplateType.TESTHARNESS:
+ valid_test_types = {'sync', 'async', 'promise'}
+ else:
+ valid_test_types = {'promise'}
- if 'reference' in test and 'html_reference' in test:
+ test_type = test.get('test_type')
+ if test_type is not None and test_type not in valid_test_types:
raise InvalidTestDefinitionError(
- f'Test {test["name"]} is invalid, "reference" and "html_reference" '
- 'can\'t both be specified at the same time.')
+ f'Invalid test_type: {test_type}. '
+ f'Valid values are: {valid_test_types}.')
def _render_template(jinja_env: jinja2.Environment, template: jinja2.Template,
@@ -306,177 +310,212 @@ def _render_template(jinja_env: jinja2.Environment, template: jinja2.Template,
def _render(jinja_env: jinja2.Environment, template_name: str,
- params: _TestParams):
- params = dict(params)
- params.update({
- # Render the code on its own, as it could contain templates expanding
- # to multiple lines. This is needed to get proper indentation of the
- # code in the main template.
- 'code': _render_template(jinja_env,
- jinja_env.from_string(params['code']),
- params)
- })
-
- return _render_template(jinja_env, jinja_env.get_template(template_name),
- params)
-
-
-def _add_default_params(test: _TestParams) -> _TestParams:
- params = {
- 'desc': '',
- 'size': [100, 50],
- 'variant_names': [],
- }
- params.update(test)
- return params
-
-
-def _write_reference_test(jinja_env: jinja2.Environment, params: _TestParams,
- enabled_tests: Set[_CanvasType],
- output_files: _OutputPaths) -> None:
- if _CanvasType.HTML_CANVAS in enabled_tests:
- html_params = dict(params)
- html_params.update({'canvas_type': _CanvasType.HTML_CANVAS.value})
- pathlib.Path(f'{output_files.element}.html').write_text(
- _render(jinja_env, 'reftest_element.html', html_params), 'utf-8')
- if _CanvasType.OFFSCREEN_CANVAS in enabled_tests:
- offscreen_params = dict(params)
- offscreen_params.update(
- {'canvas_type': _CanvasType.OFFSCREEN_CANVAS.value})
- pathlib.Path(f'{output_files.offscreen}.html').write_text(
- _render(jinja_env, 'reftest_offscreen.html', offscreen_params),
- 'utf-8')
- if _CanvasType.WORKER in enabled_tests:
- worker_params = dict(params)
- worker_params.update({'canvas_type': _CanvasType.WORKER.value})
- pathlib.Path(f'{output_files.offscreen}.w.html').write_text(
- _render(jinja_env, 'reftest_worker.html', worker_params), 'utf-8')
-
- js_ref = params.get('reference', '')
- html_ref = params.get('html_reference', '')
- ref_params = dict(params)
- ref_params.update({
- 'is_test_reference': True,
- 'code': js_ref or html_ref
- })
- ref_template_name = 'reftest_element.html' if js_ref else 'reftest.html'
- if _CanvasType.HTML_CANVAS in enabled_tests:
- pathlib.Path(f'{output_files.element}-expected.html').write_text(
- _render(jinja_env, ref_template_name, ref_params), 'utf-8')
- if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER} & enabled_tests:
- pathlib.Path(f'{output_files.offscreen}-expected.html').write_text(
- _render(jinja_env, ref_template_name, ref_params), 'utf-8')
-
-
-def _write_testharness_test(jinja_env: jinja2.Environment, params: _TestParams,
- enabled_tests: Set[_CanvasType],
- output_files: _OutputPaths) -> None:
- # Create test cases for canvas and offscreencanvas.
- if _CanvasType.HTML_CANVAS in enabled_tests:
- html_params = dict(params)
- html_params.update({'canvas_type': _CanvasType.HTML_CANVAS.value})
- pathlib.Path(f'{output_files.element}.html').write_text(
- _render(jinja_env, 'testharness_element.html', html_params),
- 'utf-8')
-
- if _CanvasType.OFFSCREEN_CANVAS in enabled_tests:
- offscreen_params = dict(params)
- offscreen_params.update(
- {'canvas_type': _CanvasType.OFFSCREEN_CANVAS.value})
- pathlib.Path(f'{output_files.offscreen}.html').write_text(
- _render(jinja_env, 'testharness_offscreen.html', offscreen_params),
- 'utf-8')
-
- if _CanvasType.WORKER in enabled_tests:
- worker_params = dict(params)
- worker_params.update({'canvas_type': _CanvasType.WORKER.value})
- pathlib.Path(f'{output_files.offscreen}.worker.js').write_text(
- _render(jinja_env, 'testharness_worker.js', worker_params),
- 'utf-8')
-
-
-def _generate_expected_image(expected: str, name: str,
- enabled_canvas_types: Set[_CanvasType],
- output_dirs: _OutputPaths) -> str:
- """Creates a reference image using Cairo and returns the file location."""
- if expected == 'green':
- return '/images/green-100x50.png'
- if expected == 'clear':
- return '/images/clear-100x50.png'
- if ';' in expected:
- print(f'Found semicolon in {name}')
- expected = re.sub(
- r'^size (\d+) (\d+)',
- r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
- r'\ncr = cairo.Context(surface)', expected)
-
- output_paths = output_dirs.sub_path(name)
- if _CanvasType.HTML_CANVAS in enabled_canvas_types:
- expected_canvas = (
- f'{expected}\n'
- f'surface.write_to_png("{output_paths.element}.png")\n')
- eval(compile(expected_canvas, f'<test {name}>', 'exec'), {},
- {'cairo': cairo})
-
- if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER
- } & enabled_canvas_types:
- expected_offscreen = (
- f'{expected}\n'
- f'surface.write_to_png("{output_paths.offscreen}.png")\n')
- eval(compile(expected_offscreen, f'<test {name}>', 'exec'), {},
- {'cairo': cairo})
-
- return f'{name}.png'
-
-
-def _generate_test(test: _TestParams, jinja_env: jinja2.Environment,
- used_tests: DefaultDict[str, Set[_CanvasType]],
- output_dirs: _OutputPaths) -> None:
- _validate_test(test)
-
- name = test['name']
-
- enabled_canvas_types = _get_enabled_canvas_types(test)
-
- # Render parameters used in the test name.
- name = jinja_env.from_string(name).render(test)
- print(f'\r({name})', ' ' * 32, '\t')
-
- expected_img = None
- if 'expected' in test and test['expected'] is not None:
- expected_img = _generate_expected_image(test['expected'], name,
- enabled_canvas_types,
- output_dirs)
-
- params = dict(test)
- params.update({
- 'code': _expand_test_code(test['code']),
- 'expected_img': expected_img
- })
-
- already_tested = used_tests[name].intersection(enabled_canvas_types)
- if already_tested:
- raise InvalidTestDefinitionError(
- f'Test {name} is defined twice for types {already_tested}')
- used_tests[name].update(enabled_canvas_types)
+ params: _TestParams, output_file_name: str):
+ template = jinja_env.get_template(template_name)
+ file_content = _render_template(jinja_env, template, params)
+ pathlib.Path(output_file_name).write_text(file_content, 'utf-8')
- file_name = name
- if 'manual' in test:
- file_name += '-manual'
- output_files = output_dirs.sub_path(file_name)
- if 'reference' in test or 'html_reference' in test:
- _write_reference_test(jinja_env, params, enabled_canvas_types,
- output_files)
- else:
- _write_testharness_test(jinja_env, params, enabled_canvas_types,
- output_files)
+def _preprocess_code(jinja_env: jinja2.Environment, code: str,
+ params: _TestParams) -> str:
+ code = _expand_test_code(code)
+ # Render the code on its own, as it could contain templates expanding
+ # to multiple lines. This is needed to get proper indentation of the
+ # code in the main template.
+ code = _render_template(jinja_env, jinja_env.from_string(code), params)
+ return code
+
+
+class _Variant():
+
+ def __init__(self, params: _MutableTestParams) -> None:
+ self._params = params
+
+ @property
+ def params(self) -> _TestParams:
+ """Read-only getter for this variant's param dict."""
+ return self._params
+
+ @staticmethod
+ def create_with_defaults(test: _TestParams) -> '_Variant':
+ """Create a _Variant from the specified params.
+
+ Default values are added for certain parameters, if missing."""
+ params = {
+ 'desc': '',
+ 'size': [100, 50],
+ 'variant_names': [],
+ }
+ params.update(test)
+ return _Variant(params)
+
+ def _get_variant_name(self, jinja_env: jinja2.Environment) -> str:
+ name = self.params['name']
+ if self.params.get('append_variants_to_name', True):
+ name = '.'.join([name] + self.params['variant_names'])
+
+ name = jinja_env.from_string(name).render(self.params)
+ return name
+
+ def _get_file_name(self) -> str:
+ file_name = self.params['name']
+
+ if 'manual' in self.params:
+ file_name += '-manual'
+
+ return file_name
+
+ def _get_canvas_types(self) -> FrozenSet[_CanvasType]:
+ canvas_types = self.params.get('canvas_types', _CanvasType)
+ invalid_types = {
+ type
+ for type in canvas_types if type not in list(_CanvasType)
+ }
+ if invalid_types:
+ raise InvalidTestDefinitionError(
+ f'Invalid canvas_types: {list(invalid_types)}. '
+ f'Accepted values are: {[t.value for t in _CanvasType]}')
+ return frozenset(_CanvasType(t) for t in canvas_types)
+
+ def _get_template_type(self) -> _TemplateType:
+ if 'reference' in self.params and 'html_reference' in self.params:
+ raise InvalidTestDefinitionError(
+ f'Test {self.params["name"]} is invalid, "reference" and '
+ '"html_reference" can\'t both be specified at the same time.')
+
+ if 'reference' in self.params:
+ return _TemplateType.REFERENCE
+ if 'html_reference' in self.params:
+ return _TemplateType.HTML_REFERENCE
+ return _TemplateType.TESTHARNESS
+
+ def finalize_params(self, jinja_env: jinja2.Environment) -> None:
+ """Finalize this variant by adding computed param fields."""
+ self._params['name'] = self._get_variant_name(jinja_env)
+ self._params['file_name'] = self._get_file_name()
+ self._params['canvas_types'] = self._get_canvas_types()
+ self._params['template_type'] = self._get_template_type()
+
+ if 'reference' in self._params:
+ self._params['reference'] = _preprocess_code(
+ jinja_env, self._params['reference'], self._params)
+
+ if 'html_reference' in self._params:
+ self._params['html_reference'] = _preprocess_code(
+ jinja_env, self._params['html_reference'], self._params)
+
+ code_params = dict(self.params)
+ if _CanvasType.HTML_CANVAS in self.params['canvas_types']:
+ code_params['canvas_type'] = _CanvasType.HTML_CANVAS.value
+ self._params['code_element'] = _preprocess_code(
+ jinja_env, self._params['code'], code_params)
+
+ if _CanvasType.OFFSCREEN_CANVAS in self.params['canvas_types']:
+ code_params['canvas_type'] = _CanvasType.OFFSCREEN_CANVAS.value
+ self._params['code_offscreen'] = _preprocess_code(
+ jinja_env, self._params['code'], code_params)
+
+ if _CanvasType.WORKER in self.params['canvas_types']:
+ code_params['canvas_type'] = _CanvasType.WORKER.value
+ self._params['code_worker'] = _preprocess_code(
+ jinja_env, self._params['code'], code_params)
+
+ _validate_test(self._params)
+
+ def generate_expected_image(self, output_dirs: _OutputPaths) -> None:
+ """Creates a reference image using Cairo and save filename in params."""
+ if 'expected' not in self.params:
+ return
+
+ expected = self.params['expected']
+ name = self.params['name']
+
+ if expected == 'green':
+ self._params['expected_img'] = '/images/green-100x50.png'
+ return
+ if expected == 'clear':
+ self._params['expected_img'] = '/images/clear-100x50.png'
+ return
+ if ';' in expected:
+ print(f'Found semicolon in {name}')
+ expected = re.sub(
+ r'^size (\d+) (\d+)',
+ r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)'
+ r'\ncr = cairo.Context(surface)', expected)
+
+ output_paths = output_dirs.sub_path(name)
+ if _CanvasType.HTML_CANVAS in self.params['canvas_types']:
+ expected_canvas = (
+ f'{expected}\n'
+ f'surface.write_to_png("{output_paths.element}.png")\n')
+ eval(compile(expected_canvas, f'<test {name}>', 'exec'), {},
+ {'cairo': cairo})
+
+ if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER
+ } & self.params['canvas_types']:
+ expected_offscreen = (
+ f'{expected}\n'
+ f'surface.write_to_png("{output_paths.offscreen}.png")\n')
+ eval(compile(expected_offscreen, f'<test {name}>', 'exec'), {},
+ {'cairo': cairo})
+
+ self._params['expected_img'] = f'{name}.png'
+
+ def _write_reference_test(self, jinja_env: jinja2.Environment,
+ output_files: _OutputPaths):
+ params = dict(self.params)
+ if _CanvasType.HTML_CANVAS in params['canvas_types']:
+ _render(jinja_env, 'reftest_element.html', params,
+ f'{output_files.element}.html')
+ if _CanvasType.OFFSCREEN_CANVAS in params['canvas_types']:
+ _render(jinja_env, 'reftest_offscreen.html', params,
+ f'{output_files.offscreen}.html')
+ if _CanvasType.WORKER in params['canvas_types']:
+ _render(jinja_env, 'reftest_worker.html', params,
+ f'{output_files.offscreen}.w.html')
+
+ params['is_test_reference'] = True
+ is_html_ref = params['template_type'] == _TemplateType.HTML_REFERENCE
+ ref_template = 'reftest.html' if is_html_ref else 'reftest_element.html'
+ if _CanvasType.HTML_CANVAS in params['canvas_types']:
+ _render(jinja_env, ref_template, params,
+ f'{output_files.element}-expected.html')
+ if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER
+ } & params['canvas_types']:
+ _render(jinja_env, ref_template, params,
+ f'{output_files.offscreen}-expected.html')
+
+ def _write_testharness_test(self, jinja_env: jinja2.Environment,
+ output_files: _OutputPaths):
+ # Create test cases for canvas and offscreencanvas.
+ if _CanvasType.HTML_CANVAS in self.params['canvas_types']:
+ _render(jinja_env, 'testharness_element.html', self.params,
+ f'{output_files.element}.html')
+
+ if _CanvasType.OFFSCREEN_CANVAS in self.params['canvas_types']:
+ _render(jinja_env, 'testharness_offscreen.html', self.params,
+ f'{output_files.offscreen}.html')
+
+ if _CanvasType.WORKER in self.params['canvas_types']:
+ _render(jinja_env, 'testharness_worker.js', self.params,
+ f'{output_files.offscreen}.worker.js')
+
+ def generate_test(self, jinja_env: jinja2.Environment,
+ output_dirs: _OutputPaths) -> None:
+ """Generate the test files to the specified output dirs."""
+ output_files = output_dirs.sub_path(self.params['file_name'])
+
+ if self.params['template_type'] in (_TemplateType.REFERENCE,
+ _TemplateType.HTML_REFERENCE):
+ self._write_reference_test(jinja_env, output_files)
+ else:
+ self._write_testharness_test(jinja_env, output_files)
def _recursive_expand_variant_matrix(original_test: _TestParams,
variant_matrix: List[_TestParams],
current_selection: List[Tuple[str, Any]],
- test_variants: List[_TestParams]):
+ test_variants: List[_Variant]):
if len(current_selection) == len(variant_matrix):
# Selection for each variant is done, so add a new test to test_list.
test = dict(original_test)
@@ -484,12 +523,10 @@ def _recursive_expand_variant_matrix(original_test: _TestParams,
for variant_name, variant_params in current_selection:
test.update(variant_params)
variant_name_list.append(variant_name)
- if test.get('append_variants_to_name', True):
- test['name'] += '.' + variant_name
# Expose variant names as a list so they can be used from the yaml
# files, which helps with better naming of tests.
test.update({'variant_names': variant_name_list})
- test_variants.append(test)
+ test_variants.append(_Variant.create_with_defaults(test))
else:
# Continue the recursion with each possible selection for the current
# variant.
@@ -501,7 +538,7 @@ def _recursive_expand_variant_matrix(original_test: _TestParams,
current_selection.pop()
-def _get_variants(test: _TestParams) -> List[_TestParams]:
+def _get_variants(test: _TestParams) -> List[_Variant]:
current_selection = []
test_variants = []
variants = test.get('variants', [])
@@ -523,6 +560,15 @@ def _get_variants(test: _TestParams) -> List[_TestParams]:
return test_variants
+def _check_uniqueness(tested: DefaultDict[str, Set[_CanvasType]], name: str,
+ canvas_types: FrozenSet[_CanvasType]) -> None:
+ already_tested = tested[name].intersection(canvas_types)
+ if already_tested:
+ raise InvalidTestDefinitionError(
+ f'Test {name} is defined twice for types {already_tested}')
+ tested[name].update(canvas_types)
+
+
def generate_test_files(name_to_dir_file: str) -> None:
"""Generate Canvas tests from YAML file definition."""
output_dirs = _OutputPaths(element='../element', offscreen='../offscreen')
@@ -575,10 +621,19 @@ def generate_test_files(name_to_dir_file: str) -> None:
used_tests = collections.defaultdict(set)
for test in tests:
- test = _add_default_params(test)
+ print(test['name'])
for variant in _get_variants(test):
- sub_dir = _get_test_sub_dir(variant['name'], name_to_sub_dir)
- _generate_test(variant, jinja_env, used_tests,
- output_dirs.sub_path(sub_dir))
+ variant.finalize_params(jinja_env)
+ if test['name'] != variant.params['name']:
+ print(f' {variant.params["name"]}')
+
+ sub_dir = _get_test_sub_dir(variant.params['file_name'],
+ name_to_sub_dir)
+ output_sub_dirs = output_dirs.sub_path(sub_dir)
+
+ _check_uniqueness(used_tests, variant.params['name'],
+ variant.params['canvas_types'])
+ variant.generate_expected_image(output_sub_dirs)
+ variant.generate_test(jinja_env, output_sub_dirs)
print()
diff --git a/tests/wpt/tests/html/canvas/tools/templates/reftest.html b/tests/wpt/tests/html/canvas/tools/templates/reftest.html
index 4c9affbbbbf..f68d4c46216 100644
--- a/tests/wpt/tests/html/canvas/tools/templates/reftest.html
+++ b/tests/wpt/tests/html/canvas/tools/templates/reftest.html
@@ -5,7 +5,7 @@
<p class="desc">{{ desc }}</p>
{% if notes %}<p class="notes">{{ notes }}{% endif %}
-{{ code | trim }}
+{{ html_reference | trim }}
{% for image in images %}
<img src="/images/{{ image }}" id="{{ image }}" class="resource">
{% endfor -%}
diff --git a/tests/wpt/tests/html/canvas/tools/templates/reftest_element.html b/tests/wpt/tests/html/canvas/tools/templates/reftest_element.html
index 6684e9c615b..6f7a8c85070 100644
--- a/tests/wpt/tests/html/canvas/tools/templates/reftest_element.html
+++ b/tests/wpt/tests/html/canvas/tools/templates/reftest_element.html
@@ -20,7 +20,8 @@
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
- {{ code | trim | indent(2) }}
+ {{ reference | trim | indent(2) if is_test_reference else
+ code_element | trim | indent(2) }}
{% if test_type == 'promise' %}
document.documentElement.classList.remove("reftest-wait");
{% endif %}
diff --git a/tests/wpt/tests/html/canvas/tools/templates/reftest_offscreen.html b/tests/wpt/tests/html/canvas/tools/templates/reftest_offscreen.html
index 1d0d93dfa16..abc840159f6 100644
--- a/tests/wpt/tests/html/canvas/tools/templates/reftest_offscreen.html
+++ b/tests/wpt/tests/html/canvas/tools/templates/reftest_offscreen.html
@@ -18,7 +18,7 @@
const canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
- {{ code | trim | indent(2) }}
+ {{ code_offscreen | trim | indent(2) }}
const outputCanvas = document.getElementById("canvas");
outputCanvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}).drawImage(canvas, 0, 0);
diff --git a/tests/wpt/tests/html/canvas/tools/templates/reftest_worker.html b/tests/wpt/tests/html/canvas/tools/templates/reftest_worker.html
index e636d3634ba..02281af5d15 100644
--- a/tests/wpt/tests/html/canvas/tools/templates/reftest_worker.html
+++ b/tests/wpt/tests/html/canvas/tools/templates/reftest_worker.html
@@ -18,7 +18,7 @@
const canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
- {{ code | trim | indent(4) }}
+ {{ code_worker | trim | indent(4) }}
const bitmap = canvas.transferToImageBitmap();
self.postMessage(bitmap, bitmap);
diff --git a/tests/wpt/tests/html/canvas/tools/templates/testharness_element.html b/tests/wpt/tests/html/canvas/tools/templates/testharness_element.html
index 56cde7936a9..c8de772ea64 100644
--- a/tests/wpt/tests/html/canvas/tools/templates/testharness_element.html
+++ b/tests/wpt/tests/html/canvas/tools/templates/testharness_element.html
@@ -42,21 +42,29 @@
{% if test_type == 'promise' %}
promise_test(async t => {
- var canvas = document.getElementById('c');
- var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
-{% else %}
+{% elif test_type == 'async' %}
+async_test(t => {
+{% elif test_type == 'sync' %}
+test(t => {
+{% endif %}
+{% if not test_type %}
var t = async_test("{{ desc | double_quote_escape }}");
_addTest(function(canvas, ctx) {
+
+{% else %}
+ var canvas = document.getElementById('c');
+ var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
+
{% endif %}
- {# Test body: #}
- {{ code | trim | indent(2) }}
+ {#- Test body: #}
+ {{ code_element | trim | indent(2) }}
{# Promise vs. async test footer: #}
-{% if test_type == 'promise' %}
-}, "{{ desc }}");
-{% else %}
+{% if not test_type %}
}{% if attributes %}, {{ attributes }}{% endif %});
+{% else %}
+}, "{{ desc | double_quote_escape }}");
{% endif -%}
</script>
diff --git a/tests/wpt/tests/html/canvas/tools/templates/testharness_offscreen.html b/tests/wpt/tests/html/canvas/tools/templates/testharness_offscreen.html
index 8ebbff77a6c..68147680e13 100644
--- a/tests/wpt/tests/html/canvas/tools/templates/testharness_offscreen.html
+++ b/tests/wpt/tests/html/canvas/tools/templates/testharness_offscreen.html
@@ -15,6 +15,11 @@
{#- Promise vs. async test header: +#}
{% if test_type == 'promise' %}
promise_test(async t => {
+
+{% elif test_type == 'async' %}
+async_test(t => {
+{% elif test_type == 'sync' %}
+test(t => {
{% else %}
var t = async_test("{{ desc | double_quote_escape }}");
var t_pass = t.done.bind(t);
@@ -22,22 +27,23 @@ var t_fail = t.step_func(function(reason) {
throw reason;
});
t.step(function() {
+
{% endif %}
-{# Test body: #}
+{#- Test body: #}
var canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
- {{ code | trim | indent(2)}}
+ {{ code_offscreen | trim | indent(2)}}
{#- Promise vs. async test footer: +#}
-{% if test_type == 'promise' %}
-
-}, "{{ desc }}");
-{% else %}
+{% if not test_type %}
t.done();
});
+{% else %}
+
+}, "{{ desc | double_quote_escape }}");
{% endif %}
</script>
{% for svgimage in svgimages %}
diff --git a/tests/wpt/tests/html/canvas/tools/templates/testharness_worker.js b/tests/wpt/tests/html/canvas/tools/templates/testharness_worker.js
index f1f04e7bb6b..570d479a0a6 100644
--- a/tests/wpt/tests/html/canvas/tools/templates/testharness_worker.js
+++ b/tests/wpt/tests/html/canvas/tools/templates/testharness_worker.js
@@ -10,6 +10,10 @@ importScripts("/html/canvas/resources/canvas-tests.js");
{# Promise vs. async test header: #}
{% if test_type == 'promise' %}
promise_test(async t => {
+{% elif test_type == 'async' %}
+async_test(t => {
+{% elif test_type == 'sync' %}
+test(t => {
{% else %}
var t = async_test("{{ desc | double_quote_escape }}");
var t_pass = t.done.bind(t);
@@ -17,19 +21,20 @@ var t_fail = t.step_func(function(reason) {
throw reason;
});
t.step(function() {
+
{% endif %}
-{# Test body: #}
+{#- Test body: #}
var canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }});
var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %});
- {{ code | trim | indent(2)}}
- t.done();
+ {{ code_worker | trim | indent(2)}}
{#- Promise vs. async test footer: +#}
-{% if test_type == 'promise' %}
-}, "{{ desc }}");
-{% else %}
+{% if not test_type %}
+ t.done();
});
+{% else %}
+}, "{{ desc | double_quote_escape }}");
{% endif %}
done();
diff --git a/tests/wpt/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml b/tests/wpt/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml
index 10d7295035e..72d027a634e 100644
--- a/tests/wpt/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml
+++ b/tests/wpt/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml
@@ -2111,7 +2111,7 @@
- name: 2d.gradient.colormix
desc: color-mix works as CanvasGradient color input
- canvasType: ['HtmlCanvas']
+ canvas_types: ['HtmlCanvas']
code: |
var g = ctx.createLinearGradient(0, 0, 100, 0);
g.addColorStop(0, '#f00');
diff --git a/tests/wpt/tests/html/canvas/tools/yaml-new/filters.yaml b/tests/wpt/tests/html/canvas/tools/yaml-new/filters.yaml
index c62eec97cb0..1ce9d8ed740 100644
--- a/tests/wpt/tests/html/canvas/tools/yaml-new/filters.yaml
+++ b/tests/wpt/tests/html/canvas/tools/yaml-new/filters.yaml
@@ -837,10 +837,6 @@
<10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>}") }};
@unroll {{ filter_declaration | replace("param", "{\-
name: 'dropShadow', \-
- <dx | dy | floodOpacity>: \-
- <10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>}") }};
- @unroll {{ filter_declaration | replace("param", "{\-
- name: 'dropShadow', \-
<stdDeviation>: \-
<10 | -1 | 0.5 | null | true | false | [] | [20] | '30' | \-
[10, -1] | [0.5, null] | [true, false] | [[], [20]] | \-
diff --git a/tests/wpt/tests/html/canvas/tools/yaml-new/layers.yaml b/tests/wpt/tests/html/canvas/tools/yaml-new/layers.yaml
index 85816cf75c2..d1e9a970435 100644
--- a/tests/wpt/tests/html/canvas/tools/yaml-new/layers.yaml
+++ b/tests/wpt/tests/html/canvas/tools/yaml-new/layers.yaml
@@ -288,6 +288,7 @@
- name: 2d.layer.layer-rendering-state-reset-in-layer
desc: Tests that layers ignore the global context filter.
+ test_type: sync
code: |
ctx.globalAlpha = 0.5;
ctx.globalCompositeOperation = 'xor';
diff --git a/tests/wpt/tests/html/dom/historical.html b/tests/wpt/tests/html/dom/historical.html
index 396e57a3911..25638104928 100644
--- a/tests/wpt/tests/html/dom/historical.html
+++ b/tests/wpt/tests/html/dom/historical.html
@@ -52,4 +52,9 @@ test(() => {
test(() => {
assert_false("HTMLTableHeaderCellElement" in window);
}, "HTMLTableHeaderCellElement interface is removed")
+
+// removed in https://github.com/whatwg/html/commit/6e4bcf5630d08e03212ad4e1a3c78beecf2a92fa
+test(() => {
+ assert_false("initHashChangeEvent" in HashChangeEvent.prototype);
+}, "HashChangeEvent's initHashChangeEvent method is removed")
</script>
diff --git a/tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-manual.tentative.html b/tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-manual.tentative.html
new file mode 100644
index 00000000000..9e513eb836f
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-manual.tentative.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selection drag and drop: events for contenteditable</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+[data-placeholder]:empty::before { content: attr(data-placeholder); }
+</style>
+<body data-expected-events="
+ b:drop:,
+ a:beforeinput:deleteByDrag,
+ a:input:deleteByDrag,
+ b:beforeinput:insertFromDrop,
+ b:textInput:,
+ b:input:insertFromDrop">
+<div><input id=a data-select="0,7" value="Drag me"></div>
+<div contenteditable=true id=b data-placeholder="...to here"></div>
+<script src="/uievents/textInput/support/common.js"></script>
+<script src="support/events.js"></script>
diff --git a/tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-same-element-manual.tentative.html b/tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-same-element-manual.tentative.html
new file mode 100644
index 00000000000..907306301fa
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/drop/events-contenteditable-same-element-manual.tentative.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selection drag and drop: events for contenteditable (same element)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body data-expected-events="
+ b:drop:,
+ b:beforeinput:deleteByDrag,
+ b:input:deleteByDrag,
+ b:beforeinput:insertFromDrop,
+ b:textInput:,
+ b:input:insertFromDrop">
+<div contenteditable=true id=b data-select="0,7">Drag me ...to here:</div>
+<script src="/uievents/textInput/support/common.js"></script>
+<script src="support/events.js"></script>
diff --git a/tests/wpt/tests/html/editing/dnd/drop/events-input-manual.tentative.html b/tests/wpt/tests/html/editing/dnd/drop/events-input-manual.tentative.html
new file mode 100644
index 00000000000..2f9914cca95
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/drop/events-input-manual.tentative.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selection drag and drop: events for &lt;input></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body data-expected-events="
+ b:drop:,
+ a:beforeinput:deleteByDrag,
+ a:input:deleteByDrag,
+ b:beforeinput:insertFromDrop,
+ b:textInput:,
+ b:input:insertFromDrop">
+<div><input id=a data-select="0,7" value="Drag me"></div>
+<div><input id=b placeholder="...to here"></div>
+<script src="/uievents/textInput/support/common.js"></script>
+<script src="support/events.js"></script>
diff --git a/tests/wpt/tests/html/editing/dnd/drop/events-input-same-element-manual.tentative.html b/tests/wpt/tests/html/editing/dnd/drop/events-input-same-element-manual.tentative.html
new file mode 100644
index 00000000000..8a578d51ad6
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/drop/events-input-same-element-manual.tentative.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selection drag and drop: events for &lt;input> (same element)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body data-expected-events="
+ b:drop:,
+ b:beforeinput:deleteByDrag,
+ b:input:deleteByDrag,
+ b:beforeinput:insertFromDrop,
+ b:textInput:,
+ b:input:insertFromDrop">
+<input id=b data-select="0,7" value="Drag me ...to here:">
+<script src="/uievents/textInput/support/common.js"></script>
+<script src="support/events.js"></script>
diff --git a/tests/wpt/tests/html/editing/dnd/drop/events-textarea-manual.tentative.html b/tests/wpt/tests/html/editing/dnd/drop/events-textarea-manual.tentative.html
new file mode 100644
index 00000000000..7fb8bf437f8
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/drop/events-textarea-manual.tentative.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selection drag and drop: events for &lt;textarea></title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body data-expected-events="
+ b:drop:,
+ a:beforeinput:deleteByDrag,
+ a:input:deleteByDrag,
+ b:beforeinput:insertFromDrop,
+ b:textInput:,
+ b:input:insertFromDrop">
+<div><input id=a data-select="0,7" value="Drag me"></div>
+<div><textarea id=b placeholder="...to here"></textarea></div>
+<script src="/uievents/textInput/support/common.js"></script>
+<script src="support/events.js"></script>
diff --git a/tests/wpt/tests/html/editing/dnd/drop/events-textarea-same-element-manual.tentative.html b/tests/wpt/tests/html/editing/dnd/drop/events-textarea-same-element-manual.tentative.html
new file mode 100644
index 00000000000..c856fd4fbea
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/drop/events-textarea-same-element-manual.tentative.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selection drag and drop: events for &lt;textarea> (same element)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body data-expected-events="
+ b:drop:,
+ b:beforeinput:deleteByDrag,
+ b:input:deleteByDrag,
+ b:beforeinput:insertFromDrop,
+ b:textInput:,
+ b:input:insertFromDrop">
+<textarea id=b data-select="0,7">Drag me ...to here:</textarea>
+<script src="/uievents/textInput/support/common.js"></script>
+<script src="support/events.js"></script>
diff --git a/tests/wpt/tests/html/editing/dnd/drop/support/events.js b/tests/wpt/tests/html/editing/dnd/drop/support/events.js
new file mode 100644
index 00000000000..015cead3853
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/drop/support/events.js
@@ -0,0 +1,31 @@
+setup({ explicit_timeout: true, single_test: true });
+function rAF() {
+ return new Promise(resolve => {
+ requestAnimationFrame(resolve);
+ });
+}
+const a = document.getElementById('a');
+const b = document.getElementById('b');
+const actualEvents = [];
+const expectedEvents = document.body.dataset.expectedEvents.replace(/\s+/g, '').split(',');
+const eventTypes = new Set(expectedEvents.map(s => s.split(':')[1]));
+for (const eventType of eventTypes) {
+ if (a) {
+ a.addEventListener(eventType, e => {
+ actualEvents.push(`a:${e.type}:${e.inputType || ''}`);
+ });
+ }
+ b.addEventListener(eventType, async (e) => {
+ actualEvents.push(`b:${e.type}:${e.inputType || ''}`);
+ if (e.type === "input") {
+ await rAF();
+ await rAF();
+ assert_array_equals(actualEvents, expectedEvents);
+ done();
+ }
+ });
+}
+const dragMeElement = document.querySelector('[data-select]');
+const [selectionStart, selectionEnd] = dragMeElement.dataset.select.split(',').map(s => parseInt(s, 10));
+setSelection(dragMeElement, selectionStart, selectionEnd);
+dragMeElement.focus();
diff --git a/tests/wpt/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js b/tests/wpt/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js
index 74c4c2e9975..8422ea4b3c9 100644
--- a/tests/wpt/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js
+++ b/tests/wpt/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js
@@ -201,8 +201,6 @@ onload = function() {
var video_ext = '';
if (elm.canPlayType('video/webm; codecs="vp9,opus"')) {
video_ext = 'webm';
- } else if (elm.canPlayType('video/ogg; codecs="theora,flac"')) {
- video_ext = 'ogv';
} else if (elm.canPlayType('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) {
video_ext = 'mp4';
}
diff --git a/tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents-ref.html b/tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents-ref.html
new file mode 100644
index 00000000000..c20a3065e44
--- /dev/null
+++ b/tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<title>button (in button layout) with display: none/contents</title>
+2 \ No newline at end of file
diff --git a/tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents.html b/tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents.html
new file mode 100644
index 00000000000..8f0ad3ab29c
--- /dev/null
+++ b/tests/wpt/tests/html/rendering/widgets/button-layout/display-none-or-contents.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>button (in button layout) with display: none/contents</title>
+<link rel=match href=display-none-or-contents-ref.html>
+<link rel=help href=https://html.spec.whatwg.org/multipage/rendering.html#button-layout-2>
+<style>
+#none{ display: none}
+#contents { display: contents; font: initial }
+</style>
+<!-- Button layout should not impact "display: none" or "display: contents" on button elements -->
+<!-- https://github.com/whatwg/html/pull/10244 -->
+<button id=none>1</button>
+<button id=contents>2</button> \ No newline at end of file
diff --git a/tests/wpt/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html b/tests/wpt/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html
index 56edf25aa8f..855f02d3b19 100644
--- a/tests/wpt/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html
+++ b/tests/wpt/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html
@@ -55,10 +55,13 @@ function type_codecs_test(type, audioCodecs, videoCodecs) {
var typeSupported = false;
var codecSupported = false;
+ var mimeSupported = canPlayType(type);
+
// Test 'type' without codecs.
// Spec: Generally, a user agent should never return "probably" for a type
// that allows the codecs parameter if that parameter is not present.
test(function() {
+ assert_implements_optional(mimeSupported, type)
t(type, 'maybe');
t(type + ';', 'maybe');
t(type + ';codecs', 'maybe');
@@ -69,6 +72,7 @@ function type_codecs_test(type, audioCodecs, videoCodecs) {
function test_codec(codec) {
var typeWithCodec = mime(type, [codec]);
test(function() {
+ assert_implements_optional(canPlayType(typeWithCodec), type)
t(typeWithCodec, 'probably');
codecSupported = true;
}, typeWithCodec + ' (optional)');
@@ -81,6 +85,7 @@ function type_codecs_test(type, audioCodecs, videoCodecs) {
// Test different pairings and orderings of audio+video codecs.
if (audioCodecs.length > 0 && videoCodecs.length > 0) {
test(function() {
+ assert_implements_optional(mimeSupported, type)
audioCodecs.forEach(function(ac) {
videoCodecs.forEach(function(vc) {
var canPlayBoth = canPlayType(mime(type, [ac, vc]));
@@ -93,6 +98,7 @@ function type_codecs_test(type, audioCodecs, videoCodecs) {
}, type + ' codecs subset');
test(function() {
+ assert_implements_optional(mimeSupported, type)
audioCodecs.forEach(function(ac) {
videoCodecs.forEach(function(vc) {
assert_equals(canPlayType(mime(type, [ac, vc])),
diff --git a/tests/wpt/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html b/tests/wpt/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html
index 3b4c3542a92..1a8aabcaff1 100644
--- a/tests/wpt/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html
+++ b/tests/wpt/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html
@@ -4,7 +4,7 @@
<script src="/resources/testharnessreport.js"></script>
<video>
<source src="/media/test.mp4" type="video/mp4">
- <source src="/media/test.ogv" type="video/ogg">
+ <source src="/media/test.webm" type="video/webm">
</video>
<script>
test(function() {
@@ -13,4 +13,4 @@ test(function() {
track.addCue(new VTTCue(0.0, 10.0, 'wow wow'));
track.mode = 'showing';
});
-</script> \ No newline at end of file
+</script>
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html
index 4e984de2740..94d7fd53b34 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html
@@ -7,7 +7,7 @@
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
-<select style="appearance:bikeshed">
+<select style="appearance:base-select">
<button type=popover>one</button>
<option>one</option>
<option>two</option>
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html
index 9182395e9e6..87425cf7a32 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html
@@ -7,7 +7,7 @@
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
-<select style="appearance:bikeshed">
+<select style="appearance:base-select">
<datalist>
<option>one</option>
<option>two</option>
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html
index b3e028097b3..b2a6b5a6d39 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html
@@ -7,7 +7,7 @@
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
-<select style="appearance:bikeshed">
+<select style="appearance:base-select">
<option>one</option>
<option>two</option>
</select>
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html
index 6d3419808bd..b6d85ac90af 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html
@@ -25,7 +25,7 @@
<script>
requestAnimationFrame(() => {
- document.querySelector('select').style = 'appearance:bikeshed';
+ document.querySelector('select').style = 'appearance:base-select';
document.querySelector('button').click();
document.documentElement.classList.remove('reftest-wait');
});
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html
index aeb4cd75ac4..610861aad8a 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html
@@ -10,7 +10,7 @@
}
</style>
-<select style="appearance:bikeshed">
+<select style="appearance:base-select">
<button type=popover>button</button>
<datalist>
<option>
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html
index c53440b8c60..2fb11ba68b0 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html
@@ -11,7 +11,7 @@
<style>
select {
- appearance: bikeshed;
+ appearance: base-select;
}
</style>
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html
index b9161874b78..aff976d1ad5 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html
@@ -11,7 +11,7 @@
<style>
select {
- appearance: bikeshed;
+ appearance: base-select;
}
</style>
@@ -41,7 +41,7 @@ promise_test(async () => {
await test_driver.click(button);
assert_false(select.matches(':open'),
'Select should be closed after clicking the button a second time.');
-}, 'Select with appearance:bikeshed should open and close when clicking the button.');
+}, 'Select with appearance:base-select should open and close when clicking the button.');
promise_test(async () => {
assert_false(select.matches(':open'),
@@ -58,5 +58,5 @@ promise_test(async () => {
'Select should be closed after clicking an option.');
assert_equals(select.value, 'two',
'Select.value should be two after clicking the option.');
-}, 'Clicking an option in an appearance:bikeshed select should choose the option and close the popover.');
+}, 'Clicking an option in an appearance:base-select select should choose the option and close the popover.');
</script>
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html
index 41ed34a9a2a..16d711515c9 100644
--- a/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html
@@ -7,7 +7,7 @@
<script src="/resources/testdriver-vendor.js"></script>
<form>
- <select style="appearance:bikeshed">
+ <select style="appearance:base-select">
<button>
<selectedoption></selectedoption>
</button>
diff --git a/tests/wpt/tests/html/semantics/invokers/interestelement-interface.tentative.html b/tests/wpt/tests/html/semantics/invokers/interestelement-interface.tentative.html
index dc119de8338..8b1e3756955 100644
--- a/tests/wpt/tests/html/semantics/invokers/interestelement-interface.tentative.html
+++ b/tests/wpt/tests/html/semantics/invokers/interestelement-interface.tentative.html
@@ -1,5 +1,6 @@
<!doctype html>
<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
<script src="/resources/testharness.js"></script>
diff --git a/tests/wpt/tests/html/semantics/invokers/interestevent-dispatch-shadow.tentative.html b/tests/wpt/tests/html/semantics/invokers/interestevent-dispatch-shadow.tentative.html
new file mode 100644
index 00000000000..d96907ec84a
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/invokers/interestevent-dispatch-shadow.tentative.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
+<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="div"></div>
+<button id="button"></button>
+
+<script>
+ test(function () {
+ const host = document.createElement("div");
+ const shadow = host.attachShadow({ mode: "closed" });
+ const slot = shadow.appendChild(document.createElement("slot"));
+ let childEvent = null;
+ let childEventTarget = null;
+ let childEventInvoker = null;
+ let hostEvent = null;
+ let hostEventTarget = null;
+ let hostEventInvoker = null;
+ slot.addEventListener(
+ "interest",
+ (e) => {
+ childEvent = e;
+ childEventTarget = e.target;
+ childEventInvoker = e.invoker;
+ },
+ { once: true },
+ );
+ host.addEventListener(
+ "interest",
+ (e) => {
+ hostEvent = e;
+ hostEventTarget = e.target;
+ hostEventInvoker = e.invoker;
+ },
+ { once: true },
+ );
+ const event = new InterestEvent("interest", {
+ bubbles: true,
+ invoker: slot,
+ composed: true,
+ });
+ slot.dispatchEvent(event);
+ assert_true(childEvent instanceof InterestEvent, "slot saw interest event");
+ assert_equals(
+ childEventTarget,
+ slot,
+ "target is child inside shadow boundary",
+ );
+ assert_equals(
+ childEventInvoker,
+ slot,
+ "invoker is child inside shadow boundary",
+ );
+ assert_equals(
+ hostEvent,
+ childEvent,
+ "event dispatch propagates across shadow boundary",
+ );
+ assert_equals(
+ hostEventTarget,
+ host,
+ "target is retargeted to shadowroot host",
+ );
+ assert_equals(
+ hostEventInvoker,
+ host,
+ "invoker is retargeted to shadowroot host",
+ );
+ }, "InterestEvent propagates across shadow boundaries retargeting invoker");
+
+ test(function (t) {
+ const host = document.createElement("div");
+ document.body.append(host);
+ t.add_cleanup(() => host.remove());
+ const shadow = host.attachShadow({ mode: "open" });
+ const button = shadow.appendChild(document.createElement("button"));
+ const interestee = host.appendChild(document.createElement("div"));
+ button.interestTargetElement = interestee;
+ let event = null;
+ let eventTarget = null;
+ let eventInvoker = null;
+ interestee.addEventListener(
+ "interest",
+ (e) => {
+ event = e;
+ eventTarget = e.target;
+ eventInvoker = e.invoker;
+ },
+ { once: true },
+ );
+ button.focus();
+ assert_true(event instanceof InterestEvent);
+ assert_equals(eventTarget, interestee, "target is interestee");
+ assert_equals(eventInvoker, host, "interestee is host");
+ }, "cross shadow InterestEvent retargets interestee to host element");
+</script>
diff --git a/tests/wpt/tests/html/semantics/invokers/interestevent-interface.tentative.html b/tests/wpt/tests/html/semantics/invokers/interestevent-interface.tentative.html
new file mode 100644
index 00000000000..ed7d82f1fb7
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/invokers/interestevent-interface.tentative.html
@@ -0,0 +1,167 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
+<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="div"></div>
+<button id="button"></button>
+
+<script>
+ test(function () {
+ const event = new InterestEvent("test");
+ assert_equals(event.action, "");
+ assert_readonly(event, "action", "readonly attribute value");
+ }, "action is a readonly defaulting to ''");
+
+ test(function () {
+ const event = new InterestEvent("test");
+ assert_equals(event.invoker, null);
+ assert_readonly(event, "invoker", "readonly attribute value");
+ }, "invoker is readonly defaulting to null");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: "sAmPle" });
+ assert_equals(event.action, "sAmPle");
+ }, "action reflects initialized attribute");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: undefined });
+ assert_equals(event.action, "");
+ }, "action set to undefined");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: null });
+ assert_equals(event.action, "null");
+ }, "action set to null");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: false });
+ assert_equals(event.action, "false");
+ }, "action set to false");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: "" });
+ assert_equals(event.action, "");
+ }, "action explicitly set to empty string");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: true });
+ assert_equals(event.action, "true");
+ }, "action set to true");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: 0.5 });
+ assert_equals(event.action, "0.5");
+ }, "action set to a number");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: [] });
+ assert_equals(event.action, "");
+ }, "action set to []");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: [1, 2, 3] });
+ assert_equals(event.action, "1,2,3");
+ }, "action set to [1, 2, 3]");
+
+ test(function () {
+ const event = new InterestEvent("test", { action: { sample: 0.5 } });
+ assert_equals(event.action, "[object Object]");
+ }, "action set to an object");
+
+ test(function () {
+ const event = new InterestEvent("test", {
+ action: {
+ toString() {
+ return "sample";
+ },
+ },
+ });
+ assert_equals(event.action, "sample");
+ }, "action set to an object with a toString function");
+
+ test(function () {
+ const eventInit = { action: "sample", invoker: document.body };
+ const event = new InterestEvent("test", eventInit);
+ assert_equals(event.action, "sample");
+ assert_equals(event.invoker, document.body);
+ }, "InterestEventInit properties set value");
+
+ test(function () {
+ const eventInit = {
+ action: "open",
+ invoker: document.getElementById("div"),
+ };
+ const event = new InterestEvent("beforetoggle", eventInit);
+ assert_equals(event.action, "open");
+ assert_equals(event.invoker, document.getElementById("div"));
+ }, "InterestEventInit properties set value 2");
+
+ test(function () {
+ const eventInit = {
+ action: "closed",
+ invoker: document.getElementById("button"),
+ };
+ const event = new InterestEvent("toggle", eventInit);
+ assert_equals(event.action, "closed");
+ assert_equals(event.invoker, document.getElementById("button"));
+ }, "InterestEventInit properties set value 3");
+
+ test(function () {
+ const event = new InterestEvent("test", { invoker: undefined });
+ assert_equals(event.invoker, null);
+ }, "invoker set to undefined");
+
+ test(function () {
+ const event = new InterestEvent("test", { invoker: null });
+ assert_equals(event.invoker, null);
+ }, "invoker set to null");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ new InterestEvent("test", { invoker: false });
+ },
+ "invoker is not an object",
+ );
+ }, "invoker set to false");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ const event = new InterestEvent("test", { invoker: true });
+ },
+ "invoker is not an object",
+ );
+ }, "invoker set to true");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ const event = new InterestEvent("test", { invoker: {} });
+ },
+ "invoker is not an object",
+ );
+ }, "invoker set to {}");
+
+ test(function () {
+ assert_throws_js(
+ TypeError,
+ function () {
+ const eventInit = { action: "closed", invoker: new XMLHttpRequest() };
+ const event = new InterestEvent("toggle", eventInit);
+ },
+ "invoker is not an Element",
+ );
+ }, "invoker set to non-Element EventTarget");
+</script>
diff --git a/tests/wpt/tests/html/semantics/invokers/interesttarget-button-event-dispatch.tentative.html b/tests/wpt/tests/html/semantics/invokers/interesttarget-button-event-dispatch.tentative.html
new file mode 100644
index 00000000000..7fdfdfaa707
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/invokers/interesttarget-button-event-dispatch.tentative.html
@@ -0,0 +1,155 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="interestee"></div>
+<button id="interestbutton" interesttarget="interestee">Button</button>
+<a href="/" id="interestanchor" interesttarget="interestee">Anchor</a>
+<button id="otherbutton">Other Button</button>
+
+<script>
+ promise_test(async function (t) {
+ t.add_cleanup(() => otherbutton.focus());
+ let event = null;
+ interestee.addEventListener("interest", (e) => (event = e), { once: true });
+ interestbutton.focus();
+ assert_true(event instanceof InterestEvent, "event is InterestEvent");
+ assert_equals(event.type, "interest", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "", "action");
+ assert_equals(event.target, interestee, "target");
+ assert_equals(event.invoker, interestbutton, "invoker");
+ }, "InterestEvent dispatches on button focus");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => otherbutton.focus());
+ let event = null;
+ interestee.addEventListener("interest", (e) => (event = e), { once: true });
+ await hoverOver(interestbutton);
+ assert_true(event instanceof InterestEvent, "event is InterestEvent");
+ assert_equals(event.type, "interest", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "", "action");
+ assert_equals(event.target, interestee, "target");
+ assert_equals(event.invoker, interestbutton, "invoker");
+ }, "InterestEvent dispatches on button hover");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => otherbutton.focus());
+ let event = null;
+ interestee.addEventListener("interest", (e) => (event = e), { once: true });
+ interestanchor.focus();
+ assert_true(event instanceof InterestEvent, "event is InterestEvent");
+ assert_equals(event.type, "interest", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "", "action");
+ assert_equals(event.target, interestee, "target");
+ assert_equals(event.invoker, interestanchor, "invoker");
+ }, "InterestEvent dispatches on anchor focus");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => otherbutton.focus());
+ let event = null;
+ interestee.addEventListener("interest", (e) => (event = e), { once: true });
+ await hoverOver(interestanchor);
+ assert_true(event instanceof InterestEvent, "event is InterestEvent");
+ assert_equals(event.type, "interest", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "", "action");
+ assert_equals(event.target, interestee, "target");
+ assert_equals(event.invoker, interestanchor, "invoker");
+ }, "InterestEvent dispatches on anchor hover");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => otherbutton.focus());
+ let event = null;
+ interestee.addEventListener("interest", (e) => (event = e), { once: true });
+ interestbutton.interestAction = "fooBar";
+ interestbutton.focus();
+ assert_true(event instanceof InterestEvent, "event is InterestEvent");
+ assert_equals(event.type, "interest", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "fooBar", "action");
+ assert_equals(event.target, interestee, "target");
+ assert_equals(event.invoker, interestbutton, "invoker");
+ }, "event action is set to interestAction");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => otherbutton.focus());
+ let event = null;
+ interestee.addEventListener("interest", (e) => (event = e), { once: true });
+ interestbutton.setAttribute("interestaction", "BaRbAz");
+ interestbutton.focus();
+ assert_true(event instanceof InterestEvent, "event is InterestEvent");
+ assert_equals(event.type, "interest", "type");
+ assert_equals(event.bubbles, false, "bubbles");
+ assert_equals(event.composed, true, "composed");
+ assert_equals(event.isTrusted, true, "isTrusted");
+ assert_equals(event.action, "BaRbAz", "action");
+ assert_equals(event.target, interestee, "target");
+ assert_equals(event.invoker, interestbutton, "invoker");
+ }, "event action is set to interestaction attribute");
+
+ promise_test(async function (t) {
+ t.add_cleanup(() => {
+ interestbutton.removeAttribute('disabled');
+ otherbutton.focus();
+ });
+ let called = false;
+ interestee.addEventListener(
+ "interest",
+ () => {
+ called = true;
+ },
+ { once: true },
+ );
+ interestbutton.setAttribute("disabled", "");
+ interestbutton.focus();
+ assert_false(called, "event was not called");
+ }, "event does not dispatch if invoker is disabled");
+
+ promise_test(async function (t) {
+ svgInterestee = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ t.add_cleanup(() => {
+ interestbutton.interestTargetElement = interestee;
+ svgInterestee.remove();
+ otherbutton.focus();
+ });
+ document.body.append(svgInterestee);
+ let called = false;
+ assert_false(svgInterestee instanceof HTMLElement);
+ assert_true(svgInterestee instanceof Element);
+ let event = null;
+ svgInterestee.addEventListener(
+ "interest",
+ (e) => {
+ event = e;
+ called = true;
+ },
+ { once: true },
+ );
+ interestbutton.interestTargetElement = svgInterestee;
+ interestbutton.focus();
+ assert_true(called, "event was called");
+ assert_equals(event.invoker, interestbutton, "event.invoker is set to right element");
+ assert_equals(event.target, svgInterestee, "event.target is set to right element");
+ }, "event dispatches if interestee is non-HTML Element");
+</script>
diff --git a/tests/wpt/tests/html/semantics/invokers/interesttarget-on-popover-behavior.tentative.html b/tests/wpt/tests/html/semantics/invokers/interesttarget-on-popover-behavior.tentative.html
new file mode 100644
index 00000000000..b930fc645dd
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/invokers/interesttarget-on-popover-behavior.tentative.html
@@ -0,0 +1,113 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
+<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
+<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<div id="interestee" popover>
+ Popover Content
+</div>
+<button id="interestbutton" interesttarget="interestee">Interest Invoker</button>
+<button id="otherbutton">Other button</button>
+
+<script>
+ function reset() {
+ hoverOver(otherbutton);
+ otherbutton.focus();
+ interestee.hidePopover();
+ interestbutton.removeAttribute("interestaction");
+ }
+
+ // auto
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_false(interestee.matches(":popover-open"));
+ await hoverOver(interestbutton);
+ assert_true(interestee.matches(":popover-open"));
+ }, "hover interest invoking (as auto) closed popover opens");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ interestee.showPopover();
+ assert_true(interestee.matches(":popover-open"));
+ await hoverOver(interestbutton);
+ assert_false(interestee.matches(":popover-open"));
+ }, "hover interest invoking (as auto) open popover closes");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_false(interestee.matches(":popover-open"));
+ interestbutton.focus();
+ assert_true(interestee.matches(":popover-open"));
+ }, "focus interest invoking (as auto) closed popover opens");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ interestee.showPopover();
+ assert_true(interestee.matches(":popover-open"));
+ interestbutton.focus();
+ assert_false(interestee.matches(":popover-open"));
+ }, "focus interest invoking (as auto) open popover closes");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_false(interestee.matches(":popover-open"));
+ interestee.addEventListener("interest", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await hoverOver(interestbutton);
+ assert_false(interestee.matches(":popover-open"));
+ }, "interest invoking (as auto) closed popover with preventDefault does not open");
+
+ // togglepopover
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_false(interestee.matches(":popover-open"));
+ interestbutton.setAttribute("interestaction", "togglepopover");
+ await hoverOver(interestbutton);
+ assert_true(interestee.matches(":popover-open"));
+ }, "hover interest invoking (as togglepopover) closed popover opens");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ interestee.showPopover();
+ assert_true(interestee.matches(":popover-open"));
+ interestbutton.setAttribute("interestaction", "togglepopover");
+ await hoverOver(interestbutton);
+ assert_false(interestee.matches(":popover-open"));
+ }, "hover interest invoking (as togglepopover) open popover closes");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_false(interestee.matches(":popover-open"));
+ interestbutton.setAttribute("interestaction", "togglepopover");
+ interestbutton.focus();
+ assert_true(interestee.matches(":popover-open"));
+ }, "focus interest invoking (as togglepopover) closed popover opens");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ interestee.showPopover();
+ assert_true(interestee.matches(":popover-open"));
+ interestbutton.setAttribute("interestaction", "togglepopover");
+ interestbutton.focus();
+ assert_false(interestee.matches(":popover-open"));
+ }, "focus interest invoking (as togglepopover) open popover closes");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_false(interestee.matches(":popover-open"));
+ interestbutton.setAttribute("interestaction", "tOgGlEpOpOvEr");
+ interestbutton.focus();
+ assert_true(interestee.matches(":popover-open"));
+ }, "interest invoking (as togglepopover - case insensitive) closed popover opens");
+
+</script>
diff --git a/tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html b/tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html
new file mode 100644
index 00000000000..9f73e092b03
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html
@@ -0,0 +1,394 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
+<meta name="timeout" content="long">
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<dialog id="invokee">
+ <button id="containedinvoker" invoketarget="invokee"></button>
+</dialog>
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ function resetState() {
+ invokee.close();
+ try { invokee.hidePopover(); } catch {}
+ invokee.removeAttribute("popover");
+ invokerbutton.removeAttribute("invokeaction");
+ containedinvoker.removeAttribute("invokeaction");
+ }
+
+ // opening a dialog
+
+ [null, "", "showmodal", /* test case sensitivity */ "sHoWmOdAl"].forEach(
+ (action) => {
+ ["property", "attribute"].forEach((setType) => {
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ if (typeof action === "string") {
+ if (setType === "property") {
+ invokerbutton.invokeaction = action;
+ } else {
+ invokerbutton.setAttribute("invokeaction", action);
+ }
+ }
+ await clickOn(invokerbutton);
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) closed dialog opens as modal`,
+ );
+
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ if (typeof action === "string") {
+ if (setType === "property") {
+ invokerbutton.invokeaction = action;
+ } else {
+ invokerbutton.setAttribute("invokeaction", action);
+ }
+ }
+ await clickOn(invokerbutton);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) closed dialog with preventDefault is noop`,
+ );
+
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ invokee.addEventListener(
+ "invoke",
+ (e) => {
+ invokerbutton.setAttribute("invokeaction", "close");
+ },
+ { once: true },
+ );
+ if (typeof action === "string") {
+ if (setType === "property") {
+ invokerbutton.invokeaction = action;
+ } else {
+ invokerbutton.setAttribute("invokeaction", action);
+ }
+ }
+ await clickOn(invokerbutton);
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) while changing action still opens as modal`,
+ );
+ });
+ },
+ );
+
+ // closing an already open dialog
+
+ [null, "", "close", /* test case sensitivity */ "cLoSe"].forEach((action) => {
+ ["property", "attribute"].forEach((setType) => {
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ invokee.show();
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ if (typeof action === "string") {
+ if (setType === "property") {
+ containedinvoker.invokeaction = action;
+ } else {
+ containedinvoker.setAttribute("invokeaction", action);
+ }
+ }
+ await clickOn(containedinvoker);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking to close (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) open dialog closes`,
+ );
+
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ invokee.show();
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ if (typeof action === "string") {
+ if (setType === "property") {
+ containedinvoker.invokeaction = action;
+ } else {
+ containedinvoker.setAttribute("invokeaction", action);
+ }
+ }
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(containedinvoker);
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking to close (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) open dialog with preventDefault is no-op`,
+ );
+
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ invokee.showModal();
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ if (typeof action === "string") {
+ if (setType === "property") {
+ containedinvoker.invokeaction = action;
+ } else {
+ containedinvoker.setAttribute("invokeaction", action);
+ }
+ }
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(containedinvoker);
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking to close (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) open modal dialog with preventDefault is no-op`,
+ );
+
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ invokee.show();
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ if (typeof action === "string") {
+ if (setType === "property") {
+ containedinvoker.invokeaction = action;
+ } else {
+ containedinvoker.setAttribute("invokeaction", action);
+ }
+ }
+ invokee.addEventListener(
+ "invoke",
+ (e) => {
+ containedinvoker.setAttribute("invokeaction", "show");
+ },
+ { once: true },
+ );
+ await clickOn(containedinvoker);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking to close (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) open dialog while changing action still closes`,
+ );
+
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ invokee.showModal();
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ if (typeof action === "string") {
+ if (setType === "property") {
+ containedinvoker.invokeaction = action;
+ } else {
+ containedinvoker.setAttribute("invokeaction", action);
+ }
+ }
+ invokee.addEventListener(
+ "invoke",
+ (e) => {
+ containedinvoker.setAttribute("invokeaction", "show");
+ },
+ { once: true },
+ );
+ await clickOn(containedinvoker);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking to close (with invokeaction ${setType} as ${
+ action == null ? "auto" : action || "explicit empty"
+ }) open modal dialog while changing action still closes`,
+ );
+ });
+ });
+
+ // showmodal explicit behaviours
+
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ containedinvoker.setAttribute("invokeaction", "showModal");
+ invokee.show();
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ await clickOn(containedinvoker);
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ }, "invoking (as showmodal) open dialog is noop");
+
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ containedinvoker.setAttribute("invokeaction", "showmodal");
+ invokee.showModal();
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ invokee.addEventListener(
+ "invoke",
+ (e) => {
+ containedinvoker.setAttribute("invokeaction", "close");
+ },
+ { once: true },
+ );
+ await clickOn(invokerbutton);
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ }, "invoking (as showmodal) open modal, while changing action still a no-op");
+
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ invokerbutton.setAttribute("invokeaction", "showmodal");
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ invokee.setAttribute("popover", "auto");
+ await clickOn(invokerbutton);
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ }, "invoking (as showmodal) closed popover dialog opens as modal");
+
+ // close explicit behaviours
+
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ invokerbutton.setAttribute("invokeaction", "close");
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ await clickOn(containedinvoker);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ }, "invoking (as close) already closed dialog is noop");
+
+ // Open Popovers using Dialog actions
+ ["showmodal", "close", ""].forEach((action) => {
+ ["manual", "auto"].forEach((popoverState) => {
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ invokee.setAttribute("popover", popoverState);
+ invokee.showPopover();
+ containedinvoker.setAttribute("invokeaction", action);
+ assert_true(
+ invokee.matches(":popover-open"),
+ "invokee :popover-open",
+ );
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(containedinvoker);
+ assert_true(
+ invokee.matches(":popover-open"),
+ "invokee :popover-open",
+ );
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (as ${
+ action || "explicit empty"
+ }) dialog as open popover=${popoverState} is noop`,
+ );
+ });
+ });
+
+ // Elements being disconnected during invoke steps
+ ["showmodal", "close", ""].forEach((action) => {
+ promise_test(
+ async function (t) {
+ t.add_cleanup(() => {
+ document.body.prepend(invokee);
+ resetState();
+ });
+ const invokee = document.querySelector("#invokee");
+ invokerbutton.setAttribute("invokeaction", action);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ invokee.addEventListener(
+ "invoke",
+ (e) => {
+ invokee.remove();
+ },
+ {
+ once: true,
+ },
+ );
+ await clickOn(invokerbutton);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (as ${
+ action || "explicit empty"
+ }) dialog that is removed is noop`,
+ );
+
+ promise_test(
+ async function (t) {
+ const invokerbutton = document.createElement("button");
+ invokerbutton.invokeTargetElement = invokee;
+ invokerbutton.setAttribute("invokeaction", action);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ await clickOn(invokerbutton);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (as ${
+ action || "explicit empty"
+ }) dialog from a detached invoker`,
+ );
+
+ promise_test(
+ async function (t) {
+ const invokerbutton = document.createElement("button");
+ const invokee = document.createElement("dialog");
+ invokerbutton.invokeTargetElement = invokee;
+ invokerbutton.setAttribute("invokeaction", action);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ await clickOn(invokerbutton);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (as ${
+ action || "explicit empty"
+ }) detached dialog from a detached invoker`,
+ );
+ });
+</script>
diff --git a/tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-invalid-behavior.tentative.html b/tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-invalid-behavior.tentative.html
new file mode 100644
index 00000000000..af84c22594a
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/invokers/invoketarget-on-dialog-invalid-behavior.tentative.html
@@ -0,0 +1,120 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" />
+<meta name="timeout" content="long">
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<dialog id="invokee">
+ <button id="containedinvoker" invoketarget="invokee"></button>
+</dialog>
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ function resetState() {
+ invokee.close();
+ try { invokee.hidePopover(); } catch {}
+ invokee.removeAttribute("popover");
+ invokerbutton.removeAttribute("invokeaction");
+ containedinvoker.removeAttribute("invokeaction");
+ }
+
+ // invalid
+ [
+ "foo",
+ "foo-bar",
+ "auto",
+ "showpopover",
+ "hidepopover",
+ "togglepopover",
+ "showpicker",
+ ].forEach((action) => {
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ invokerbutton.setAttribute("invokeaction", action);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ await clickOn(invokerbutton);
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ }, `invoking (as ${action}) on dialog does nothing`);
+
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ containedinvoker.setAttribute("invokeaction", action);
+ invokee.show();
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ await clickOn(containedinvoker);
+ assert_true(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ }, `invoking (as ${action}) on open dialog does nothing`);
+
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ containedinvoker.setAttribute("invokeaction", action);
+ invokee.showModal();
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ await clickOn(containedinvoker);
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ }, `invoking (as ${action}) on open modal dialog does nothing`);
+
+ promise_test(async function (t) {
+ t.add_cleanup(resetState);
+ containedinvoker.setAttribute("invokeaction", action);
+ invokee.showModal();
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ invokee.addEventListener(
+ "invoke",
+ (e) => {
+ containedinvoker.setAttribute("invokeaction", "");
+ },
+ { once: true },
+ );
+ await clickOn(containedinvoker);
+ assert_true(invokee.open, "invokee.open");
+ assert_true(invokee.matches(":modal"), "invokee :modal");
+ }, `invoking (as ${action}) on open modal while changing the attributer does nothing`);
+ });
+
+ // Open Popovers using Dialog actions
+ ["showmodal", "close", ""].forEach((action) => {
+ ["manual", "auto"].forEach((popoverState) => {
+ promise_test(
+ async function (t) {
+ t.add_cleanup(resetState);
+ invokee.setAttribute("popover", popoverState);
+ invokee.showPopover();
+ containedinvoker.setAttribute("invokeaction", action);
+ assert_true(
+ invokee.matches(":popover-open"),
+ "invokee :popover-open",
+ );
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(containedinvoker);
+ assert_true(
+ invokee.matches(":popover-open"),
+ "invokee :popover-open",
+ );
+ assert_false(invokee.open, "invokee.open");
+ assert_false(invokee.matches(":modal"), "invokee :modal");
+ },
+ `invoking (as ${
+ action || "explicit empty"
+ }) dialog as open popover=${popoverState} is noop`,
+ );
+ });
+ });
+</script>
diff --git a/tests/wpt/tests/html/semantics/invokers/invoketarget-on-input-number.tentative.html b/tests/wpt/tests/html/semantics/invokers/invoketarget-on-input-number.tentative.html
new file mode 100644
index 00000000000..b06053b9f1f
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/invokers/invoketarget-on-input-number.tentative.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<meta charset="utf-8" />
+<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
+<link rel="help" href="https://open-ui.org/components/invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<input type="number" id="invokee" value="0">
+<button id="invokerbutton" invoketarget="invokee"></button>
+
+<script>
+ function reset() {
+ invokee.value = 0;
+ invokerbutton.removeAttribute('invokeaction');
+ }
+
+ // stepUp
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_equals(invokee.valueAsNumber, 0);
+ invokerbutton.setAttribute("invokeaction", "stepup");
+ await clickOn(invokerbutton);
+ assert_equals(invokee.valueAsNumber, 1);
+ }, "invoking number input with stepup action increments value");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_equals(invokee.valueAsNumber, 0);
+ invokerbutton.setAttribute("invokeaction", "sTePuP");
+ await clickOn(invokerbutton);
+ assert_equals(invokee.valueAsNumber, 1);
+ }, "invoking number input with stepup action (case-insensitive) increments value");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_equals(invokee.valueAsNumber, 0);
+ invokerbutton.setAttribute("invokeaction", "stepup");
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ assert_equals(invokee.valueAsNumber, 0);
+ }, "invoking number input with stepup action and preventDefault does not increment value");
+
+ // stepDown
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_equals(invokee.valueAsNumber, 0);
+ invokerbutton.setAttribute("invokeaction", "stepdown");
+ await clickOn(invokerbutton);
+ assert_equals(invokee.valueAsNumber, -1);
+ }, "invoking number input with stepdown action decrements value");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_equals(invokee.valueAsNumber, 0);
+ invokerbutton.setAttribute("invokeaction", "sTePdOwN");
+ await clickOn(invokerbutton);
+ assert_equals(invokee.valueAsNumber, -1);
+ }, "invoking number input with stepdown action (case-insensitive) decrements value");
+
+ promise_test(async function (t) {
+ t.add_cleanup(reset);
+ assert_equals(invokee.valueAsNumber, 0);
+ invokerbutton.setAttribute("invokeaction", "stepdown");
+ invokee.addEventListener("invoke", (e) => e.preventDefault(), {
+ once: true,
+ });
+ await clickOn(invokerbutton);
+ assert_equals(invokee.valueAsNumber, 0);
+ }, "invoking number input with stepdown action and preventDefault does not decrement value");
+</script>
diff --git a/tests/wpt/tests/html/semantics/invokers/resources/invoker-utils.js b/tests/wpt/tests/html/semantics/invokers/resources/invoker-utils.js
index 317945502d4..4261f9c0d32 100644
--- a/tests/wpt/tests/html/semantics/invokers/resources/invoker-utils.js
+++ b/tests/wpt/tests/html/semantics/invokers/resources/invoker-utils.js
@@ -2,11 +2,26 @@ function waitForRender() {
return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
}
async function clickOn(element) {
- const actions = new test_driver.Actions();
await waitForRender();
- await actions.pointerMove(0, 0, {origin: element})
+ let rect = element.getBoundingClientRect();
+ let actions = new test_driver.Actions();
+ // FIXME: Switch to pointerMove(0, 0, {origin: element}) once
+ // https://github.com/web-platform-tests/wpt/issues/41257 is fixed.
+ await actions
+ .pointerMove(Math.round(rect.x + rect.width / 2), Math.round(rect.y + rect.height / 2), {})
.pointerDown({button: actions.ButtonType.LEFT})
.pointerUp({button: actions.ButtonType.LEFT})
.send();
await waitForRender();
}
+async function hoverOver(element) {
+ await waitForRender();
+ let rect = element.getBoundingClientRect();
+ let actions = new test_driver.Actions();
+ // FIXME: Switch to pointerMove(0, 0, {origin: element}) once
+ // https://github.com/web-platform-tests/wpt/issues/41257 is fixed.
+ await actions
+ .pointerMove(Math.round(rect.x + rect.width / 2), Math.round(rect.y + rect.height / 2), {})
+ .send();
+ await waitForRender();
+}
diff --git a/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html b/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html
index 248f4969f44..c62ff5b24d6 100644
--- a/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html
+++ b/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html
@@ -7,6 +7,7 @@
<li>font-weight is adjusted to be at least 200.</li>
<li>font-style should only have "normal" or "italic" values.</li>
<li>word-spacing should be at most 0.5 of the font size, and non-negative.</li>
+ <li>letter-spacing should be between -0.05 and 0.2 of the font size.</li>
</ul>
</div>
@@ -18,11 +19,14 @@
font-size: 100px;
height: auto;
width: auto;
+ letter-spacing: 20px;
}
#id2 {
word-spacing: 0em;
height: auto;
width: auto;
+ font-size: 10px;
+ letter-spacing: -0.5px;
}
</style>
diff --git a/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html b/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html
index 85bf3071f83..b8337ab87d8 100644
--- a/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html
+++ b/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html
@@ -9,6 +9,7 @@
<li>font-weight is adjusted to be at least 200.</li>
<li>font-style should only have "normal" or "italic" values.</li>
<li>word-spacing should be at most 0.5 of the font size, and non-negative.</li>
+ <li>letter-spacing should be between -0.05 and 0.2 of the font size.</li>
</ul>
</div>
@@ -20,11 +21,14 @@
font-size: 100px;
height: auto;
width: auto;
+ letter-spacing: 25px;
}
#id2 {
word-spacing: -1000px;
height: auto;
width: auto;
+ font-size: 10px;
+ letter-spacing: -1px;
}
</style>
diff --git a/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties.tentative.html b/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties.tentative.html
index 1cd3f735945..c0f0fe34542 100644
--- a/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties.tentative.html
+++ b/tests/wpt/tests/html/semantics/permission-element/bounded-css-properties.tentative.html
@@ -8,46 +8,57 @@
* font-weight is adjusted to be at least 200.
* font-style should only have "normal" or "italic" values.
* word-spacing should be at most 0.5 of the font size, and non-negative.
+ * letter-spacing should be between -0.05 and 0.2 of the font size.
-->
<style>
- #id1 {
+ #id-over-bounds {
font-weight: 100;
font-style: oblique 30deg;
word-spacing: 1em;
font-size: 100px;
+ letter-spacing: 21px;
}
- #id2 {
+ #id-under-bounds {
+ word-spacing: -1px;
+ font-size: 100px;
+ letter-spacing: -6px;
+ }
+ #id-within-bounds {
font-weight: 300;
font-style: italic;
word-spacing: 0.4em;
font-size: 100px;
- }
- #id3 {
- word-spacing: -1px;
+ letter-spacing: 15px;
}
</style>
-<permission id="id1" type="geolocation">
-<permission id="id2" type="camera">
-<permission id="id3" type="microphone">
+<permission id="id-over-bounds" type="geolocation">
+<permission id="id-under-bounds" type="camera">
+<permission id="id-within-bounds" type="microphone">
<script>
test(function(){
- var el_outside_bounds = document.getElementById("id1");
- assert_equals(getComputedStyle(el_outside_bounds).fontWeight, "200", "font-weight");
- assert_equals(getComputedStyle(el_outside_bounds).fontStyle, "normal", "font-style");
- assert_equals(getComputedStyle(el_outside_bounds).wordSpacing, "50px", "word-spacing");
+ var el = document.getElementById("id-over-bounds");
+ assert_equals(getComputedStyle(el).fontWeight, "200", "font-weight");
+ assert_equals(getComputedStyle(el).fontStyle, "normal", "font-style");
+ assert_equals(getComputedStyle(el).wordSpacing, "50px", "word-spacing");
+ assert_equals(getComputedStyle(el).letterSpacing, "20px", "letter-spacing");
- var el_negative_bounds = document.getElementById("id3");
- assert_equals(getComputedStyle(el_negative_bounds).wordSpacing, "0px", "word-spacing, negative");
+ el = document.getElementById("id-under-bounds");
+ assert_equals(getComputedStyle(el).wordSpacing, "0px", "word-spacing, negative");
+ assert_equals(getComputedStyle(el).letterSpacing, "-5px", "letter-spacing, negative");
}, "Properties with out-of-bounds values should be corrected");
test(function(){
- var el_inside_bounds = document.getElementById("id2");
- assert_equals(getComputedStyle(el_inside_bounds).fontWeight, "300", "font-weight");
- assert_equals(getComputedStyle(el_inside_bounds).fontStyle, "italic", "font-style");
- assert_equals(getComputedStyle(el_inside_bounds).wordSpacing, "40px", "word-spacing");
+ var el = document.getElementById("id-within-bounds");
+ assert_equals(getComputedStyle(el).fontWeight, "300", "font-weight");
+ assert_equals(getComputedStyle(el).fontStyle, "italic", "font-style");
+ assert_equals(getComputedStyle(el).wordSpacing, "40px", "word-spacing");
+ assert_equals(getComputedStyle(el).letterSpacing, "15px", "letter-spacing");
+
+ el.style.letterSpacing = "-4px";
+ assert_equals(getComputedStyle(el).letterSpacing, "-4px", "letter-spacing, negative");
}, "Properties with values in bounds should not be modified");
</script>
</body> \ No newline at end of file
diff --git a/tests/wpt/tests/html/semantics/popovers/popover-light-dismiss.html b/tests/wpt/tests/html/semantics/popovers/popover-light-dismiss.html
index 916d52ef5ed..45db242e918 100644
--- a/tests/wpt/tests/html/semantics/popovers/popover-light-dismiss.html
+++ b/tests/wpt/tests/html/semantics/popovers/popover-light-dismiss.html
@@ -11,6 +11,19 @@
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/popover-utils.js"></script>
+<style>
+ [popover] {
+ /* Position most popovers at the bottom-right, out of the way */
+ inset:auto;
+ bottom:0;
+ right:0;
+ }
+ [popover]::backdrop {
+ /* This should *not* affect anything: */
+ pointer-events: auto;
+ }
+</style>
+
<button id=b1t popovertarget='p1'>Popover 1</button>
<button id=b1s popovertarget='p1' popovertargetaction=show>Popover 1</button>
<span id=outside>Outside all popovers</span>
@@ -26,11 +39,6 @@
<style>
#p1 {top: 50px;}
#p2 {top: 120px;}
- [popover] {bottom:auto;}
- [popover]::backdrop {
- /* This should *not* affect anything: */
- pointer-events: auto;
- }
</style>
<script>
const popover1 = document.querySelector('#p1');
@@ -584,24 +592,50 @@ promise_test(async () => {
<div id=p29 popover>Popover 29</div>
<button id=b29 popovertarget=p29>Open popover 29</button>
-<iframe id=iframe29 width=100 height=100></iframe>
+<iframe id=iframe29 width=100 height=30></iframe>
<script>
promise_test(async () => {
let iframe_url = (new URL("/common/blank.html", location.href)).href;
iframe29.src = iframe_url;
iframe29.contentDocument.body.style.height = '100%';
- assert_false(p29.matches(':popover-open'));
+ assert_false(p29.matches(':popover-open'),'initially hidden');
p29.showPopover();
- assert_true(p29.matches(':popover-open'));
+ assert_true(p29.matches(':popover-open'),'showing');
let actions = new test_driver.Actions();
await actions.pointerMove(0,0,{origin: b29})
.pointerDown({button: actions.ButtonType.LEFT})
.send();
+ await waitForRender();
+ assert_true(p29.matches(':popover-open'),'showing after pointerdown');
actions = new test_driver.Actions();
await actions.pointerMove(0,0,{origin: iframe29.contentDocument.body})
.pointerUp({button: actions.ButtonType.LEFT})
.send();
- assert_true(p29.matches(':popover-open'));
+ await waitForRender();
+ assert_true(p29.matches(':popover-open'),'showing after pointerup');
},`Pointer down in one document and pointer up in another document shouldn't dismiss popover`);
</script>
+
+<div id=p30 popover>Popover 30</div>
+<button id=b30 popovertarget=p30>Open popover 30</button>
+<button id=b30b>Non-invoker</button>
+<script>
+promise_test(async () => {
+ assert_false(p30.matches(':popover-open'),'initially hidden');
+ p30.showPopover();
+ assert_true(p30.matches(':popover-open'),'showing');
+ let actions = new test_driver.Actions();
+ await actions.pointerMove(0,0,{origin: b30})
+ .pointerDown({button: actions.ButtonType.LEFT})
+ .send();
+ await waitForRender();
+ assert_true(p30.matches(':popover-open'),'showing after pointerdown');
+ actions = new test_driver.Actions();
+ await actions.pointerMove(0,0,{origin: b30b})
+ .pointerUp({button: actions.ButtonType.LEFT})
+ .send();
+ await waitForRender();
+ assert_true(p30.matches(':popover-open'),'showing after pointerup');
+},`Pointer down inside invoker and up outside that invoker shouldn't dismiss popover`);
+</script>
diff --git a/tests/wpt/tests/import-maps/WEB_FEATURES.yml b/tests/wpt/tests/import-maps/WEB_FEATURES.yml
new file mode 100644
index 00000000000..dc3d7fdc233
--- /dev/null
+++ b/tests/wpt/tests/import-maps/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: import-maps
+ files: "**"
diff --git a/tests/wpt/tests/intersection-observer/WEB_FEATURES.yml b/tests/wpt/tests/intersection-observer/WEB_FEATURES.yml
new file mode 100644
index 00000000000..1b6a42746fc
--- /dev/null
+++ b/tests/wpt/tests/intersection-observer/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: intersection-observer
+ files: "**"
diff --git a/tests/wpt/tests/intersection-observer/svg-container-element.html b/tests/wpt/tests/intersection-observer/svg-container-element.html
index e2b6ab5cb07..1c27acee514 100644
--- a/tests/wpt/tests/intersection-observer/svg-container-element.html
+++ b/tests/wpt/tests/intersection-observer/svg-container-element.html
@@ -14,6 +14,7 @@
<script>
const viewportWidth = document.documentElement.clientWidth;
const viewportHeight = document.documentElement.clientHeight;
+const kEpsilon = 1; // clientHeight and clientWidth are rounded if the viewport is fractional.
setup(() => {
window.entries = [];
window.target = document.getElementById("target");
@@ -40,7 +41,7 @@ function step0() {
0, 0, 0, 0,
0, viewportWidth, 0, viewportHeight,
false,
- ]);
+ ], kEpsilon);
}
function step1() {
checkLastEntry(entries, 1, [
@@ -48,6 +49,6 @@ function step1() {
8, 8 + targetRect.width, 8, 8 + targetRect.height,
0, viewportWidth, 0, viewportHeight,
true,
- ]);
+ ], kEpsilon);
}
</script>
diff --git a/tests/wpt/tests/intersection-observer/v2/WEB_FEATURES.yml b/tests/wpt/tests/intersection-observer/v2/WEB_FEATURES.yml
new file mode 100644
index 00000000000..e57d67f6415
--- /dev/null
+++ b/tests/wpt/tests/intersection-observer/v2/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: intersection-observer-v2
+ files: "**"
diff --git a/tests/wpt/tests/jpegxl/WEB_FEATURES.yml b/tests/wpt/tests/jpegxl/WEB_FEATURES.yml
new file mode 100644
index 00000000000..a81700e1fb0
--- /dev/null
+++ b/tests/wpt/tests/jpegxl/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: jpegxl
+ files: "**"
diff --git a/tests/wpt/tests/largest-contentful-paint/transparent-text.html b/tests/wpt/tests/largest-contentful-paint/transparent-text.html
new file mode 100644
index 00000000000..9eb978ab5cd
--- /dev/null
+++ b/tests/wpt/tests/largest-contentful-paint/transparent-text.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<!--
+ Transparent text should not be eligible for LCP.
+-->
+<style>
+ .large-transparent {
+ font-size: 2000px;
+ position: fixed;
+ top: 0;
+ left: 0;
+ padding: 0;
+ margin: 0;
+ pointer-events: none;
+ color: transparent;
+ z-index: -999;
+ }
+</style>
+
+<body>
+ <img src='/images/lcp-133x106.png' id='lcp' />
+ <p id="fake_lcp" class='large-transparent'>fake LCP</p>
+
+ <script>
+ const LcpEntryListPromise = (t) => {
+ return new Promise(resolve => {
+ new PerformanceObserver((entryList, observer) => {
+ if (entryList.getEntries().length > 0) {
+ resolve(entryList.getEntries());
+
+ observer.disconnect();
+ }
+ }).observe({ type: 'largest-contentful-paint', buffered: true });
+ });
+ }
+
+ promise_test(async t => {
+ assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented");
+
+ lcpEntries = await LcpEntryListPromise();
+
+ assert_equals(lcpEntries.length, 1, "There should only be 1 entry.");
+
+ assert_true(lcpEntries[0].url.includes('lcp-133x106.png'), "The LCP entry should be the image.");
+ }, "Transparent text should not be LCP.")
+ </script>
+</body> \ No newline at end of file
diff --git a/tests/wpt/tests/lint.ignore b/tests/wpt/tests/lint.ignore
index 4a2faecb576..61bca89ddc9 100644
--- a/tests/wpt/tests/lint.ignore
+++ b/tests/wpt/tests/lint.ignore
@@ -749,3 +749,7 @@ HTML INVALID SYNTAX: html/syntax/parsing/unclosed-svg-script.html
HTML INVALID SYNTAX: mathml/crashtests/mozilla/411603-1.html
HTML INVALID SYNTAX: quirks/percentage-height-calculation.html
HTML INVALID SYNTAX: trusted-types/TrustedTypePolicyFactory-getAttributeType-namespace.html
+
+# Pre compressed data using Shared Brotli and Shared Zstandard.
+TRAILING WHITESPACE, INDENT TABS, CR AT EOL: fetch/compression-dictionary/resources/compressed.br-d.data
+TRAILING WHITESPACE, INDENT TABS, CR AT EOL: fetch/compression-dictionary/resources/compressed.zstd-d.data
diff --git a/tests/wpt/tests/long-animation-frame/tentative/loaf-stream-source-location.html b/tests/wpt/tests/long-animation-frame/tentative/loaf-stream-source-location.html
index 5776ff52552..0fd30859d73 100644
--- a/tests/wpt/tests/long-animation-frame/tentative/loaf-stream-source-location.html
+++ b/tests/wpt/tests/long-animation-frame/tentative/loaf-stream-source-location.html
@@ -17,8 +17,7 @@ promise_test(async t => {
const scriptElement = document.createElement("script");
scriptElement.src = scriptLocation;
document.body.appendChild(scriptElement);
- }, script => {
- return script.invoker === "Promise.resolve" }, t);
+ }, script => script.invoker === "StreamPromise.resolve.then", t);
assert_true(script.sourceURL.includes("stream-promise-generates-loaf.js"));
}, "Source location should be extracted for stream promises");
diff --git a/tests/wpt/tests/long-animation-frame/tentative/loaf-stream.html b/tests/wpt/tests/long-animation-frame/tentative/loaf-stream.html
index 424f2cd0d1e..e35bc2f9aa7 100644
--- a/tests/wpt/tests/long-animation-frame/tentative/loaf-stream.html
+++ b/tests/wpt/tests/long-animation-frame/tentative/loaf-stream.html
@@ -34,6 +34,6 @@ test_promise_script(async t => {
});
response.body.pipeTo(writable);
await readable.getReader().read();
-}, "resolve", "Promise.resolve");
+}, "resolve", "StreamPromise.resolve");
</script>
</body>
diff --git a/tests/wpt/tests/mathml/WEB_FEATURES.yml b/tests/wpt/tests/mathml/WEB_FEATURES.yml
new file mode 100644
index 00000000000..4d03bc3eb4d
--- /dev/null
+++ b/tests/wpt/tests/mathml/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: mathml
+ files: "**"
diff --git a/tests/wpt/tests/mathml/presentation-markup/operators/mo-axis-height-1.html b/tests/wpt/tests/mathml/presentation-markup/operators/mo-axis-height-1.html
index 6de62841887..10ea4dcffc5 100644
--- a/tests/wpt/tests/mathml/presentation-markup/operators/mo-axis-height-1.html
+++ b/tests/wpt/tests/mathml/presentation-markup/operators/mo-axis-height-1.html
@@ -29,24 +29,100 @@
window.addEventListener("load", () => { loadAllFonts().then(runTests); });
function runTests() {
+ const AxisHeight = 5000 * emToPx;
+
test(function() {
- var v1 = 5000 * emToPx;
var moMiddle = (getBox("mo1").bottom + getBox("mo1").top) / 2;
assert_approx_equals(getBox("mo1").height,
14000 * emToPx, epsilon, "mo: size");
assert_approx_equals(getBox("baseline1").bottom - moMiddle,
- v1, epsilon, "mo: axis height");
- }, "AxisHeight (size variant)");
+ AxisHeight, epsilon, "mo: axis height");
+ }, "symmetric stretching with respect to the math axis (size variant)");
test(function() {
- var v1 = 5000 * emToPx;
var moMiddle = (getBox("mo2").bottom + getBox("mo2").top) / 2;
assert_approx_equals(getBox("mo2").height,
- 2 * (getBox("target2").height - v1),
+ 2 * (getBox("target2").height - AxisHeight),
epsilon, "mo: size");
assert_approx_equals(getBox("baseline2").bottom - moMiddle,
- v1, epsilon, "mo: axis height");
- }, "AxisHeight (glyph assembly)");
+ AxisHeight, epsilon, "mo: axis height");
+ }, "symmetric stretching with respect to the math axis (glyph assembly)");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ const Tascent = minsize / 2 + AxisHeight;
+ const Tdescent = minsize - Tascent;
+ assert_approx_equals(getBox("baseline3").bottom - getBox("mo3").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo3").bottom - getBox("baseline3").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = Tdescent = 0, minsize = 14em");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ var Tascent = getBox("baseline4").bottom - getBox("target4").top;
+ assert_greater_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target4").bottom - getBox("baseline4").bottom;
+ const T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * minsize / T + AxisHeight;
+ Tdescent = minsize - Tascent;
+ assert_approx_equals(getBox("baseline4").bottom - getBox("mo4").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo4").bottom - getBox("baseline4").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 6em > AxisHeight, Tdescent = 1em, symmetric = false, minsize = 14em");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ var Tascent = getBox("baseline5").bottom - getBox("target5").top;
+ assert_less_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target5").bottom - getBox("baseline5").bottom;
+ const T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * minsize / T + AxisHeight;
+ Tdescent = minsize - Tascent;
+ assert_approx_equals(getBox("baseline5").bottom - getBox("mo5").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo5").bottom - getBox("baseline5").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 4em < AxisHeight, Tdescent = 3em, symmetric = false, minsize = 14em");
+
+ test(function() {
+ const maxsize = 14000 * emToPx;
+ var Tascent = getBox("baseline6").bottom - getBox("target6").top;
+ assert_greater_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target6").bottom - getBox("baseline6").bottom;
+ const T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * maxsize / T + AxisHeight;
+ Tdescent = maxsize - Tascent;
+ assert_approx_equals(getBox("baseline6").bottom - getBox("mo6").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo6").bottom - getBox("baseline6").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 6em > AxisHeight, Tdescent = 22em, symmetric = false, maxsize = 14em");
+
+ test(function() {
+ const maxsize = 14000 * emToPx;
+ var Tascent = getBox("baseline7").bottom - getBox("target7").top;
+ assert_less_than(Tascent, AxisHeight);
+ var Tdescent = getBox("target7").bottom - getBox("baseline7").bottom;
+ var T = Tascent + Tdescent;
+ Tascent = Math.max(0, Tascent - AxisHeight) * maxsize / T + AxisHeight;
+ Tdescent = maxsize - Tascent;
+ assert_approx_equals(getBox("baseline7").bottom - getBox("mo7").top, Tascent, epsilon, "mo ascent");
+ assert_approx_equals(getBox("mo7").bottom - getBox("baseline7").bottom, Tdescent, epsilon, "mo descent");
+ }, "Tascent = 4em < AxisHeight, Tdescent = 24em, symmetric = false, maxsize = 14em");
+
+ test(function() {
+ const minsize = 14000 * emToPx;
+ const Uascent = getBox("baseline8").bottom - getBox("target8").top;
+ const Udescent = getBox("target8").bottom - getBox("baseline8").bottom;
+ assert_less_than(2 * Math.max(Uascent - AxisHeight, Udescent + AxisHeight), minsize, "Sascent + Sdescent < minsize");
+ assert_approx_equals(getBox("mo8").height, minsize, epsilon, "mo size");
+ const MathAxis = getBox("baseline8").bottom - AxisHeight;
+ assert_approx_equals(MathAxis - getBox("mo8").top, getBox("mo8").bottom - MathAxis, epsilon, "mo is symmetric");
+ }, "symmetric stretching with respect to the math axis (minsize = 14em)");
+
+ test(function() {
+ const maxsize = 14000 * emToPx;
+ const Uascent = getBox("baseline9").bottom - getBox("target9").top;
+ const Udescent = getBox("target9").bottom - getBox("baseline9").bottom;
+ assert_greater_than(2 * Math.max(Uascent - AxisHeight, Udescent + AxisHeight), maxsize, "Sascent + Sdescent > maxsize");
+ assert_approx_equals(getBox("mo9").height, maxsize, epsilon, "mo size");
+ const MathAxis = getBox("baseline9").bottom - AxisHeight;
+ assert_approx_equals(MathAxis - getBox("mo9").top, getBox("mo9").bottom - MathAxis, epsilon, "mo is symmetric");
+ }, "symmetric stretching with respect to the math axis (maxsize = 14em)");
done();
}
@@ -56,20 +132,87 @@
<div id="log"></div>
<p>
<math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline1" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mrow>
- <mspace id="baseline1" style="background: blue" width="50px" height="1px"/>
- <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mo id="mo1" symmetric="true" style="color: green">&#x21A8;</mo>
- <mspace style="background: gray" width="10px" height="50px"/>
+ <mpadded style="background: gray" width="10px" height="50px"><mn>1</mn></mpadded>
</mrow>
</math>
<math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline2" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mrow>
- <mspace id="baseline2" style="background: blue" width="50px" height="1px"/>
- <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
<mo id="mo2" symmetric="true" style="color: green">&#x21A8;</mo>
- <mspace id="target2" style="background: gray" width="10px" height="200px"/>
+ <mpadded id="target2" style="background: gray" width="10px" height="200px"><mn>2</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline3" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo3" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target3" style="background: gray" width="10px" height="0px" depth="0px"><mn>3</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline4" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo4" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target4" style="background: gray" width="10px" height="6em" depth="1em"><mn>4</mn></mpadded>
+ </mrow>
+ </math>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline5" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo5" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target5" style="background: gray" width="10px" height="4em" depth="3em"><mn>5</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline6" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo6" maxsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target6" style="background: gray" width="10px" height="6em" depth="22em"><mn>6</mn></mpadded>
+ </mrow>
+ </math>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline7" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo7" maxsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target7" style="background: gray" width="10px" height="4em" depth="24em"><mn>7</mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+
+
+ <p>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline8" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo8" symmetric="true" minsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target8" style="background: gray" width="10px" height="6em" depth="1em"><mn>8</mn></mpadded>
+ </mrow>
+ </math>
+ <math style="font-family: axisheight5000-verticalarrow14000;">
+ <mspace id="baseline9" style="background: blue" width="50px" height="1px"/>
+ <mpadded voffset="50px"><mspace style="background: cyan" width="50px" height="1px"/></mpadded>
+ <mrow>
+ <mo id="mo9" symmetric="true" maxsize="14em" style="color: green">&#x21A8;</mo>
+ <mpadded id="target9" style="background: gray" width="10px" height="6em" depth="24em"><mn>9</mn></mpadded>
</mrow>
</math>
+ </p>
</body>
</html>
diff --git a/tests/wpt/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html b/tests/wpt/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html
index 3e7e5c9bcc8..c07f64327a3 100644
--- a/tests/wpt/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html
+++ b/tests/wpt/tests/mathml/presentation-markup/operators/mo-minsize-maxsize-001.html
@@ -20,6 +20,10 @@
mo {
font-family: operators;
}
+ @font-face {
+ font-family: stretchy;
+ src: url("/fonts/math/stretchy.woff");
+ }
</style>
<script>
setup({ explicit_done: true });
@@ -48,17 +52,27 @@
test(function() {
assert_approx_equals(document.getElementById("percent_minsize").getBoundingClientRect().height, 12 * emToPx, epsilon, "percent minsize");
assert_approx_equals(document.getElementById("percent_maxsize").getBoundingClientRect().height, 3 * emToPx, epsilon, "percent maxsize");
- }, `minsize/maxsize percentages are relative to the target size`);
+ }, `minsize/maxsize percentages are relative to the unstretched size`);
test(function() {
- // These tests are not really strong:
- // - The smallest glyph for this stretchy operator is a 1em square so
- // it can't go under a minsize of 1em anyway.
- // - The maxsize is theorically infinite, this only tests that a large
- // value of 300em is clamped.
- assert_approx_equals(document.getElementById("default_minsize").getBoundingClientRect().height, 1 * emToPx, epsilon, "default minsize is 1em");
+ // - The unstretched size is a lower bound for the stretched size, so
+ // specifying a lower minsize has no effect. This test only verifies
+ // that the default minsize is at most 100% the unstretched size.
+ const unstretched_size = 1 * emToPx;
+ assert_approx_equals(document.getElementById("default_minsize").getBoundingClientRect().height, unstretched_size, epsilon, "default minsize is 100%");
+
+ // Previous version of MathML Core were defining minsize as 1em rather
+ // than 100% the unstretched size. So try the same test with a .5em
+ // unstretched size.
+ const unstretched_size_2 = .5 * emToPx;
+ assert_approx_equals(document.getElementById("default_minsize_2").getBoundingClientRect().height, unstretched_size_2, epsilon, "default minsize is not 1em");
+
+ // - The target size is an upper bound for the stretched size, so
+ // specifying a larger maxsize has no effect. This test only
+ // verifies that the default maxsize is at least 300 times the
+ // unstretched size.
assert_approx_equals(document.getElementById("default_maxsize").getBoundingClientRect().height, 300 * emToPx, epsilon, "default maxsize is infinity");
- }, `default minsize/maxsize percentages`);
+ }, `default minsize/maxsize values`);
done();
}
@@ -71,7 +85,7 @@
<mrow>
<mspace width="1em" height="5em" style="background: blue"/>
<mo id="negative_minsize" minsize="-10em" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -80,7 +94,7 @@
<mrow>
<mspace width="1em" height="5em" style="background: blue"/>
<mo id="maxsize_less_than_minsize" minsize="7em" maxsize="2em" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -89,7 +103,7 @@
<mrow>
<mspace width="1em" height="5em" style="background: blue"/>
<mo id="minsize_less_than_negative_maxsize" minsize="-2em" maxsize="-1em" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -98,7 +112,7 @@
<mrow>
<mspace id="zero_target_size_with_minsize_math_axis" width="1em" height="0em" style="background: blue"/>
<mo id="zero_target_size_with_minsize" minsize="2em" stretchy="true" symmetric="true">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -106,8 +120,8 @@
<math>
<mrow>
<mspace width="1em" height="6em" style="background: blue"/>
- <mo id="percent_minsize" minsize="200%" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mo id="percent_minsize" minsize="1200%" stretchy="true" symmetric="false">⥯</mo>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -115,8 +129,8 @@
<math>
<mrow>
<mspace width="1em" height="6em" style="background: blue"/>
- <mo id="percent_maxsize" maxsize="50%" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mo id="percent_maxsize" maxsize="300%" stretchy="true" symmetric="false">⥯</mo>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -125,7 +139,16 @@
<mrow>
<mspace width="1em" height=".5em" style="background: blue"/>
<mo id="default_minsize" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
+ </mrow>
+ </math>
+ </p>
+ <p>
+ <math>
+ <mrow>
+ <mspace width="1em" height=".25em" style="background: blue"/>
+ <mo style="font-family: stretchy" id="default_minsize_2" stretchy="true" symmetric="false">↨</mo>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
@@ -134,10 +157,9 @@
<mrow>
<mspace width="1em" height="300em" style="background: blue"/>
<mo id="default_maxsize" stretchy="true" symmetric="false">⥯</mo>
- <mn><!-- not space like --></mn>
+ <mpadded height="0" depth="0"><mn><!-- not space like --></mn></mpadded>
</mrow>
</math>
</p>
-
</body>
</html>
diff --git a/tests/wpt/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html b/tests/wpt/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html
index 5d447aa1d28..1cb3c90bcb9 100644
--- a/tests/wpt/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html
+++ b/tests/wpt/tests/mathml/presentation-markup/operators/mo-stretch-properties-dynamic-001.html
@@ -42,7 +42,8 @@
element = document.getElementById("minsize_remove");
element.removeAttribute("minsize");
- assert_approx_equals(element.getBoundingClientRect().height, 1 * emToPx, epsilon, "remove");
+ const unstretched_size = 1 * emToPx;
+ assert_approx_equals(element.getBoundingClientRect().height, unstretched_size, epsilon, "remove");
}, `minsize`);
test(function() {
diff --git a/tests/wpt/tests/mathml/tools/stretchy.py b/tests/wpt/tests/mathml/tools/stretchy.py
index 34530f5792b..33d4decd4f5 100755
--- a/tests/wpt/tests/mathml/tools/stretchy.py
+++ b/tests/wpt/tests/mathml/tools/stretchy.py
@@ -4,7 +4,9 @@ from utils import mathfont
import fontforge
# Create a WOFF font with glyphs for all the operator strings.
-font = mathfont.create("stretchy", "Copyright (c) 2021 Igalia S.L.")
+font = mathfont.create("stretchy", "Copyright (c) 2021-2024 Igalia S.L.")
+
+font.math.AxisHeight = 0
# Set parameters for stretchy tests.
font.math.MinConnectorOverlap = mathfont.em // 2
@@ -27,6 +29,7 @@ font.math.OverbarExtraAscender = 0
# These two characters will be stretchable in both directions.
horizontalArrow = 0x295A # LEFTWARDS HARPOON WITH BARB UP FROM BAR
verticalArrow = 0x295C # UPWARDS HARPOON WITH BARB RIGHT FROM BAR
+upDownArrowWithBase = 0x21A8 # UP DOWN ARROW WITH BASE
mathfont.createSizeVariants(font, aUsePUA=True, aCenterOnBaseline=False)
@@ -40,4 +43,10 @@ mathfont.createSquareGlyph(font, verticalArrow)
mathfont.createStretchy(font, verticalArrow, True)
mathfont.createStretchy(font, verticalArrow, False)
+# U+21A8 stretches vertically using two size variants: a base glyph (of height
+# half an em) and taller glyphs (of heights 1, 2, 3 and 4 em).
+g = font.createChar(upDownArrowWithBase)
+mathfont.drawRectangleGlyph(g, mathfont.em, mathfont.em/2, 0)
+font[upDownArrowWithBase].verticalVariants = "uni21A8 v0 v1 v2 v3"
+
mathfont.save(font)
diff --git a/tests/wpt/tests/media/A4.ogv b/tests/wpt/tests/media/A4.ogv
deleted file mode 100644
index de99616eceb..00000000000
--- a/tests/wpt/tests/media/A4.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/media/counting.ogv b/tests/wpt/tests/media/counting.ogv
deleted file mode 100644
index ce03c19e50e..00000000000
--- a/tests/wpt/tests/media/counting.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/media/green-at-15.ogv b/tests/wpt/tests/media/green-at-15.ogv
deleted file mode 100644
index 50d59dfb38b..00000000000
--- a/tests/wpt/tests/media/green-at-15.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/media/movie_300.ogv b/tests/wpt/tests/media/movie_300.ogv
deleted file mode 100644
index 0f83996e5dc..00000000000
--- a/tests/wpt/tests/media/movie_300.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/media/movie_5.ogv b/tests/wpt/tests/media/movie_5.ogv
deleted file mode 100644
index e8990d1120a..00000000000
--- a/tests/wpt/tests/media/movie_5.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/media/test.ogv b/tests/wpt/tests/media/test.ogv
deleted file mode 100644
index 0c55f6c7226..00000000000
--- a/tests/wpt/tests/media/test.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/media/video.ogv b/tests/wpt/tests/media/video.ogv
deleted file mode 100644
index 5cb5f878481..00000000000
--- a/tests/wpt/tests/media/video.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html b/tests/wpt/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html
index eb7b443df39..44dfa801537 100644
--- a/tests/wpt/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html
+++ b/tests/wpt/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html
@@ -67,11 +67,11 @@
var otherHost = get_host_info().HTTP_NOTSAMESITE_ORIGIN.slice(0, -4); // cut of http port
var url = new URL(
otherHost +
- "{{ports[https][0]}}/mixed-content/tentative/resources/test.ogv?pipe=header(Access-Control-Allow-Origin,*)"
+ "{{ports[https][0]}}/mixed-content/tentative/resources/test.webm?pipe=header(Access-Control-Allow-Origin,*)"
);
var i = document.createElement("video");
i.oncanplaythrough = test.step_func_done((_) => {
- assert_equals(Math.floor(i.duration), 300, "Length. Other host");
+ assert_equals(Math.floor(i.duration), 1, "Length. Other host");
});
i.crossOrigin = "anonymous";
i.onerror = test.unreached_func(
diff --git a/tests/wpt/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html b/tests/wpt/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html
index ea2d07309c0..d9ad3dda392 100644
--- a/tests/wpt/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html
+++ b/tests/wpt/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html
@@ -13,10 +13,10 @@
function assert_video_loads(test) {
// Since autoupgrades don't upgrade custom ports, we use the https port with an HTTP scheme. A successful autoupgrade will result in the right URL loading (and no autoupgrade will result in failure).
- var url = new URL("http://{{host}}:{{ports[https][0]}}/mixed-content/tentative/resources/test.ogv")
+ var url = new URL("http://{{host}}:{{ports[https][0]}}/mixed-content/tentative/resources/test.webm")
var i = document.createElement('video');
i.oncanplaythrough = test.step_func_done(_ => {
- assert_equals(Math.floor(i.duration), 300, "Length.");
+ assert_equals(Math.floor(i.duration), 1, "Length.");
});
i.onerror = test.unreached_func("Video should load successfully from " + url);
i.src = url;
@@ -28,10 +28,10 @@
function assert_other_host_video_loads(test) {
// Since autoupgrades don't upgrade custom ports, we use the https port with an HTTP scheme. A successful autoupgrade will result in the right URL loading (and no autoupgrade will result in failure).
var otherHost = get_host_info().HTTP_NOTSAMESITE_ORIGIN.slice(0,-4); // cut of http port
- var url = new URL( otherHost + "{{ports[https][0]}}/mixed-content/tentative/resources/test.ogv")
+ var url = new URL( otherHost + "{{ports[https][0]}}/mixed-content/tentative/resources/test.webm")
var i = document.createElement('video');
i.oncanplaythrough = test.step_func_done(_ => {
- assert_equals(Math.floor(i.duration), 300, "Length. Other host");
+ assert_equals(Math.floor(i.duration), 1, "Length. Other host");
});
i.onerror = test.unreached_func("Video of other host should load successfully from " + url);
i.src = url;
diff --git a/tests/wpt/tests/mixed-content/tentative/resources/test.ogv b/tests/wpt/tests/mixed-content/tentative/resources/test.ogv
deleted file mode 100644
index 0f83996e5dc..00000000000
--- a/tests/wpt/tests/mixed-content/tentative/resources/test.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/mixed-content/tentative/resources/test.webm b/tests/wpt/tests/mixed-content/tentative/resources/test.webm
new file mode 100644
index 00000000000..e3e4cb0baca
--- /dev/null
+++ b/tests/wpt/tests/mixed-content/tentative/resources/test.webm
Binary files differ
diff --git a/tests/wpt/tests/payment-method-basic-card/billing-address-is-null-manual.https.html b/tests/wpt/tests/payment-method-basic-card/billing-address-is-null-manual.https.html
index 3250e0a2c55..5eac71963eb 100644
--- a/tests/wpt/tests/payment-method-basic-card/billing-address-is-null-manual.https.html
+++ b/tests/wpt/tests/payment-method-basic-card/billing-address-is-null-manual.https.html
@@ -101,8 +101,8 @@
function checkRedaction(billingAddress) {
assert_true(
- billingAddress instanceof PaymentAddress,
- "Expected instance of PaymentAddress"
+ billingAddress instanceof ContactAddress,
+ "Expected instance of ContactAddress"
);
for (const item of ["organization", "phone", "recipient"]) {
assert_equals(
@@ -134,7 +134,7 @@
<button onclick="requestBillingAddress()">
When billing address is
requested,`PaymentMethodChangeEvent.methodData.billingAddress` is a
- `PaymentAddress`.
+ `ContactAddress`.
</button>
</li>
<li><button onclick="done()">Done!</button></li>
diff --git a/tests/wpt/tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html b/tests/wpt/tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html
new file mode 100644
index 00000000000..fc1ce3523e5
--- /dev/null
+++ b/tests/wpt/tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html
@@ -0,0 +1,109 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://www.w3.org/TR/payment-request/#ContactAddress-interface">
+<title>
+ PaymentResponse.prototype.shippingAddress
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../payment-response/helpers.js"></script>
+<script>
+const options = { requestShipping: true };
+function runManualTest(button, expected = {}) {
+ button.disabled = true;
+ promise_test(async () => {
+ const { response } = await getPaymentRequestResponse(options);
+ await response.complete();
+ assert_idl_attribute(response, "shippingAddress");
+ const { shippingAddress: addr } = response;
+ assert_true(
+ addr instanceof ContactAddress,
+ "Expect instance of ContactAddress"
+ );
+ // An [ISO3166] alpha-2 code. The canonical form is upper case.
+ const { country } = addr;
+ assert_equals(country.length, 2, "Expected length is 2");
+ assert_true(/^[A-Z]{2}$/.test(country), "Canonical form is upper case");
+ assert_true(
+ addr.addressLine instanceof Array,
+ "Expected addressLine to be an array"
+ );
+ assert_throws_js(
+ TypeError,
+ () => {
+ addr.addressLine.push("this must throw");
+ },
+ "Array must be frozen"
+ );
+ for (let [attr, expectedValue] of Object.entries(expected)) {
+ assert_idl_attribute(addr, attr);
+ const msg = `Expected ContactAddress.${attr} to equal ${expectedValue}.`;
+ //.toString() flattens array addressLine,
+ //.toLowerCase() because case can't be enforced for some attributes
+ const actualValue = addr[attr].toString().toLowerCase();
+ expectedValue = expectedValue.toString().toLowerCase();
+ assert_equals(actualValue, expectedValue, msg);
+ }
+ // Check toJSON result
+ for (let [prop, jsonValue] of Object.entries(addr.toJSON())) {
+ const actualValue = jsonValue.toString().toLowerCase();
+ const expectedValue = expected[prop].toString().toLowerCase();
+ const msg = `Expected JSON ${prop} to be ${expectedValue}`;
+ assert_equals(actualValue, expectedValue, msg);
+ }
+ }, button.textContent.trim());
+ done();
+}
+</script>
+<h2>ContactAddress interface</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When prompted, please enter addresses as follows...
+</p>
+<ol>
+ <li>
+ <button onclick="
+ const expectedAddress = {
+ country: 'AU',
+ regionCode: 'QLD',
+ addressLine: '55 test st',
+ city: 'Chapel Hill',
+ dependentLocality: '',
+ postalCode: '6095',
+ region: 'QLD',
+ sortingCode: '',
+ organization: 'w3c',
+ recipient: 'web platform test',
+ phone: '+61733780000',
+ };
+ runManualTest(this, expectedAddress);">
+ If the requestShipping member is true, then shippingAddress's ContactAddress must match the expected values.
+ </button>
+ Please use:
+ <dl>
+ <dt>Recipient:</dt>
+ <dd>web platform test</dd>
+ <dt>Address line:</dt>
+ <dd>55 test st</dd>
+ <dt>Country</dt>
+ <dd>Australia</dd>
+ <dt>City</dt>
+ <dd>Chapel Hill</dd>
+ <dd>State/Region</dd>
+ <dd>Queensland</dd>
+ <dt>postal code </dt>
+ <dd>6095</dd>
+ <dt>organization</dt>
+ <dd>w3c</dd>
+ <dt>Phone number</dt>
+ <dd>+61 7 3378 0000</dd>
+ </dl>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-call-immediate-manual.https.html b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-call-immediate-manual.https.html
new file mode 100644
index 00000000000..1365ecefebe
--- /dev/null
+++ b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-call-immediate-manual.https.html
@@ -0,0 +1,206 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method">
+<link rel="help" href="https://github.com/w3c/payment-request/pull/591">
+<title>
+ PaymentRequestUpdateEvent.updateWith() needs to be called immediately
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const validMethod = Object.freeze({ supportedMethods: "basic-card" });
+const validMethods = Object.freeze([validMethod, applePay]);
+const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+});
+const validShippingOptionA = Object.freeze({
+ id: "a-shipping-option",
+ label: "A shipping option",
+ amount: validAmount,
+ selected: true,
+});
+const validShippingOptionB = Object.freeze({
+ id: "b-shipping-option",
+ label: "B shipping option",
+ amount: validAmount,
+});
+const validDetails = Object.freeze({
+ total: validTotal,
+ shippingOptions: [validShippingOptionA, validShippingOptionB],
+});
+const validOptions = Object.freeze({
+ requestShipping: true,
+});
+
+function testImmediateUpdate({ textContent: testName }) {
+ promise_test(async t => {
+ const request = new PaymentRequest(
+ validMethods,
+ validDetails,
+ validOptions
+ );
+ const eventPromise = new Promise((resolve, reject) => {
+ request.addEventListener(
+ "shippingaddresschange",
+ ev => {
+ // Forces updateWith() to be run in the next event loop tick so that
+ // [[waitForUpdate]] is already true when it runs.
+ t.step_timeout(() => {
+ try {
+ ev.updateWith(validDetails);
+ resolve(); // This is bad.
+ } catch (err) {
+ reject(err); // this is good.
+ }
+ });
+ },
+ { once: true }
+ );
+ });
+ const acceptPromise = request.show();
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ eventPromise,
+ "The event loop already spun, so [[waitForUpdate]] is now true"
+ );
+ const response = await acceptPromise;
+ await response.complete();
+ }, testName.trim());
+}
+
+function testSubsequentUpdateWithCalls({ textContent: testName }) {
+ promise_test(async t => {
+ const request = new PaymentRequest(
+ validMethods,
+ validDetails,
+ validOptions
+ );
+ const eventPromise = new Promise((resolve, reject) => {
+ request.addEventListener("shippingaddresschange", async ev => {
+ const p = Promise.resolve(validDetails);
+ ev.updateWith(p);
+ await p;
+ try {
+ ev.updateWith(validDetails);
+ resolve(); // this is bad, we should never get to here.
+ } catch (err) {
+ reject(err); // this is good!
+ }
+ });
+ });
+ const responsePromise = request.show();
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ eventPromise,
+ "Expected eventPromise to have rejected, because updateWith() was a called twice"
+ );
+ const response = await responsePromise;
+ await response.complete();
+ }, testName.trim());
+}
+
+function testRecycleEvents({ textContent: testName }) {
+ promise_test(async t => {
+ const request = new PaymentRequest(
+ validMethods,
+ validDetails,
+ validOptions
+ );
+
+ // Register both listeners.
+ const addressChangedPromise = new Promise(resolve => {
+ request.addEventListener("shippingaddresschange", resolve, {
+ once: true,
+ });
+ });
+
+ const optionChangedPromise = new Promise(resolve => {
+ request.addEventListener("shippingoptionchange", resolve, {
+ once: true,
+ });
+ });
+
+ const responsePromise = request.show();
+
+ // Let's wait for the address to change.
+ const addressChangeEvent = await addressChangedPromise;
+
+ // Sets [[waitingForUpdate]] to true.
+ addressChangeEvent.updateWith(validDetails);
+
+ // Let's wait for the shippingOption.
+ const optionChangeEvent = await optionChangedPromise;
+
+ // Here, we try to be sneaky, and reuse the addressChangeEvent to perform the update.
+ // However, addressChangeEvent [[waitingForUpdate]] is true, so it throws.
+ assert_throws_dom(
+ "InvalidStateError",
+ () => {
+ addressChangeEvent.updateWith(validDetails);
+ },
+ "addressChangeEvent [[waitingForUpdate]] is true, so it must throw"
+ );
+
+ // But optionChangeEvent is still usable tho, so...
+ optionChangeEvent.updateWith(validDetails);
+
+ assert_throws_dom(
+ "InvalidStateError",
+ () => {
+ optionChangeEvent.updateWith(validDetails);
+ },
+ "optionChangeEvent [[waitingForUpdate]] is true, so it must throw"
+ );
+
+ const response = await responsePromise;
+ await response.complete();
+ }, testName.trim());
+}
+</script>
+<h2>updateWith() method</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is shown, select a different shipping address once. Then pay.
+</p>
+<ol>
+ <li id="test-0">
+ <button onclick="testImmediateUpdate(this);">
+ updateWith() must be called immediately, otherwise must throw an InvalidStateError.
+ </button>
+ </li>
+ <li id="test-1">
+ <button onclick="testSubsequentUpdateWithCalls(this);">
+ Once the event has performed an update, subsequent calls to updateWith() must throw InvalidStateError.
+ </button>
+ </li>
+ <li id="test-2">
+ <button onclick="testRecycleEvents(this);">
+ Recycling events must not be possible.
+ </button> When the payment sheet is shown, select a different shipping address once, then change shipping option once. Then pay.
+ </li>
+ <li>
+ <button onclick="done();">Done!</button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-duplicate-shipping-options-manual.https.html b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-duplicate-shipping-options-manual.https.html
new file mode 100644
index 00000000000..a4a7afd7f63
--- /dev/null
+++ b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-duplicate-shipping-options-manual.https.html
@@ -0,0 +1,106 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#updatewith()-method">
+<title>
+ updateWith() method - duplicate shippingOption ids
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const validMethod = Object.freeze({ supportedMethods: "basic-card" });
+const validMethods = [validMethod, applePay];
+const validAmount = Object.freeze({
+ currency: "USD",
+ value: "5.00",
+});
+const validShippingOption = Object.freeze({
+ id: "option1",
+ label: "Option 1",
+ amount: validAmount,
+ selected: true,
+});
+const validShippingOptions = Object.freeze([validShippingOption]);
+const validDetails = Object.freeze({
+ total: {
+ label: "Total due",
+ amount: validAmount,
+ },
+ shippingOptions: validShippingOptions,
+});
+const validOptions = Object.freeze({
+ requestShipping: true,
+});
+
+test(() => {
+ try {
+ const request = new PaymentRequest(validMethods, validDetails);
+ } catch (err) {
+ done();
+ throw err;
+ }
+}, "Must construct a PaymentRequest (smoke test)");
+
+function testFireEvents(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const request = new PaymentRequest(
+ validMethods,
+ validDetails,
+ validOptions
+ );
+ request.addEventListener("shippingaddresschange", event => {
+ // Same option, so duplicate ids
+ const otherShippingOption = Object.assign({}, validShippingOption, {
+ id: "other",
+ });
+ const shippingOptions = [
+ validShippingOption,
+ otherShippingOption,
+ validShippingOption,
+ ];
+ const newDetails = Object.assign({}, validDetails, { shippingOptions });
+ event.updateWith(newDetails);
+ });
+ const acceptPromise = request.show();
+ await promise_rejects_js(
+ t,
+ TypeError,
+ acceptPromise,
+ "Duplicate shippingOption ids must abort with TypeError"
+ );
+ }, button.textContent.trim());
+ done();
+}
+</script>
+<h2>updateWith() method - duplicate shippingOptions ids</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is shown, select a different shipping address.
+ If you have to manually abort the test from the payment sheet, then the
+ test has failed.
+</p>
+<ol>
+ <li>
+ <button onclick="testFireEvents(this)">
+ If there are duplicate shippingOption ids, then abort payment request.
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html
new file mode 100644
index 00000000000..c1ed1b5f685
--- /dev/null
+++ b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html
@@ -0,0 +1,196 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#updatewith-method">
+<title>
+ Incremental updates via updateWith()
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({
+ explicit_done: true,
+ explicit_timeout: true,
+});
+
+const methods = [{
+ supportedMethods: "basic-card",
+}, {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+}];
+
+const options = {
+ requestShipping: true,
+};
+
+const initialDetails = {
+ total: {
+ label: "Initial total",
+ amount: {
+ currency: "USD",
+ value: "1.0",
+ },
+ },
+ shippingOptions: [
+ {
+ id: "neutral",
+ label: "NEUTRAL SHIPPING OPTION",
+ selected: true,
+ amount: {
+ currency: "USD",
+ value: "0.00",
+ },
+ },
+ ],
+};
+
+function testFireEvent(button, updateDetails) {
+ button.disabled = true;
+ const request = new PaymentRequest(methods, initialDetails, options);
+ const handlerPromise = new Promise(resolve => {
+ request.onshippingaddresschange = event => {
+ event.updateWith(updateDetails);
+ resolve(event);
+ };
+ });
+ promise_test(async t => {
+ const response = await request.show();
+ const event = await handlerPromise;
+ await response.complete("success");
+ }, button.textContent.trim());
+}
+
+</script>
+<h2>
+ Incremental updates
+</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ Unless stated otherwise, each test will update some part of the displayed payment sheet in
+ a manner indicated below. When prompted, please change or enter a new
+ shipping address, look for the tested change, and complete the payment.
+</p>
+<p>
+ If the payment request locks up or otherwise aborts, the test has failed.
+</p>
+<ol>
+ <li>
+ <button onclick="testFireEvent(this, {});">
+ Passing an empty dictionary does not cause the sheet to change.
+ No values in the sheet must change.
+ </button>
+ </li>
+</ol>
+
+<section>
+ <h3>Incremental updates via PaymentDetailsUpdate.total</h3>
+ <ol>
+ <li>
+ <button onclick="
+ const total = {
+ label: 'PASS',
+ amount: {
+ currency: 'XXX',
+ value: '20',
+ },
+ };
+ const updatedDetails = { total };
+ testFireEvent(this, updatedDetails);">
+ After changing shipping address, the total becomes XXX20, with the label "PASS".
+ </button>
+ </li>
+ </ol>
+</section>
+
+<section>
+ <h3>Incremental updates via PaymentDetailsBase.displayItems</h3>
+ <ol>
+ <li>
+ <button onclick="
+ const item = {
+ label: 'PASS',
+ amount: { currency: 'ABC', value: '55.00' },
+ };
+ const updatedDetails = {
+ displayItems: [ item ]
+ };
+ testFireEvent(this, updatedDetails);">
+ After changing shipping address, a new display item is shown
+ with a with label PASS, and value of ABC55.00.
+ </button>
+ </li>
+ </ol>
+</section>
+
+<section>
+ <h3>Incremental updates via PaymentDetailsBase.shippingOptions</h3>
+ <ol>
+ <li>
+ <button onclick="
+ const shippingOptions = [
+ {
+ id: 'pass',
+ label: 'PASS',
+ amount: { currency: 'USD', value: '1.00' },
+ selected: true,
+ },
+ {
+ id: 'fail',
+ label: 'FAIL IF THIS IS SELECTED',
+ amount: { currency: 'USD', value: '25.00' }
+ },
+ ];
+ const updatedDetails = {
+ shippingOptions
+ };
+ testFireEvent(this, updatedDetails);">
+ After changing shipping address, two new shipping options appear.
+ The shipping option labelled "PASS" with a value of USD1.0 is selected.
+ </button>
+ </li>
+ </ol>
+</section>
+
+<section>
+ <h3>Incremental updates via PaymentDetailsBase.modifiers</h3>
+ <ol>
+ <li>
+ <button onclick="
+ const additionalItem = {
+ label: 'PASS-DISPLAY-ITEM',
+ amount: { currency: 'USD', value: '3.00' },
+ };
+ const modifiers = [{
+ additionalDisplayItems: [ additionalItem ],
+ supportedMethods: 'basic-card',
+ total: {
+ label: 'PASS-TOTAL',
+ amount: { currency: 'USD', value: '123.00' },
+ },
+ }];
+ const updatedDetails = { modifiers };
+ testFireEvent(this, updatedDetails);">
+ After changing shipping address, a new display item is shown
+ with a with label PASS-DISPLAY-ITEM, and value of ABC55.00 and the total is
+ labelled PASS-TOTAL with a value of USD123.0
+ </button>
+ </li>
+ <li>
+ <button onclick="done()">DONE - see results</button>
+ </li>
+ </ol>
+</section>
+
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-method-abort-update-manual.https.html b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-method-abort-update-manual.https.html
new file mode 100644
index 00000000000..e24452c2a99
--- /dev/null
+++ b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-method-abort-update-manual.https.html
@@ -0,0 +1,286 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dfn-abort-the-update">
+<title>
+ updateWith() method - "abort the update"
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+
+// PaymentMethod
+const validMethod = Object.freeze({
+ supportedMethods: "valid-but-wont-ever-match",
+});
+
+const validMethodBasicCard = Object.freeze({
+ supportedMethods: "basic-card",
+});
+
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+
+// Methods
+const validMethods = Object.freeze([validMethodBasicCard, validMethod, applePay]);
+
+// Amounts
+const validAmount = Object.freeze({
+ currency: "USD",
+ value: "1.00",
+});
+
+const invalidAmount = Object.freeze({
+ currency: "¡INVALID!",
+ value: "A1.0",
+});
+
+const negativeAmount = Object.freeze({
+ currency: "USD",
+ value: "-1.00",
+});
+
+// Totals
+const validTotal = Object.freeze({
+ label: "Valid Total",
+ amount: validAmount,
+});
+
+const invalidTotal = Object.freeze({
+ label: "Invalid Total",
+ amount: invalidAmount,
+});
+
+const invalidNegativeTotal = Object.freeze({
+ label: "Invalid negative total",
+ amount: negativeAmount,
+});
+
+// PaymentDetailsInit
+const validDetails = Object.freeze({
+ total: validTotal,
+});
+
+const invalidDetailsNegativeTotal = Object.freeze({
+ total: invalidNegativeTotal,
+});
+
+// PaymentOptions
+const validOptions = Object.freeze({
+ requestShipping: true,
+});
+
+// PaymentItem
+const validPaymentItem = Object.freeze({
+ amount: validAmount,
+ label: "Valid payment item",
+});
+
+const invalidPaymentItem = Object.freeze({
+ amount: invalidAmount,
+ label: "Invalid payment item",
+});
+
+// PaymentItem
+const validPaymentItems = Object.freeze([validPaymentItem]);
+const invalidPaymentItems = Object.freeze([invalidPaymentItem]);
+
+// PaymentShippingOption
+const invalidShippingOption = Object.freeze({
+ id: "abc",
+ label: "Invalid shipping option",
+ amount: invalidAmount,
+ selected: true,
+});
+
+// PaymentShippingOptions
+const validShippingOption = Object.freeze({
+ id: "abc",
+ label: "valid shipping option",
+ amount: validAmount,
+});
+
+const validShippingOptions = Object.freeze([validShippingOption]);
+const invalidShippingOptions = Object.freeze([invalidShippingOption]);
+
+// PaymentDetailsModifier
+const validModifier = Object.freeze({
+ additionalDisplayItems: validPaymentItems,
+ supportedMethods: "valid-but-wont-ever-match",
+ total: validTotal,
+});
+
+const modifierWithInvalidDisplayItems = Object.freeze({
+ additionalDisplayItems: invalidPaymentItems,
+ supportedMethods: "basic-card",
+ total: validTotal,
+});
+
+const modifierWithValidDisplayItems = Object.freeze({
+ additionalDisplayItems: validPaymentItems,
+ supportedMethods: "basic-card",
+ total: validTotal,
+});
+
+const modifierWithInvalidTotal = Object.freeze({
+ additionalDisplayItems: validPaymentItems,
+ supportedMethods: "basic-card",
+ total: invalidTotal,
+});
+
+const recursiveData = {};
+recursiveData.foo = recursiveData;
+Object.freeze(recursiveData);
+
+const modifierWithRecursiveData = Object.freeze({
+ supportedMethods: "basic-card",
+ total: validTotal,
+ data: recursiveData,
+});
+
+function testBadUpdate(button, badDetails, expectedError, errorCode) {
+ button.disabled = true;
+ promise_test(async t => {
+ const request = new PaymentRequest(
+ validMethods,
+ validDetails,
+ validOptions
+ );
+ request.onshippingaddresschange = event => {
+ event.updateWith(badDetails);
+ };
+ // First we check the bad update.
+ const acceptPromise = request.show();
+ let test_func;
+ if (typeof expectedError == "function") {
+ test_func = promise_rejects_js;
+ } else {
+ test_func = promise_rejects_dom;
+ }
+ await test_func(
+ t,
+ expectedError,
+ acceptPromise,
+ "badDetails must cause acceptPromise to reject with expectedError"
+ );
+ // The request [[state]] is now "closed", so let's check for InvalidStateError
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ request.show(),
+ "show() must reject with InvalidStateError"
+ );
+ }, button.innerText.trim());
+}
+</script>
+<h2>updateWith() method - "abort the update"</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is shown, change the shipping address.
+</p>
+<ol>
+ <li>
+ <button onclick="
+ const rejectedPromise = Promise.reject(new SyntaxError('test'));
+ testBadUpdate(this, rejectedPromise, 'AbortError');
+ ">
+ Rejection of detailsPromise must abort the update with an "AbortError" DOMException.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const invalidDetails = { total: `this will cause a TypeError!` };
+ testBadUpdate(this, invalidDetails, TypeError);
+ ">
+ Total in the update is a string, so converting to IDL must abort the update with a TypeError.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const invalidDetails = { total: recursiveData };
+ testBadUpdate(this, invalidDetails, TypeError);
+ ">
+ Total is recursive, so converting to IDL must abort the update with a TypeError.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ testBadUpdate(this, invalidDetailsNegativeTotal, TypeError);
+ ">
+ Updating with a negative total results in a TypeError.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const badDetails = Object.assign({}, validDetails, { displayItems: invalidPaymentItems });
+ testBadUpdate(this, badDetails, RangeError);
+ ">
+ Updating with a displayItem with an invalid currency results in RangeError.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const duplicateShippingOptions = [validShippingOption, validShippingOption];
+ const badDetails = Object.assign({}, validDetails, { shippingOptions: duplicateShippingOptions });
+ testBadUpdate(this, badDetails, TypeError);
+ ">
+ Updating with duplicate shippingOptions (same IDs) results in a TypeError.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const badDetails = Object.assign({}, validDetails, { shippingOptions: invalidShippingOptions });
+ testBadUpdate(this, badDetails, RangeError);
+ ">
+ Updating with a shippingOption with an invalid currency value results in a RangError.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ // validModifier is there as to avoid false positives - it should just get ignored
+ const badModifiers = { modifiers: [ modifierWithInvalidTotal, validModifier ] };
+ const badDetails = Object.assign({}, validDetails, badModifiers);
+ testBadUpdate(this, badDetails, RangeError);
+ ">
+ Must throw a RangeError when a modifier's total item has an invalid currency.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ // validModifier is there as to avoid false positives - it should just get ignored
+ const badModifiers = { modifiers: [ modifierWithInvalidDisplayItems, validModifier ] };
+ const badDetails = Object.assign({}, validDetails, badModifiers);
+ testBadUpdate(this, badDetails, RangeError);
+ ">
+ Must throw a RangeError when a modifier display item has an invalid currency.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ // validModifier is there as to avoid false positives - it should just get ignored
+ const badModifiers = { modifiers: [ modifierWithRecursiveData, validModifier ] };
+ const badDetails = Object.assign({}, validDetails, badModifiers);
+ testBadUpdate(this, badDetails, TypeError);
+ ">
+ Must throw as Modifier has a recursive dictionary.
+ </button>
+ </li>
+ <li>
+ <button onclick="done();">Done!</button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html
new file mode 100644
index 00000000000..fb16de5699a
--- /dev/null
+++ b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html
@@ -0,0 +1,125 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method">
+<title>updateWith() method - state machine checks</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const validMethod = Object.freeze({ supportedMethods: "basic-card" });
+const validMethods = Object.freeze([validMethod, applePay]);
+const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+});
+const validShippingOption = Object.freeze({
+ id: "a-shipping-option",
+ label: "A shipping option",
+ amount: validAmount,
+ selected: true,
+});
+const validDetails = Object.freeze({
+ total: validTotal,
+ shippingOptions: [validShippingOption],
+});
+const validOptions = Object.freeze({
+ requestShipping: true,
+});
+
+function getPaymentPromises() {
+ const request = new PaymentRequest(validMethods, validDetails, validOptions);
+ const eventPromise = new Promise(resolve => {
+ request.addEventListener("shippingaddresschange", resolve);
+ });
+ const responsePromise = request.show();
+ return { eventPromise, responsePromise };
+}
+
+function testRequestIsClosed(button) {
+ button.disabled = "true";
+ promise_test(async t => {
+ const { eventPromise, responsePromise } = getPaymentPromises();
+ const event = await eventPromise;
+ // We are going to abort the responsePromise, so we can ignore error.
+ responsePromise.catch(err => err);
+ // Set request.[[state]] to closed
+ await event.target.abort();
+ assert_throws_dom(
+ "InvalidStateError",
+ () => {
+ event.updateWith(validDetails);
+ },
+ "request.[[state]] is not interactive, must throw an InvalidStateError."
+ );
+ responsePromise.catch(err => err);
+ }, button.textContent.trim());
+}
+
+function testRequestIsUpdating(button) {
+ button.disabled = "true";
+ promise_test(async t => {
+ const { eventPromise, responsePromise } = getPaymentPromises();
+ const event = await eventPromise;
+ // We are going to put a promise into a pending state
+ // check that a second call to updateWith() throws,
+ // then resolve the pending promise below.
+ let resolver;
+ const pendingPromise = new Promise(resolve => {
+ resolver = resolve;
+ });
+ // Set request.[[updating]] to true
+ event.updateWith(pendingPromise);
+ assert_throws_dom(
+ "InvalidStateError",
+ () => {
+ event.updateWith(validDetails);
+ },
+ "request.[[updating]] to true, must throw an InvalidStateError."
+ );
+ // We got the error we wanted, so let's resolve with valid details.
+ resolver(validDetails);
+ await pendingPromise;
+ await event.target.abort();
+ responsePromise.catch(err => err);
+ }, button.textContent.trim());
+}
+
+</script>
+<h2>updateWith() method - state machine checks</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is shown, select a different shipping address once. Then pay.
+</p>
+<ol>
+ <li id="test-0">
+ <button onclick="testRequestIsClosed(this);">
+ When updateWith() is called, if request.[[state]] is not "interactive", then throw an " InvalidStateError" DOMException.
+ </button>
+ </li>
+ <li id="test-1">
+ <button onclick="testRequestIsUpdating(this);">
+ When updateWith() is called, If request.[[updating]] is true, then throw an "InvalidStateError" DOMException.
+ </button>
+ </li>
+ <li>
+ <button onclick="done();">Done!</button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html
index 9a60fe7a4c0..fffd3b3ec54 100644
--- a/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html
+++ b/tests/wpt/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html
@@ -29,7 +29,9 @@ test(() => {
// Github issue: https://github.com/w3c/browser-payment-api/issues/546
test(() => {
const untrustedEvents = [
- new PaymentRequestUpdateEvent("just a test")
+ new PaymentRequestUpdateEvent("just a test"),
+ new PaymentRequestUpdateEvent("shippingaddresschange"),
+ new PaymentRequestUpdateEvent("shippingoptionchange"),
].forEach(ev => {
assert_throws_dom(
"InvalidStateError",
@@ -45,7 +47,9 @@ test(() => {
test(() => {
const request = new PaymentRequest(defaultMethods, defaultDetails);
const untrustedEvents = [
- new PaymentRequestUpdateEvent("just a test")
+ new PaymentRequestUpdateEvent("just a test"),
+ new PaymentRequestUpdateEvent("shippingaddresschange"),
+ new PaymentRequestUpdateEvent("shippingoptionchange"),
].map(ev => {
request.dispatchEvent(ev); // set .target and dispatch flag
// unstrusted event.
diff --git a/tests/wpt/tests/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html b/tests/wpt/tests/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html
new file mode 100644
index 00000000000..94e6fa5105b
--- /dev/null
+++ b/tests/wpt/tests/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentvalidationerrors-shippingaddress">
+<title>
+ PaymentValidationErrors' `shippingAddress` member (AddressErrors)
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../payment-response/helpers.js"></script>
+<script>
+function retryShowsShippingAddressMember(button, error) {
+ button.disabled = true;
+ promise_test(async t => {
+ const options = {
+ requestShipping: true,
+ }
+ const { response } = await getPaymentRequestResponse(options);
+ await response.retry({ shippingAddress: error });
+ await response.complete("success");
+ }, button.textContent.trim());
+}
+</script>
+<h2>
+ Manual Test for PaymentValidationErrors' `shippingAddress` member - Please run in order!
+</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When presented with the payment sheet, use any card and select to "Pay".
+ You will be asked to retry the payment and an error should be shown somewhere
+ in the UI. The expected error string is described in each individual test.
+ If you see the error, hit "Pay" again. If you don't see the error,
+ abort the payment request by hitting "esc" - which means that particular test
+ has failed.
+</p>
+<ol>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { addressLine: 'ADDRESSLINE ERROR' });">
+ The payment sheet shows "ADDRESSLINE ERROR" for the shipping address' addressLine.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { city: 'CITY ERROR' });">
+ The payment sheet shows "CITY ERROR" for the shipping address' city.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { country: 'COUNTRY ERROR' });">
+ The payment sheet shows "COUNTRY ERROR" for the shipping address' country.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { dependentLocality: 'DEPENDENTLOCALITY ERROR' });">
+ The payment sheet shows "DEPENDENTLOCALITY ERROR" for the shipping address' dependentLocality.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { organization: 'ORGANIZATION ERROR' });">
+ The payment sheet shows "ORGANIZATION ERROR" for the shipping address' organization.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { phone: 'PHONE ERROR' });">
+ The payment sheet shows "PHONE ERROR" for the shipping address' phone.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { postalCode: 'POSTALCODE ERROR' });">
+ The payment sheet shows "POSTALCODE ERROR" for the shipping address' postal code.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { recipient: 'RECIPIENT ERROR' });">
+ The payment sheet shows "RECIPIENT ERROR" for the shipping address' recipient.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { region: 'REGION ERROR' });">
+ The payment sheet shows "REGION ERROR" for the shipping address' region.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { regionCode: 'REGIONCODE ERROR' });">
+ The payment sheet shows "REGIONCODE ERROR" for the shipping address' region code.
+ </button>
+ </li>
+ <li>
+ <button onclick="retryShowsShippingAddressMember(this, { sortingCode: 'SORTINGCODE ERROR' });">
+ The payment sheet shows "SORTINGCODE ERROR" for the shipping address' sorting code.
+ </button>
+ </li>
+ <li>
+ <button onclick="done();">
+ Done!
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">owners</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/algorithms-manual.https.html b/tests/wpt/tests/payment-request/algorithms-manual.https.html
new file mode 100644
index 00000000000..b90c312aba3
--- /dev/null
+++ b/tests/wpt/tests/payment-request/algorithms-manual.https.html
@@ -0,0 +1,176 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#algorithms">
+<title>
+ Payment Request algorithms
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({
+ explicit_done: true,
+ explicit_timeout: true,
+});
+const methods = [
+ {
+ supportedMethods: "basic-card",
+ },
+ {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ },
+ }
+];
+const shippingOptions = {
+ shippingOptions: [
+ {
+ id: "fail",
+ label: "Option 1",
+ amount: {
+ currency: "USD",
+ value: "5.00",
+ },
+ selected: true,
+ },
+ {
+ id: "pass",
+ label: "Option 2",
+ amount: {
+ currency: "USD",
+ value: "5.00",
+ },
+ },
+ ],
+};
+
+const detailsNoShippingOptions = {
+ total: {
+ label: "Total due",
+ amount: {
+ currency: "USD",
+ value: "1.0",
+ },
+ },
+};
+
+const detailsWithShippingOptions = Object.assign(
+ {
+ total: {
+ label: "Total due",
+ amount: {
+ currency: "USD",
+ value: "1.0",
+ },
+ },
+ },
+ shippingOptions
+);
+
+const options = {
+ requestShipping: true,
+};
+
+function testFireEvent(button, details, eventName, expectRequestProps) {
+ button.disabled = true;
+ promise_test(async t => {
+ new PaymentRequest(methods, detailsNoShippingOptions, options);
+ const request = new PaymentRequest(methods, details, options);
+ const handlerPromise = new Promise(resolve => {
+ request[`on${eventName}`] = event => {
+ // "prevent immediate propagation" flag is set.
+ // This listener below won't fire!
+ event.updateWith(details);
+ resolve(event);
+ };
+ });
+ // This listener should never fire because the
+ // the event handler caused "prevent immediate propagation" to be set.
+ request.addEventListener(
+ eventName,
+ t.unreached_func("Second event listener should never fire")
+ );
+ const response = await request.show();
+ const event = await handlerPromise;
+ assert_true(
+ event instanceof window.PaymentRequestUpdateEvent,
+ "Expected instances of PaymentRequestUpdateEvent"
+ );
+ await response.complete("success");
+ }, button.textContent.trim());
+}
+
+async function runAbortTest(button) {
+ button.disabled = true;
+ const { textContent: testName } = button;
+ promise_test(async t => {
+ const request = new PaymentRequest(methods, detailsNoShippingOptions);
+ // Await the user to abort
+ await promise_rejects_dom(t, "AbortError", request.show());
+ // [[state]] is now closed
+ await promise_rejects_dom(t, "InvalidStateError", request.show());
+ }, testName.trim());
+}
+</script>
+<h2>
+ Tests for "algorithms" section
+</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<section>
+ <h3 id="abort-algo">
+ User aborts the payment request algorithm
+ </h3>
+ <link rel="help" href="https://w3c.github.io/payment-request/#user-aborts-the-payment-request-algorithm">
+ <p>
+ When presented with the payment sheet, abort the payment request (e.g., by hitting the esc key or pressing a UA provided button).
+ </p>
+ <ol>
+ <li>
+ <button onclick="runAbortTest(this);">
+ If the user aborts, the UA must run the user aborts the payment request algorithm.
+ </button>
+ </li>
+ </ol>
+</section>
+
+<section>
+ <h3 id="shipping-address-changed-algo">Shipping address changed algorithm</h3>
+ <link rel="help" href="https://www.w3.org/TR/payment-request/#shipping-address-changed-algorithm">
+ <p>
+ When prompted, please change or enter a new shipping address and then select Pay.
+ </p>
+ <ol>
+ <li>
+ <button onclick="testFireEvent(this, detailsWithShippingOptions, 'shippingaddresschange', {});">
+ The shipping address changed algorithm runs when the user provides a new shipping address.
+ </button>
+ </li>
+ </ol>
+</section>
+
+<section>
+ <h3 id="shipping-option-changed-algo">Shipping option changed algorithm</h3>
+ <link rel="help" href="https://w3c.github.io/payment-request/#shipping-option-changed-algorithm">
+ <p>
+ Finally, when prompted, please select "shipping option 2" and then select Pay.
+ </p>
+ <ol>
+ <li>
+ <button onclick="testFireEvent(this, detailsWithShippingOptions, 'shippingoptionchange', {}, 'pass'); done();">
+ The shipping option changed algorithm runs when the user chooses a new shipping option.
+ </button>
+ </li>
+ </ol>
+</section>
+
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/billing-address-changed-manual.https.html b/tests/wpt/tests/payment-request/billing-address-changed-manual.https.html
new file mode 100644
index 00000000000..d03f7615187
--- /dev/null
+++ b/tests/wpt/tests/payment-request/billing-address-changed-manual.https.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html> <meta charset="utf-8" />
+<title>Test for requesting billing address</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({
+ explicit_done: true,
+ explicit_timeout: true,
+ });
+
+ const methods = [
+ { supportedMethods: "basic-card" },
+ {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ },
+ },
+ ];
+
+ const details = {
+ total: {
+ label: "label",
+ amount: { currency: "USD", value: "5.00" },
+ },
+ };
+ test(() => {
+ assert_true(
+ "onpaymentmethodchange" in PaymentRequest.prototype,
+ "The paymentmethodchange is not supported"
+ );
+ }, "onpaymentmethodchange is in prototype");
+
+ function dontRequestBillingAddress() {
+ promise_test(async t => {
+ const request = new PaymentRequest(methods, details, {});
+ const showPromise = request.show();
+
+ // Let's check the method data from event.
+ const { methodDetails } = await new Promise(resolve =>
+ request.addEventListener("paymentmethodchange", resolve)
+ );
+
+ assert_true("billingAddress" in methodDetails);
+ assert_equals(
+ methodDetails.billingAddress,
+ null,
+ "Expected methodDetails.billingAddress to be null"
+ );
+ await request.abort();
+ });
+ }
+
+ function requestBillingAddress() {
+ promise_test(async t => {
+ const request = new PaymentRequest(methods, details, {
+ requestBillingAddress: true,
+ });
+ const showPromise = request.show();
+
+ // Let's check the method data from event.
+ const { methodDetails } = await new Promise(resolve =>
+ request.addEventListener("paymentmethodchange", resolve)
+ );
+
+ assert_true("billingAddress" in methodDetails);
+
+ const { billingAddress } = methodDetails;
+ assert_true(
+ billingAddress instanceof ContactAddress,
+ "Expected instance of ContactAddress"
+ );
+ await request.abort();
+ });
+ }
+</script>
+
+<h2>Request billing address</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the
+ page. Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is presented, select a payment method (e.g., a credit
+ card).
+</p>
+<ol>
+ <li>
+ <button onclick="dontRequestBillingAddress()">
+ When no billing address is requested,
+ `PaymentMethodChangeEvent.methodDetails.billingAddress` is null.
+ </button>
+ </li>
+ <li>
+ <button onclick="requestBillingAddress()">
+ When billing address is
+ requested,`PaymentMethodChangeEvent.methodDetails.billingAddress` is a
+ `ContactAddress`.
+ </button>
+ </li>
+ <li><button onclick="done()">Done!</button></li>
+</ol>
+<small>
+ If you find a buggy test, please
+ <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> and
+ tag one of the
+ <a
+ href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml"
+ >suggested reviewers</a
+ >.
+</small>
diff --git a/tests/wpt/tests/payment-request/change-shipping-option-manual.https.html b/tests/wpt/tests/payment-request/change-shipping-option-manual.https.html
new file mode 100644
index 00000000000..438001804ac
--- /dev/null
+++ b/tests/wpt/tests/payment-request/change-shipping-option-manual.https.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for PaymentRequest shippingOption attribute</title>
+<link rel="help" href="https://w3c.github.io/payment-request/#shippingoption-attribute">
+<link rel="help" href="https://w3c.github.io/payment-request/#onshippingoptionchange-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const validMethod = Object.freeze({ supportedMethods: "basic-card" });
+const applePayMethod = {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ },
+};
+const validMethods = Object.freeze([validMethod, applePayMethod]);
+const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+});
+const validDetails = Object.freeze({ total: validTotal });
+
+const validShippingOption1 = Object.freeze({
+ id: "valid-1",
+ label: "PICK ME!",
+ amount: validAmount,
+ selected: false,
+});
+
+const validShippingOption2 = Object.freeze({
+ id: "initially-selected",
+ label: "Valid shipping option 2",
+ amount: validAmount,
+ selected: true,
+});
+
+const requestShipping = Object.freeze({
+ requestShipping: true,
+});
+
+function testShippingOptionChanged() {
+ promise_test(async t => {
+ const detailsWithShippingOptions = Object.assign({}, validDetails, {
+ shippingOptions: [validShippingOption1, validShippingOption2],
+ });
+ const request = new PaymentRequest(
+ validMethods,
+ detailsWithShippingOptions,
+ requestShipping
+ );
+ assert_equals(
+ request.shippingOption,
+ "initially-selected",
+ "Must be 'initially-selected', as the selected member is true"
+ );
+ const listenerPromise = new Promise(resolve => {
+ request.addEventListener("shippingoptionchange", () => {
+ resolve(request.shippingOption);
+ });
+ });
+ const handlerPromise = new Promise(resolve => {
+ request.onshippingoptionchange = () => {
+ resolve(request.shippingOption);
+ };
+ });
+ request.show().catch(err => err);
+
+ const results = await Promise.all([listenerPromise, handlerPromise]);
+ assert_true(
+ results.every(result => result === "valid-1"),
+ "Expected valid-1 as the shippingOption"
+ );
+ await request.abort();
+ });
+ done();
+}
+</script>
+
+<h2>PaymentRequest shippingOption attribute</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is presented, select "PICK ME!" as the shipping option.
+</p>
+<ol>
+ <li>
+ <button onclick="testShippingOptionChanged()">
+ When the shipping option is manually changed, request.shippingOption represents the user's choice.
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/change-shipping-option-select-last-manual.https.html b/tests/wpt/tests/payment-request/change-shipping-option-select-last-manual.https.html
new file mode 100644
index 00000000000..4ad31d65317
--- /dev/null
+++ b/tests/wpt/tests/payment-request/change-shipping-option-select-last-manual.https.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for PaymentDetailsBase's shippingOptions member</title>
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentdetailsbase-shippingoptions">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const validMethods = Object.freeze([
+ { supportedMethods: "basic-card" },
+ {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ },
+ },
+]);
+const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+});
+const validDetails = Object.freeze({ total: validTotal });
+
+const validShippingOption1 = Object.freeze({
+ id: "fail-if-selected-1",
+ label: "FAIL if selected 1",
+ amount: validAmount,
+ selected: true,
+});
+
+const validShippingOption2 = Object.freeze({
+ id: "fail-if-selected-2",
+ label: "FAIL if selected 2",
+ amount: validAmount,
+ selected: false,
+});
+
+const validShippingOption3 = Object.freeze({
+ id: "pass-if-selected",
+ label: "THIS MUST BE AUTOMATICALLY SELECTED",
+ amount: validAmount,
+ selected: true,
+});
+
+function testShippingOptionChanged(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const detailsWithShippingOptions = {
+ ...validDetails,
+ shippingOptions: [
+ validShippingOption1,
+ validShippingOption2,
+ validShippingOption3,
+ ],
+ };
+ const request = new PaymentRequest(
+ validMethods,
+ detailsWithShippingOptions,
+ { requestShipping: true }
+ );
+ assert_equals(
+ request.shippingOption,
+ "pass-if-selected",
+ "Must be 'pass-if-selected', as the selected member is true"
+ );
+ request.onshippingoptionchange = () => {
+ assert_unreached("onshippingoptionchange fired unexpectedly");
+ };
+ const response = await request.show();
+ assert_equals(response.shippingOption, "pass-if-selected");
+ response.complete();
+ }, button.textContent.trim());
+ done();
+}
+</script>
+
+<h2>PaymentRequest shippingOption attribute</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is presented, hit pay.
+</p>
+<ol>
+ <li>
+ <button onclick="testShippingOptionChanged(this)">
+ When default shipping option is pre-selected, must not fire onshippingoptionchange
+ and PaymentResponse must reflect the pre-selected option.
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/dynamically-change-shipping-options-manual.https.html b/tests/wpt/tests/payment-request/dynamically-change-shipping-options-manual.https.html
new file mode 100644
index 00000000000..0e6670a1b82
--- /dev/null
+++ b/tests/wpt/tests/payment-request/dynamically-change-shipping-options-manual.https.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<title>Test for PaymentRequest shippingOption dynamic updating</title>
+<link
+ rel="help"
+ href="https://w3c.github.io/payment-request/#shippingoption-attribute"
+/>
+<link
+ rel="help"
+ href="https://w3c.github.io/payment-request/#onshippingoptionchange-attribute"
+/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({ explicit_done: true, explicit_timeout: true });
+ const validMethod = Object.freeze({ supportedMethods: "basic-card" });
+ const applePayMethod = {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ },
+ };
+ const validMethods = Object.freeze([validMethod, applePayMethod]);
+ const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+ const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+ });
+ const validDetails = Object.freeze({ total: validTotal });
+
+ const initialValidShippingOption = Object.freeze({
+ id: "default-method",
+ label: "Default shipping method",
+ amount: validAmount,
+ selected: true,
+ });
+
+ const validDynamicShippingOption = Object.freeze({
+ id: "dynamically-added-id",
+ label: "Dynamically added shipping option",
+ amount: validAmount,
+ selected: false,
+ });
+
+ const requestShipping = Object.freeze({
+ requestShipping: true,
+ });
+
+ function testShippingOptionChanged() {
+ promise_test(async (t) => {
+ const detailsWithShippingOptions = {
+ ...validDetails,
+ shippingOptions: [initialValidShippingOption],
+ };
+ const request = new PaymentRequest(
+ validMethods,
+ detailsWithShippingOptions,
+ requestShipping
+ );
+ const shippingAddressChangeListener = new Promise((resolve) => {
+ request.addEventListener(
+ "shippingaddresschange",
+ (ev) => {
+ // resolve(request.shippingOption);
+ ev.updateWith({
+ shippingOptions: [
+ initialValidShippingOption,
+ validDynamicShippingOption,
+ ],
+ });
+ resolve();
+ },
+ { once: true }
+ );
+ });
+ const handlerPromise = new Promise((resolve) => {
+ request.onshippingoptionchange = () => {
+ resolve(request.shippingOption);
+ };
+ });
+ request.show().catch((err) => err);
+
+ const results = await Promise.all([
+ shippingAddressChangeListener,
+ handlerPromise,
+ ]);
+ assert_true(
+ results[1] === "dynamically-added-id",
+ "Expected dynamically-added-id as the shippingOption"
+ );
+ await request.abort();
+ });
+ }
+</script>
+
+<h2>PaymentRequest shippingOption attribute</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the
+ page. Each button (except the 'Done' button) will bring up the Payment Request
+ UI window.
+</p>
+<ol>
+ <li>
+ When the payment sheet is presented, view options for Shipping Method. There
+ should only be one: "Default shipping method"
+ </li>
+ <li>
+ Change your Shipping Address - either update your existing one by changing
+ something (name, address, etc), or select a different Shipping Address, or
+ add a new Shipping Address and select it.
+ </li>
+ <li>
+ Go back to Shipping Method, and there is now an option called "Dynamically
+ added shipping option". Select it
+ </li>
+ <li>
+ Click on the 'Done' button
+ </li>
+</ol>
+<ul>
+ <li>
+ <button onclick="testShippingOptionChanged()">
+ When the address is changed, shipping methods can be updated
+ </button>
+ </li>
+ <li>
+ <button onclick="done()">Done</button>
+ </li>
+</ul>
+<small>
+ If you find a buggy test, please
+ <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> and
+ tag one of the
+ <a
+ href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml"
+ >suggested reviewers</a
+ >.
+</small>
diff --git a/tests/wpt/tests/payment-request/historical.https.html b/tests/wpt/tests/payment-request/historical.https.html
index aa183a58cdb..3e881d0122b 100644
--- a/tests/wpt/tests/payment-request/historical.https.html
+++ b/tests/wpt/tests/payment-request/historical.https.html
@@ -10,7 +10,7 @@
["paymentRequestID", "PaymentResponse"],
// https://github.com/w3c/browser-payment-api/pull/258
- ["careOf", "PaymentAddress"],
+ ["careOf", "ContactAddress"],
// https://github.com/w3c/browser-payment-api/pull/219
["totalAmount", "PaymentResponse"],
@@ -20,7 +20,7 @@
["paymentRequestId", "PaymentResponse"],
// https://github.com/w3c/payment-request/pull/765
- ["languageCode", "PaymentAddress"],
+ ["languageCode", "ContactAddress"],
//https://github.com/whatwg/html/pull/5915
["allowPaymentRequest", "HTMLIFrameElement"],
diff --git a/tests/wpt/tests/payment-request/payment-request-constructor-thcrash.https.html b/tests/wpt/tests/payment-request/payment-request-constructor-thcrash.https.html
new file mode 100644
index 00000000000..b6003070851
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-request-constructor-thcrash.https.html
@@ -0,0 +1,254 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Crash tests PaymentRequest Constructor</title>
+<link rel="help" href="https://w3c.github.io/browser-payment-api/#constructor">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+"use strict";
+const ABUSIVE_AMOUNT = 100000;
+
+const applePay = {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+};
+
+const basicCard = Object.freeze({
+ supportedMethods: "basic-card",
+});
+
+const defaultAmount = Object.freeze({
+ currency: "USD",
+ value: "1.00",
+});
+
+const evilAmount = Object.freeze({
+ currency: "USD",
+ value: "1".repeat(ABUSIVE_AMOUNT),
+});
+
+const defaultMethods = Object.freeze([basicCard, applePay]);
+
+const defaultTotal = Object.freeze({
+ label: "label",
+ amount: defaultAmount,
+});
+
+const evilTotal = Object.freeze({
+ label: "a".repeat(ABUSIVE_AMOUNT),
+ amount: evilAmount,
+});
+
+const defaultDetails = Object.freeze({
+ total: defaultTotal,
+ get id() {
+ return Math.random();
+ },
+});
+
+const defaultPaymentItem = Object.freeze({
+ label: "label",
+ amount: defaultAmount,
+});
+
+const defaultShippingOption = {
+ get id() {
+ return "shipping option " + Math.random();
+ },
+ amount: defaultAmount,
+ label: "shipping option label",
+};
+
+// First argument is sequence<PaymentMethodData> methodData
+test(() => {
+ let evilMethods = [Object.assign({}, basicCard)];
+ // smoke test
+ try {
+ new PaymentRequest(evilMethods, defaultDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, let's add an abusive amount of methods.
+ while (evilMethods.length < ABUSIVE_AMOUNT) {
+ evilMethods.push({supportedMethods: "evil-method" + evilMethods.length});
+ }
+ try {
+ new PaymentRequest(evilMethods, defaultDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if there is an abusive number of payment methods in the methodData sequence");
+
+// PaymentMethodData.supportedMethods
+test(() => {
+ const supportedMethods = "basic-card";
+ // Smoke test
+ try {
+ new PaymentRequest([{ supportedMethods }], defaultDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, we make supportedMethods super large
+ const evilMethodData = [
+ {
+ supportedMethods: supportedMethods.repeat(ABUSIVE_AMOUNT),
+ },
+ ];
+ try {
+ new PaymentRequest(evilMethodData, defaultDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if PaymentMethodData.supportedMethods is an abusive length");
+
+// PaymentDetailsInit.id
+test(() => {
+ const id = "abc";
+ // Smoke Test
+ try {
+ new PaymentRequest(
+ defaultMethods,
+ Object.assign({}, defaultDetails, { id })
+ );
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, we make the id super large;
+ const evilDetails = Object.assign({}, defaultDetails, {
+ id: id.repeat(ABUSIVE_AMOUNT),
+ });
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if the request id has an abusive length");
+
+// PaymentDetailsInit.total.label
+test(() => {
+ const evilDetails = Object.assign({}, defaultDetails);
+ // Smoke Test
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, we make the label super large;
+ evilDetails.total = {
+ label: "l".repeat(ABUSIVE_AMOUNT),
+ amount: defaultAmount,
+ };
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if PaymentDetailsInit.total.label is an abusive length");
+
+test(() => {
+ const evilDetails = Object.assign({}, defaultDetails);
+ // Smoke Test
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, we can use evilAmount
+ evilDetails.total = evilAmount;
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if total.amount.value is an abusive length");
+
+for (const [prop, defaultValue] of [
+ ["displayItems", defaultPaymentItem],
+ ["shippingOptions", defaultShippingOption],
+]) {
+ test(() => {
+ const evilDetails = Object.assign({}, defaultDetails);
+ evilDetails[prop] = [defaultValue];
+ // Smoke Test
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ while (evilDetails[prop].length < ABUSIVE_AMOUNT) {
+ evilDetails[prop] = evilDetails[prop].concat(evilDetails[prop]);
+ }
+ // Now, construct with evil items!
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+ }, `Don't crash if details.${prop} has an abusive number of items`);
+}
+
+test(() => {
+ const evilDetails = Object.assign({}, defaultDetails);
+ const evilShippingOption = Object.assign({}, defaultShippingOption);
+ evilDetails.shippingOptions = [evilShippingOption];
+ // Smoke Test
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, we make the label super large;
+ evilShippingOption.label = "l".repeat(ABUSIVE_AMOUNT);
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if PaymentShippingOptions.label is an abusive length");
+
+test(() => {
+ const evilDetails = Object.assign({}, defaultDetails);
+ const evilShippingOption = Object.assign({}, defaultShippingOption);
+ evilDetails.shippingOptions = [evilShippingOption];
+ // Smoke Test
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, we make use of evilAmount;
+ evilShippingOption.amount = evilAmount;
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if the PaymentShippingOptions.amount.value is an abusive length");
+
+test(() => {
+ const evilDetails = Object.assign({}, defaultDetails);
+ const evilDisplayItem = Object.assign({}, defaultPaymentItem);
+ evilDetails.displayItems = [evilDisplayItem];
+ // Smoke Test
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_unreached("failed smoke test: " + err.stack);
+ }
+ // Now, we make the label super large;
+ evilDisplayItem.label = "l".repeat(ABUSIVE_AMOUNT);
+ try {
+ new PaymentRequest(defaultMethods, evilDetails);
+ } catch (err) {
+ assert_equals(err.name, "TypeError", "must be a TypeError");
+ }
+}, "Don't crash if PaymentItem.label is an abusive length");
+</script>
diff --git a/tests/wpt/tests/payment-request/payment-request-constructor.https.sub.html b/tests/wpt/tests/payment-request/payment-request-constructor.https.sub.html
index c1ecc225838..9b0ad06454f 100644
--- a/tests/wpt/tests/payment-request/payment-request-constructor.https.sub.html
+++ b/tests/wpt/tests/payment-request/payment-request-constructor.https.sub.html
@@ -240,7 +240,7 @@ test(() => {
test(() => {
smokeTest();
- for (const prop in ["displayItems", "modifiers"]) {
+ for (const prop in ["displayItems", "shippingOptions", "modifiers"]) {
try {
const details = Object.assign({}, defaultDetails, { [prop]: [] });
new PaymentRequest(defaultMethods, details);
@@ -361,6 +361,186 @@ test(() => {
}
}, "it handles high precision currency values without throwing");
+// Process shipping options:
+
+const defaultShippingOption = Object.freeze({
+ id: "default",
+ label: "",
+ amount: defaultAmount,
+ selected: false,
+});
+const defaultShippingOptions = Object.freeze([
+ Object.assign({}, defaultShippingOption),
+]);
+
+test(() => {
+ smokeTest();
+ for (const amount of invalidAmounts) {
+ const invalidAmount = Object.assign({}, defaultAmount, {
+ value: amount,
+ });
+ const invalidShippingOption = Object.assign({}, defaultShippingOption, {
+ amount: invalidAmount,
+ });
+ const details = Object.assign({}, defaultDetails, {
+ shippingOptions: [invalidShippingOption],
+ });
+ assert_throws_js(
+ TypeError,
+ () => {
+ new PaymentRequest(defaultMethods, details, { requestShipping: true });
+ },
+ `Expected TypeError for option.amount.value: "${amount}"`
+ );
+ }
+}, `For each option in details.shippingOptions: if option.amount.value is not a valid decimal monetary value, then throw a TypeError`);
+
+test(() => {
+ smokeTest();
+ const shippingOptions = [defaultShippingOption];
+ const details = Object.assign({}, defaultDetails, { shippingOptions });
+ const request = new PaymentRequest(defaultMethods, details);
+ assert_equals(
+ request.shippingOption,
+ null,
+ "shippingOption must be null, as requestShipping is missing"
+ );
+ // defaultDetails lacks shipping options
+ const request2 = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: true,
+ });
+ assert_equals(
+ request2.shippingOption,
+ null,
+ `request2.shippingOption must be null`
+ );
+}, "If there is no selected shipping option, then PaymentRequest.shippingOption remains null");
+
+test(() => {
+ smokeTest();
+ const selectedOption = Object.assign({}, defaultShippingOption, {
+ selected: true,
+ id: "the-id",
+ });
+ const shippingOptions = [selectedOption];
+ const details = Object.assign({}, defaultDetails, { shippingOptions });
+ const requestNoShippingRequested1 = new PaymentRequest(
+ defaultMethods,
+ details
+ );
+ assert_equals(
+ requestNoShippingRequested1.shippingOption,
+ null,
+ "Must be null when no shipping is requested (defaults to false)"
+ );
+ const requestNoShippingRequested2 = new PaymentRequest(
+ defaultMethods,
+ details,
+ { requestShipping: false }
+ );
+ assert_equals(
+ requestNoShippingRequested2.shippingOption,
+ null,
+ "Must be null when requestShipping is false"
+ );
+ const requestWithShipping = new PaymentRequest(defaultMethods, details, {
+ requestShipping: "truthy value",
+ });
+ assert_equals(
+ requestWithShipping.shippingOption,
+ "the-id",
+ "Selected option must be 'the-id'"
+ );
+}, "If there is a selected shipping option, and requestShipping is set, then that option becomes synchronously selected");
+
+test(() => {
+ smokeTest();
+ const failOption1 = Object.assign({}, defaultShippingOption, {
+ selected: true,
+ id: "FAIL1",
+ });
+ const failOption2 = Object.assign({}, defaultShippingOption, {
+ selected: false,
+ id: "FAIL2",
+ });
+ const passOption = Object.assign({}, defaultShippingOption, {
+ selected: true,
+ id: "the-id",
+ });
+ const shippingOptions = [failOption1, failOption2, passOption];
+ const details = Object.assign({}, defaultDetails, { shippingOptions });
+ const requestNoShipping = new PaymentRequest(defaultMethods, details, {
+ requestShipping: false,
+ });
+ assert_equals(
+ requestNoShipping.shippingOption,
+ null,
+ "shippingOption must be null, as requestShipping is false"
+ );
+ const requestWithShipping = new PaymentRequest(defaultMethods, details, {
+ requestShipping: true,
+ });
+ assert_equals(
+ requestWithShipping.shippingOption,
+ "the-id",
+ "selected option must 'the-id"
+ );
+}, "If requestShipping is set, and if there is a multiple selected shipping options, only the last is selected.");
+
+test(() => {
+ smokeTest();
+ const selectedOption = Object.assign({}, defaultShippingOption, {
+ selected: true,
+ });
+ const unselectedOption = Object.assign({}, defaultShippingOption, {
+ selected: false,
+ });
+ const shippingOptions = [selectedOption, unselectedOption];
+ const details = Object.assign({}, defaultDetails, { shippingOptions });
+ const requestNoShipping = new PaymentRequest(defaultMethods, details);
+ assert_equals(
+ requestNoShipping.shippingOption,
+ null,
+ "shippingOption must be null, because requestShipping is false"
+ );
+ assert_throws_js(
+ TypeError,
+ () => {
+ new PaymentRequest(defaultMethods, details, { requestShipping: true });
+ },
+ "Expected to throw a TypeError because duplicate IDs"
+ );
+}, "If there are any duplicate shipping option ids, and shipping is requested, then throw a TypeError");
+
+test(() => {
+ smokeTest();
+ const dupShipping1 = Object.assign({}, defaultShippingOption, {
+ selected: true,
+ id: "DUPLICATE",
+ label: "Fail 1",
+ });
+ const dupShipping2 = Object.assign({}, defaultShippingOption, {
+ selected: false,
+ id: "DUPLICATE",
+ label: "Fail 2",
+ });
+ const shippingOptions = [dupShipping1, defaultShippingOption, dupShipping2];
+ const details = Object.assign({}, defaultDetails, { shippingOptions });
+ const requestNoShipping = new PaymentRequest(defaultMethods, details);
+ assert_equals(
+ requestNoShipping.shippingOption,
+ null,
+ "shippingOption must be null, because requestShipping is false"
+ );
+ assert_throws_js(
+ TypeError,
+ () => {
+ new PaymentRequest(defaultMethods, details, { requestShipping: true });
+ },
+ "Expected to throw a TypeError because duplicate IDs"
+ );
+}, "Throw when there are duplicate shippingOption ids, even if other values are different");
+
// Process payment details modifiers:
test(() => {
smokeTest();
@@ -474,4 +654,44 @@ test(() => {
});
}, "Rethrow any exceptions of JSON-serializing modifier.data");
+//Setting ShippingType attribute during construction
+test(() => {
+ smokeTest();
+ assert_throws_js(TypeError, () => {
+ new PaymentRequest(defaultMethods, defaultDetails, {
+ shippingType: "invalid",
+ });
+ });
+}, "Shipping type should be valid");
+
+test(() => {
+ smokeTest();
+ const request = new PaymentRequest(defaultMethods, defaultDetails, {});
+ assert_equals(request.shippingAddress, null, "must be null");
+}, "PaymentRequest.shippingAddress must initially be null");
+
+test(() => {
+ smokeTest();
+ const request1 = new PaymentRequest(defaultMethods, defaultDetails, {});
+ assert_equals(request1.shippingType, null, "must be null");
+ const request2 = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: false,
+ });
+ assert_equals(request2.shippingType, null, "must be null");
+}, "If options.requestShipping is not set, then request.shippingType attribute is null.");
+
+test(() => {
+ smokeTest();
+ // option.shippingType defaults to 'shipping'
+ const request1 = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: true,
+ });
+ assert_equals(request1.shippingType, "shipping", "must be shipping");
+ const request2 = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: true,
+ shippingType: "delivery",
+ });
+ assert_equals(request2.shippingType, "delivery", "must be delivery");
+}, "If options.requestShipping is true, request.shippingType will be options.shippingType.");
+
</script>
diff --git a/tests/wpt/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html b/tests/wpt/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html
index c608608c7eb..b4ca2a0c40b 100644
--- a/tests/wpt/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html
+++ b/tests/wpt/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html
@@ -178,6 +178,63 @@ test(() => {
}
}, "Check and canonicalize invalid details.displayItems amount and rethrow RangeError.");
+// Process shipping options:
+test(() => {
+ assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw.");
+ const shippingOptions = [];
+ for (const validCurrency of wellFormedCurrencyCodes) {
+ const shippingOption = {
+ id: `test` + Math.random(),
+ label: "shipping option",
+ amount: { currency: validCurrency, value: "5.00" },
+ selected: !shippingOptions.length,
+ };
+ const details = {
+ total: defaultTotal,
+ shippingOptions: [shippingOption],
+ };
+ try {
+ new PaymentRequest(defaultMethods, details, { requestShipping: true });
+ } catch (err) {
+ assert_unreached(
+ `Unexpected exception with valid shippingOption currency code "${validCurrency}": ${err.message}.`
+ );
+ }
+ shippingOptions.push(shippingOption);
+ }
+ try {
+ const details = Object.assign({}, defaultDetails, { shippingOptions });
+ new PaymentRequest(defaultMethods, details, { requestShipping: true });
+ } catch (err) {
+ assert_unreached(
+ `Unexpected error with multiple valid shppingOptions: ${err.message}.`
+ );
+ }
+}, "Check and canonicalize valid details.shippingOptions amount.");
+
+test(() => {
+ assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw.");
+ for (const invalidCurrency of invalidCurrencyCodes) {
+ const shippingOption = {
+ id: "test",
+ label: "shipping option",
+ amount: { currency: invalidCurrency, value: "5.00" },
+ selected: true,
+ };
+ const details = {
+ total: defaultTotal,
+ shippingOptions: [shippingOption],
+ };
+ assert_throws_js(
+ RANGE_ERROR,
+ () => {
+ new PaymentRequest(defaultMethods, details, { requestShipping: true });
+ },
+ `Expected RangeError with invalid shippingOption currency code "${invalidCurrency}".`
+ );
+ }
+}, "Check and canonicalize invalid details.shippingOptions amount and rethrow RangeError.");
+
// Process payment details modifiers:
test(() => {
assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw.");
diff --git a/tests/wpt/tests/payment-request/payment-request-onshippingaddresschange-attribute.https.html b/tests/wpt/tests/payment-request/payment-request-onshippingaddresschange-attribute.https.html
new file mode 100644
index 00000000000..5b2538992f7
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-request-onshippingaddresschange-attribute.https.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for onshippingaddresschange attribute</title>
+<link rel="help" href="https://w3c.github.io/browser-payment-api/#onshippingaddresschange-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const basicCard = Object.freeze({ supportedMethods: "basic-card" });
+const defaultMethods = Object.freeze([basicCard, applePay]);
+const defaultDetails = Object.freeze({
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+});
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ assert_idl_attribute(request, "onshippingaddresschange");
+}, "Must have a onshippingaddresschange IDL attribute");
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ const ev = new Event("shippingaddresschange");
+ let didHandle = false;
+ request.onshippingaddresschange = evt => {
+ assert_equals(ev, evt, "must be same event");
+ didHandle = true;
+ };
+ request.dispatchEvent(ev);
+ assert_true(didHandle, "event did not fire");
+}, `onshippingaddresschange attribute is a generic handler for "shippingaddresschange"`);
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ const ev = new PaymentRequestUpdateEvent("shippingaddresschange");
+ let didHandle = false;
+ request.onshippingaddresschange = evt => {
+ assert_equals(ev, evt, "must be same event");
+ didHandle = true;
+ };
+ request.dispatchEvent(ev);
+ assert_true(didHandle, "event did not fire");
+}, `onshippingaddresschange attribute is a handler for PaymentRequestUpdateEvent`);
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ const ev = new PaymentRequestUpdateEvent("shippingaddresschange");
+ let didHandle = false;
+ let didListen = false;
+ request.onshippingaddresschange = evt => {
+ assert_equals(ev, evt, "must be same event");
+ didHandle = true;
+ };
+ request.addEventListener("shippingaddresschange", evt => {
+ assert_equals(ev, evt, "must be same event");
+ didListen = true;
+ });
+ request.dispatchEvent(ev);
+ assert_true(didHandle, "onshippingaddresschange did not receive the event");
+ assert_true(didListen, "addEventListener did not receive the event");
+}, `onshippingaddresschange attribute and listeners both work`);
+</script>
diff --git a/tests/wpt/tests/payment-request/payment-request-onshippingoptionchange-attribute.https.html b/tests/wpt/tests/payment-request/payment-request-onshippingoptionchange-attribute.https.html
new file mode 100644
index 00000000000..43ea5dcce87
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-request-onshippingoptionchange-attribute.https.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for onshippingoptionchange attribute</title>
+<link rel="help" href="https://w3c.github.io/browser-payment-api/#onshippingoptionchange-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const basicCard = Object.freeze({ supportedMethods: "basic-card" });
+const defaultMethods = Object.freeze([basicCard, applePay]);
+const defaultDetails = Object.freeze({
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+});
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ assert_idl_attribute(request, "onshippingoptionchange");
+}, "Must have a onshippingoptionchange IDL attribute");
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ const ev = new Event("shippingoptionchange");
+ let didHandle = false;
+ request.onshippingoptionchange = evt => {
+ assert_equals(ev, evt, "must be same event");
+ didHandle = true;
+ };
+ request.dispatchEvent(ev);
+ assert_true(didHandle, "event did not fire");
+}, `onshippingoptionchange attribute is a generic handler for "shippingoptionchange"`);
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ const ev = new PaymentRequestUpdateEvent("shippingoptionchange");
+ let didHandle = false;
+ request.onshippingoptionchange = evt => {
+ assert_equals(ev, evt, "must be same event");
+ didHandle = true;
+ };
+ request.dispatchEvent(ev);
+ assert_true(didHandle, "event did not fire");
+}, `onshippingoptionchange attribute is a handler for PaymentRequestUpdateEvent`);
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ const ev = new PaymentRequestUpdateEvent("shippingoptionchange");
+ let didHandle = false;
+ let didListen = false;
+ request.onshippingoptionchange = evt => {
+ assert_equals(ev, evt, "must be same event");
+ didHandle = true;
+ };
+ request.addEventListener("shippingoptionchange", evt => {
+ assert_equals(ev, evt, "must be same event");
+ didListen = true;
+ });
+ request.dispatchEvent(ev);
+ assert_true(didHandle, "onshippingoptionchange did not receive the event");
+ assert_true(didListen, "addEventListener did not receive the event");
+}, `onshippingoptionchange attribute and listeners both work`);
+
+</script>
diff --git a/tests/wpt/tests/payment-request/payment-request-shippingAddress-attribute.https.html b/tests/wpt/tests/payment-request/payment-request-shippingAddress-attribute.https.html
new file mode 100644
index 00000000000..08918356b6f
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-request-shippingAddress-attribute.https.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for PaymentRequest shippingAddress attribute</title>
+<link rel="help" href="https://w3c.github.io/payment-request/#shippingaddress-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const validMethod = Object.freeze({ supportedMethods: "foo" });
+const validMethods = Object.freeze([validMethod]);
+const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+});
+const validDetails = Object.freeze({ total: validTotal });
+
+test(() => {
+ const request = new PaymentRequest(validMethods, validDetails);
+ assert_idl_attribute(request, "shippingAddress");
+}, "Must have a .shippingAddress IDL attribute.");
+
+test(() => {
+ const request = new PaymentRequest(validMethods, validDetails);
+ assert_equals(request.shippingAddress, null, "expected null");
+}, ".shippingAddress attribute must default to null.");
+
+</script>
diff --git a/tests/wpt/tests/payment-request/payment-request-shippingOption-attribute.https.html b/tests/wpt/tests/payment-request/payment-request-shippingOption-attribute.https.html
new file mode 100644
index 00000000000..b5f9ea65c6c
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-request-shippingOption-attribute.https.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for PaymentRequest shippingOption attribute</title>
+<link rel="help" href="https://w3c.github.io/payment-request/#shippingoption-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+const validMethod = Object.freeze({ supportedMethods: "foo" });
+const validMethods = Object.freeze([validMethod]);
+const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+});
+const validDetails = Object.freeze({ total: validTotal });
+const validShippingOption = Object.freeze({
+ id: "valid",
+ label: "Valid shipping option",
+ amount: validAmount,
+ selected: false,
+});
+
+const requestShipping = Object.freeze({
+ requestShipping: true,
+});
+
+test(() => {
+ const request = new PaymentRequest(validMethods, validDetails);
+ assert_idl_attribute(request, "shippingOption");
+}, "Must have a .shippingOption IDL attribute.");
+
+test(() => {
+ const request = new PaymentRequest(validMethods, validDetails);
+ assert_equals(request.shippingOption, null, "expected null");
+}, ".shippingOption attribute must default to null.");
+
+test(() => {
+ const detailsWithShippingOptions = Object.assign({}, validDetails, {
+ shippingOptions: [validShippingOption],
+ });
+ const request = new PaymentRequest(
+ validMethods,
+ detailsWithShippingOptions,
+ requestShipping
+ );
+ assert_equals(request.shippingOption, null, "expected null");
+}, "If there is a single shipping option, but selected is false, then .shippingOption must be null.");
+
+test(() => {
+ const shippingOption2 = Object.assign({}, validShippingOption, {
+ id: "valid2",
+ });
+ const detailsWithShippingOptions = Object.assign({}, validDetails, {
+ shippingOptions: [validShippingOption, shippingOption2],
+ });
+ const request = new PaymentRequest(
+ validMethods,
+ detailsWithShippingOptions,
+ requestShipping
+ );
+ assert_equals(request.shippingOption, null, "expected null");
+}, "If there are multiple shipping options all with `selected` set to false, then .shippingOption is null.");
+
+test(() => {
+ const shippingOption2 = Object.assign({}, validShippingOption, {
+ id: "pass",
+ selected: true,
+ });
+ const detailsWithShippingOptions = Object.assign({}, validDetails, {
+ shippingOptions: [shippingOption2, validShippingOption],
+ });
+ const request = new PaymentRequest(
+ validMethods,
+ detailsWithShippingOptions,
+ requestShipping
+ );
+ assert_equals(request.shippingOption, "pass", "expected 'pass'");
+}, "Given multiple shipping options, it must use the selected shipping option for .shippingOption value.");
+
+test(() => {
+ const shippingOption1 = Object.assign({}, validShippingOption, {
+ id: "fail",
+ selected: true,
+ });
+ const shippingOption2 = Object.assign({}, validShippingOption, {
+ id: "pass",
+ selected: true,
+ });
+ const detailsWithShippingOptions = Object.assign({}, validDetails, {
+ shippingOptions: [shippingOption1, shippingOption2, validShippingOption],
+ });
+ const request = new PaymentRequest(
+ validMethods,
+ detailsWithShippingOptions,
+ requestShipping
+ );
+ assert_equals(request.shippingOption, "pass", "expected 'pass'");
+}, "If there are multiple of the shipping options with selected true, then .shippingOption is the last selected shipping option in order.");
+</script>
diff --git a/tests/wpt/tests/payment-request/payment-request-shippingType-attribute.https.html b/tests/wpt/tests/payment-request/payment-request-shippingType-attribute.https.html
new file mode 100644
index 00000000000..11f75b1c862
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-request-shippingType-attribute.https.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Mozilla and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for PaymentRequest's shippingType attribute</title>
+<link rel="help" href="https://w3c.github.io/payment-request/#shippingtype-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+const paymentShipingTypes = Object.freeze(["delivery", "pickup", "shipping"]);
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const basicCard = Object.freeze({ supportedMethods: "basic-card" });
+const defaultMethods = Object.freeze([basicCard, applePay]);
+const defaultDetails = Object.freeze({
+ total: {
+ label: "",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+});
+
+test(() => {
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ assert_idl_attribute(request, "shippingType");
+}, "Must have a shippingType IDL attribute");
+
+test(() => {
+ const request1 = new PaymentRequest(defaultMethods, defaultDetails, {});
+ assert_equals(request1.shippingType, null, "must be null");
+ const request2 = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: false,
+ });
+ assert_equals(request2.shippingType, null, "must be null");
+ for (const shippingType of paymentShipingTypes) {
+ const request = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: false,
+ shippingType,
+ });
+ assert_equals(request.shippingType, null, "must be null");
+ }
+}, "If options.requestShipping is false, then request.shippingType attribute is null.");
+
+test(() => {
+ // option.shippingType defaults to 'shipping'
+ const defaultRequest = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: true,
+ });
+ assert_equals(defaultRequest.shippingType, "shipping", "must be shipping");
+ for (const shippingType of paymentShipingTypes) {
+ const request = new PaymentRequest(defaultMethods, defaultDetails, {
+ requestShipping: true,
+ shippingType,
+ });
+ assert_equals(
+ request.shippingType,
+ shippingType,
+ `must be ${shippingType}`
+ );
+ }
+}, "If options.requestShipping is true, request.shippingType will be options.shippingType.");
+</script>
diff --git a/tests/wpt/tests/payment-request/payment-response/helpers.js b/tests/wpt/tests/payment-request/payment-response/helpers.js
index 1242ecb743c..3e4f5cfd36f 100644
--- a/tests/wpt/tests/payment-request/payment-response/helpers.js
+++ b/tests/wpt/tests/payment-request/payment-response/helpers.js
@@ -65,8 +65,30 @@ async function getPaymentRequestResponse(options, id) {
label: "Total due",
amount: { currency: "USD", value: "1.0" },
},
+ shippingOptions: [
+ {
+ id: "fail1",
+ label: "Fail option 1",
+ amount: { currency: "USD", value: "5.00" },
+ selected: false,
+ },
+ {
+ id: "pass",
+ label: "Pass option",
+ amount: { currency: "USD", value: "5.00" },
+ selected: true,
+ },
+ {
+ id: "fail2",
+ label: "Fail option 2",
+ amount: { currency: "USD", value: "5.00" },
+ selected: false,
+ },
+ ],
};
const request = new PaymentRequest(methods, details, options);
+ request.onshippingaddresschange = ev => ev.updateWith(details);
+ request.onshippingoptionchange = ev => ev.updateWith(details);
const response = await request.show();
return { request, response };
}
@@ -106,5 +128,23 @@ async function runManualTest(button, options, expected = {}, id = undefined) {
assert_equals(typeof response.details, "object", "Expected an object");
// Testing that this does not throw:
response.toJSON();
+ if (options && options.requestShipping) {
+ assert_equals(
+ response.shippingOption,
+ "pass",
+ "request.shippingOption must be 'pass'"
+ );
+ } else {
+ assert_equals(
+ request.shippingOption,
+ null,
+ "If requestShipping is falsy, request.shippingOption must be null"
+ );
+ assert_equals(
+ response.shippingOption,
+ null,
+ "request.shippingOption must be null"
+ );
+ }
}, button.textContent.trim());
}
diff --git a/tests/wpt/tests/payment-request/payment-response/retry-method-manual.https.html b/tests/wpt/tests/payment-request/payment-response/retry-method-manual.https.html
new file mode 100644
index 00000000000..a5aab49e387
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-response/retry-method-manual.https.html
@@ -0,0 +1,296 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-retry">
+<title>
+ PaymentResponse.prototype.retry() method
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helpers.js"></script>
+<script>
+test(() => {
+ assert_true(
+ "retry" in PaymentResponse.prototype,
+ "retry must be in prototype"
+ );
+ assert_true(
+ PaymentResponse.prototype.retry instanceof Function,
+ "retry must be a function"
+ );
+}, "PaymentResponse.prototype must have a retry() function (smoke test).");
+
+function checkCompletedCantRetry(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response } = await getPaymentRequestResponse();
+ // sets response.[[complete]] to true.
+ await response.complete("success");
+ return promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ response.retry(),
+ "response.[[complete]] is true, so rejects with InvalidStateError."
+ );
+ }, button.textContent.trim());
+}
+
+function repeatedCallsToRetry(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response } = await getPaymentRequestResponse();
+ const retryPromise = response.retry();
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ response.retry(),
+ "Calling retry() again rejects with an InvalidStateError"
+ );
+ await retryPromise;
+ await response.complete("success");
+ }, button.textContent.trim());
+}
+
+function callCompleteWhileRetrying(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response } = await getPaymentRequestResponse();
+ const retryPromise = response.retry();
+ const completePromise1 = response.complete("success");
+ const completePromise2 = response.complete("fail");
+ assert_not_equals(
+ completePromise1,
+ completePromise2,
+ "complete() must return unique promises"
+ );
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ completePromise1,
+ "Calling complete() while retrying rejects with an InvalidStateError"
+ );
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ completePromise2,
+ "Calling complete() while retrying rejects with an InvalidStateError"
+ );
+ assert_not_equals(
+ completePromise1,
+ completePromise2,
+ "complete() must return unique promises"
+ );
+ await retryPromise;
+ await response.complete("success");
+ }, button.textContent.trim());
+}
+
+function callingRequestAbortMustNotAbort(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response, request } = await getPaymentRequestResponse();
+ const retryPromise = response.retry();
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ request.abort(),
+ "Calling request.abort() while retrying rejects with an InvalidStateError"
+ );
+ await retryPromise;
+ await response.complete("success");
+ }, button.textContent.trim());
+}
+
+function canRetryMultipleTimes(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response } = await getPaymentRequestResponse();
+ assert_equals(
+ await response.retry(),
+ undefined,
+ "Expected undefined as the resolve value"
+ );
+ assert_equals(
+ await response.retry(),
+ undefined,
+ "Expected undefined as the resolve value"
+ );
+ await response.complete("success");
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ response.retry(),
+ "Calling retry() after complete() rejects with a InvalidStateError"
+ );
+ }, button.textContent.trim());
+}
+
+function userCanAbortARetry(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response } = await getPaymentRequestResponse();
+ await promise_rejects_dom(
+ t,
+ "AbortError",
+ response.retry(),
+ "The user aborting a retry rejects with a AbortError"
+ );
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ response.retry(),
+ "After the user aborts, response [[complete]] is true so retry() must reject with InvalidStateError"
+ );
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ response.complete("success"),
+ "After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
+ );
+ }, button.textContent.trim());
+}
+
+function userIsShownErrorsFields(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response, request } = await getPaymentRequestResponse({ requestShipping: true });
+ const retryPromise = response.retry({
+ shippingAddress: { city: "Invalid city", addressLine: "Invalid address line" },
+ });
+ await retryPromise;
+ await response.complete("success");
+ }, button.textContent.trim());
+}
+
+function abortTheUpdate(button) {
+ button.disabled = true;
+ promise_test(async t => {
+ const { response, request } = await getPaymentRequestResponse({
+ requestShipping: true,
+ });
+ const shipOptionChangePromise = new Promise(resolve => {
+ request.onshippingoptionchange = event => {
+ // causes "abort the update" to run
+ event.updateWith({ total: "error!" });
+ resolve();
+ };
+ });
+ const retryPromise = response.retry();
+ await shipOptionChangePromise;
+ await promise_rejects_js(
+ t,
+ TypeError,
+ retryPromise,
+ "retry() aborts with a TypeError, because totals' value is invalid"
+ );
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ response.complete("success"),
+ "After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError"
+ );
+ }, button.textContent.trim());
+}
+
+function callingRetryReturnsUniquePromise(button){
+ button.disabled = true;
+ promise_test(async t => {
+ const { response } = await getPaymentRequestResponse();
+ const retryPromise = response.retry();
+ const promises = new Set([
+ retryPromise,
+ response.retry(),
+ response.retry(),
+ ]);
+ assert_equals(promises.size, 3, "Must have three unique objects");
+ await retryPromise;
+ await response.complete();
+ }, button.textContent.trim());
+};
+
+
+</script>
+<h2>
+ Manual Tests for PaymentResponse.retry() - Please run in order!
+</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When presented with the payment sheet, use any credit card select to "Pay" multiple times.
+</p>
+<ol>
+ <li>
+ <button onclick="checkCompletedCantRetry(this);">
+ A completed payment request cannot be retried.
+ </button>
+ </li>
+ <li>
+ <button onclick="repeatedCallsToRetry(this);">
+ Calling retry() more than once yield a rejected promise, but the
+ retryPromise resolves independently.
+ </button>
+ </li>
+ <li>
+ <button onclick="callCompleteWhileRetrying(this);">
+ Calling complete() while a retry() is in progress results in an InvalidStateError.
+ </button>
+ </li>
+ <li>
+ <button onclick="callingRequestAbortMustNotAbort(this);">
+ Trying to abort() via PaymentRequest is not possible.
+ </button>
+ </li>
+ <li>
+ <button onclick="canRetryMultipleTimes(this);">
+ It's possible to retry() multiple times and eventually complete().
+ After complete(), however, retry() rejects with an InvalidStateError.
+ </button>
+ </li>
+ <li>
+ <p>
+ When shown the payment sheet, hit pay once, then abort retrying the payment.
+ </p>
+ <button onclick="userCanAbortARetry(this);">
+ The user aborting retrying a payment causes the retryPromise to reject with AbortError.
+ Aborting a payment is causes it complete.
+ </button>
+ </li>
+ <li>
+ <p>
+ When shown the payment sheet, hit pay once. Check payment sheet for error fields.
+ Then hit escape or otherwise abort the payment.
+ </p>
+ <button onclick="userIsShownErrorsFields(this);">
+ When retrying, the user is shown error fields to fix.
+ </button>
+ </li>
+ <li>
+ <p>
+ When shown the payment sheet, hit pay once.
+ Then, change the shipping option.
+ Select to pay again.
+ </p>
+ <button onclick="abortTheUpdate(this);">
+ When "abort the update" occurs because of an update error,
+ the `retryPromise` is rejected and response.[[complete]] becomes true.
+ </button>
+ </li>
+ <li>
+ <p>
+ When shown the payment sheet, hit pay once. Then retry once.
+ </p>
+ <button onclick="callingRetryReturnsUniquePromise(this);">
+ Calling retry() multiple times is always a new object.
+ </button>
+ </li>
+ <li>
+ <button onclick="done();">
+ Done!
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">owners</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/payment-response/retry-method-warnings-manual.https.html b/tests/wpt/tests/payment-request/payment-response/retry-method-warnings-manual.https.html
new file mode 100644
index 00000000000..b68bf183097
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-response/retry-method-warnings-manual.https.html
@@ -0,0 +1,158 @@
+<!DOCTYPE html> <meta charset="utf-8" />
+<title>Warn when errorFields don't match request[[options]]</title>
+<link rel="help" href="https://github.com/w3c/payment-request/pull/807" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helpers.js"></script>
+<script>
+ test(() => {
+ assert_true(
+ "retry" in PaymentResponse.prototype,
+ "retry must be in prototype"
+ );
+ assert_true(
+ PaymentResponse.prototype.retry instanceof Function,
+ "retry must be a function"
+ );
+ }, "PaymentResponse.prototype must have a retry() function (smoke test).");
+
+ const defaultOptions = {
+ requestPayerName: false,
+ requestPayerEmail: false,
+ requestPayerPhone: false,
+ requestShipping: false,
+ };
+ function testShowWarning(button, errorFields) {
+ button.disabled = true;
+ promise_test(async () => {
+ const { response } = await getPaymentRequestResponse(defaultOptions);
+ await response.retry(errorFields);
+ await response.complete();
+ });
+ }
+</script>
+<h2>Manual Tests - Please run in order!</h2>
+<p>
+ Please open the developer console. Each of the tests below should generate a
+ warning in the developer console.
+</p>
+<p>When presented with the payment sheet, hit pay twice.</p>
+<ol>
+ <li>
+ <button onclick="testShowWarning(this, {payer: {name: 'Dont show this'}});">
+ Show warning if `requestPayerName` if false, and `errorFields.payer.name` is
+ present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {payer: {email: 'Dont show this'}});"
+ >
+ Show warning if `requestPayerEmail` if false, and `errorFields.payer.email`
+ is present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {payer: {phone: 'Dont show this'}});"
+ >
+ Show warning if `requestPayerPhone` if false, and `errorFields.payer.phone`
+ is present.
+ </button>
+ </li>
+ <li>
+ <button onclick="testShowWarning(this, {shippingAddress: {}});">
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {addressLine: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.addressLine` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {city: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.city` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {country: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.country` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {dependentLocality: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.dependentLocality` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {organization: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.organization` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {phone: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.phone` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {postalCode: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.postalCode` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {recipient: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.recipient` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {region: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.region` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {regionCode: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.regionCode` member present.
+ </button>
+ </li>
+ <li>
+ <button
+ onclick="testShowWarning(this, {shippingAddress: {sortingCode: 'Dont show this'}});"
+ >
+ Show warning if `requestShipping` if false, and
+ `errorFields.shippingAddress.sortingCode` member present.
+ </button>
+ </li>
+ <li><button onclick="done()">Done!</button></li>
+</ol>
diff --git a/tests/wpt/tests/payment-request/payment-response/shippingAddress-attribute-manual.https.html b/tests/wpt/tests/payment-request/payment-response/shippingAddress-attribute-manual.https.html
new file mode 100644
index 00000000000..f9f0a6e4faf
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-response/shippingAddress-attribute-manual.https.html
@@ -0,0 +1,101 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-shippingaddress">
+<title>
+ PaymentResponse.prototype.shippingAddress
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helpers.js"></script>
+<script>
+async function checkNullShippingAddress(button, options) {
+ button.disabled = true;
+ const { request, response } = await getPaymentRequestResponse(options);
+ await response.complete();
+ test(() => {
+ assert_idl_attribute(response, "shippingAddress");
+ assert_equals(
+ response.shippingAddress,
+ null,
+ "Expected response.shippingAddress to be null"
+ );
+ assert_equals(
+ request.shippingAddress,
+ null,
+ "Expected request.shippingAddress to be null"
+ );
+ }, button.textContent.trim());
+}
+
+async function runManualTest(button, options = {}, expected = {}, id) {
+ button.disabled = true;
+ const { request, response } = await getPaymentRequestResponse(options, id);
+ await response.complete();
+ test(() => {
+ assert_equals(response.requestId, request.id, `Expected ids to match`);
+ assert_idl_attribute(response, "shippingAddress");
+ const { shippingAddress: addr } = request;
+ assert_true(
+ addr instanceof ContactAddress,
+ "Expect instance of ContactAddress"
+ );
+ try {
+ addr.toJSON();
+ } catch (err) {
+ assert_unreached(
+ `Unexpected exception calling ContactAddress.toJSON(): ${err}`
+ );
+ }
+ if (expected.shippingAddress === null) {
+ return;
+ }
+ assert_equals(addr.country, "AF", "Expected AF for country");
+ assert_true(
+ addr.addressLine instanceof Array,
+ "Expected addressLine to be an array"
+ );
+ assert_equals(
+ addr.addressLine.toString(),
+ "1 wpt street",
+ "Expected '1 wpt street' for addressLine"
+ );
+ assert_equals(addr.city, "Kabul", "Expected city to be Kabul");
+ assert_equals(addr.postalCode, "1001", "Expect postalCode to be 1001");
+ assert_equals(addr.recipient, "web platform test");
+ }, button.textContent.trim());
+}
+</script>
+<h2>shippingAddress attribute</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When prompted, please enter "web platform test" as recipient, at address "1 wpt street" in "Kabul, Afghanistan", zip/postal code 1001.
+</p>
+<ol>
+ <li>
+ <button onclick="checkNullShippingAddress(this, undefined)">
+ If options is undefined, then shippingAddress must be null.
+ </button>
+ </li>
+ <li>
+ <button onclick="checkNullShippingAddress(this, {})">
+ If the requestShipping member is missing, then shippingAddress must be null.
+ </button>
+ </li>
+ <li>
+ <button onclick="checkNullShippingAddress(this, {requestShipping: false})">
+ If the requestShipping member is false, then shippingAddress must be null.
+ </button>
+ </li>
+ <li>
+ <button onclick="runManualTest(this, {requestShipping: true}).then(done)">
+ If the requestShipping member is true, then shippingAddress must be "1 wpt street" in "Kabul, Afghanistan", zip code 1001.
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/payment-response/shippingOption-attribute-manual.https.html b/tests/wpt/tests/payment-request/payment-response/shippingOption-attribute-manual.https.html
new file mode 100644
index 00000000000..687d3a52de9
--- /dev/null
+++ b/tests/wpt/tests/payment-request/payment-response/shippingOption-attribute-manual.https.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-shippingoption">
+<title>
+ PaymentResponse.prototype.complete() method
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="helpers.js"></script>
+<h2>shippingOption attribute</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ For the last test, please select the only available shipping option and select "Pay".
+</p>
+<ol>
+ <li>
+ <button onclick="runManualTest(this, undefined, { shippingOption: null })">
+ If the options is undefined, then shippingOption must be null.
+ </button>
+ </li>
+ <li>
+ <button onclick="runManualTest(this, { requestShipping: undefined }, { shippingOption: null })">
+ If the requestShipping member is missing, then shippingOption must be null.
+ </button>
+ </li>
+ <li>
+ <button onclick="runManualTest(this, { requestShipping: false }, { shippingOption: null })">
+ If the requestShipping member is false, then shippingOption must be null.
+ </button>
+ </li>
+ <li>
+ <button onclick="runManualTest(this, { requestShipping: true }, { shippingOption: 'pass' }).then(done)">
+ If the requestShipping member is true, then shippingOption must be the id of the selected shipping option ("pass").
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/shipping-address-changed-manual.https.html b/tests/wpt/tests/payment-request/shipping-address-changed-manual.https.html
new file mode 100644
index 00000000000..aad57cd724c
--- /dev/null
+++ b/tests/wpt/tests/payment-request/shipping-address-changed-manual.https.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). -->
+<meta charset="utf-8">
+<title>Test for PaymentRequest shippingAddress attribute</title>
+<link rel="help" href="https://w3c.github.io/payment-request/#shippingaddress-attribute">
+<link rel="help" href="https://w3c.github.io/payment-request/#onshippingaddresschange-attribute">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const validMethod = Object.freeze({ supportedMethods: "basic-card" });
+const validMethods = Object.freeze([validMethod, applePay]);
+const validAmount = Object.freeze({ currency: "USD", value: "5.00" });
+const validTotal = Object.freeze({
+ label: "label",
+ amount: validAmount,
+});
+const validShippingOption = Object.freeze({
+ id: "valid",
+ label: "Shipping Option",
+ amount: validAmount,
+ selected: false,
+});
+const validDetails = Object.freeze({
+ total: validTotal,
+ shippingOptions: [validShippingOption],
+});
+const requestShipping = Object.freeze({
+ requestShipping: true,
+});
+
+function testShippingAddressChange() {
+ promise_test(async t => {
+ const request = new PaymentRequest(
+ validMethods,
+ validDetails,
+ requestShipping
+ );
+ assert_equals(
+ request.shippingAddress,
+ null,
+ "request.shippingAddress must initially be null"
+ );
+ const listenerPromise = new Promise(resolve => {
+ request.addEventListener("shippingaddresschange", () => {
+ resolve(request.shippingAddress);
+ });
+ });
+ const handlerPromise = new Promise(resolve => {
+ request.onshippingaddresschange = () => {
+ resolve(request.shippingAddress);
+ };
+ });
+ request.show().catch(err => err);
+ const results = await Promise.all([listenerPromise, handlerPromise]);
+ results.forEach(obj => {
+ assert_true(obj instanceof ContactAddress,
+ "Expected instance of ContactAddress");
+ assert_equals(obj.organization, "", "organization should be redacted");
+ assert_equals(obj.phone, "", "phone should be redacted");
+ assert_equals(obj.recipient, "", "recipient should be redacted");
+ assert_equals(obj.addressLine.length, 0, "addressLine should be redacted");
+ });
+ await request.abort();
+ });
+ done();
+}
+
+</script>
+
+<h2>PaymentRequest shippingAddress attribute</h2>
+<p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+</p>
+<p>
+ When the payment sheet is presented, enter or select a shipping address.
+</p>
+<ol>
+ <li>
+ <button onclick="testShippingAddressChange()">
+ When the shipping address is manually changed, request.shippingAddress is a ContactAddress.
+ </button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/show-method-optional-promise-rejects.https.html b/tests/wpt/tests/payment-request/show-method-optional-promise-rejects.https.html
index 4a41f28fc9b..3b429655039 100644
--- a/tests/wpt/tests/payment-request/show-method-optional-promise-rejects.https.html
+++ b/tests/wpt/tests/payment-request/show-method-optional-promise-rejects.https.html
@@ -84,6 +84,10 @@
total: invalidNegativeTotal,
});
+ // PaymentOptions
+ const validOptions = Object.freeze({
+ requestShipping: true,
+ });
// PaymentItem
const validPaymentItem = Object.freeze({
@@ -100,6 +104,24 @@
const validPaymentItems = Object.freeze([validPaymentItem]);
const invalidPaymentItems = Object.freeze([invalidPaymentItem]);
+ // PaymentShippingOption
+ const invalidShippingOption = Object.freeze({
+ id: "abc",
+ label: "Invalid shipping option",
+ amount: invalidAmount,
+ selected: true,
+ });
+
+ // PaymentShippingOptions
+ const validShippingOption = Object.freeze({
+ id: "abc",
+ label: "valid shipping option",
+ amount: validAmount,
+ });
+
+ const validShippingOptions = Object.freeze([validShippingOption]);
+ const invalidShippingOptions = Object.freeze([invalidShippingOption]);
+
// PaymentDetailsModifier
const validModifier = Object.freeze({
additionalDisplayItems: validPaymentItems,
@@ -144,7 +166,8 @@
promise_test(async (t) => {
const request = new PaymentRequest(
validMethods,
- validDetails
+ validDetails,
+ validOptions
);
await test_driver.bless("Payment request");
const detailsPromise = Promise.resolve(badDetails);
@@ -194,6 +217,21 @@
);
testBadUpdate(
+ "Updating with duplicate shippingOptions (same IDs) results in a TypeError.",
+ {
+ ...validDetails,
+ shippingOptions: [validShippingOption, validShippingOption],
+ },
+ TypeError
+ );
+
+ testBadUpdate(
+ "Updating with a shippingOption with an invalid currency value results in a RangError.",
+ { ...validDetails, shippingOptions: invalidShippingOptions },
+ RangeError
+ );
+
+ testBadUpdate(
"Must throw a RangeError when a modifier's total item has an invalid currency.",
{ ...validDetails, modifiers: [modifierWithInvalidTotal, validModifier] },
RangeError
diff --git a/tests/wpt/tests/payment-request/show-method-optional-promise-resolves-manual.https.html b/tests/wpt/tests/payment-request/show-method-optional-promise-resolves-manual.https.html
new file mode 100644
index 00000000000..5360a9704af
--- /dev/null
+++ b/tests/wpt/tests/payment-request/show-method-optional-promise-resolves-manual.https.html
@@ -0,0 +1,339 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for PaymentRequest.show(optional promise) method</title>
+<link rel="help" href="https://w3c.github.io/browser-payment-api/#show-method">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+setup({
+ allow_uncaught_exception: true,
+ explicit_done: true,
+ explicit_timeout: true,
+});
+
+// DATA USED BY TESTS
+// PaymentMethods
+const validMethods = Object.freeze([
+ {
+ supportedMethods: "valid-but-wont-ever-match",
+ },
+ {
+ supportedMethods: "basic-card",
+ },
+ {
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+ },
+]);
+
+// Amounts
+const failAmount = Object.freeze({
+ currency: "USD",
+ value: "1.00",
+});
+const passAmount = Object.freeze({
+ currency: "CAD",
+ value: "50.00",
+});
+const neutralAmount = Object.freeze({
+ currency: "AUD",
+ value: "0.00",
+});
+
+// Labels
+const failLabel = "💥 TEST HAS FAILED 💥";
+const passLabel = "✅ TEST HAS PASSED ✅";
+const neutralLabel = "Ignore this label";
+// Totals
+const failTotal = Object.freeze({
+ label: failLabel,
+ amount: failAmount,
+});
+const passTotal = Object.freeze({
+ label: passLabel,
+ amount: passAmount,
+});
+const neutralTotal = Object.freeze({
+ label: neutralLabel,
+ amount: passAmount,
+});
+
+// PaymentItem
+const failPaymentItem = Object.freeze({
+ amount: failAmount,
+ label: failLabel,
+});
+const failPaymentItems = Object.freeze([failPaymentItem]);
+
+const passPaymentItem = Object.freeze({
+ amount: passAmount,
+ label: passLabel,
+});
+const passPaymentItems = Object.freeze([passPaymentItem]);
+
+// PaymentShippingOptions
+const failShippingOption = Object.freeze({
+ id: "fail",
+ label: failLabel,
+ amount: failAmount,
+});
+const failShippingOptions = Object.freeze([failShippingOption]);
+
+const neutralShippingOption = Object.freeze({
+ id: "neutral",
+ label: neutralLabel,
+ amount: neutralAmount,
+});
+
+const updatedShippingOption1 = Object.freeze({
+ id: "updated-1",
+ label: `${passLabel} - option 1`,
+ amount: passAmount,
+});
+const updatedShippingOption2 = Object.freeze({
+ id: "updated-2",
+ label: `${passLabel} - option 2 (MUST BE SELECTED!)`,
+ amount: passAmount,
+ selected: true,
+});
+const passShippingOptions = Object.freeze([
+ updatedShippingOption1,
+ updatedShippingOption2,
+]);
+
+// Modifiers
+// create a modifier objects for each validMethods
+// and single additional display item
+const failModifiers = validMethods.map(modifier => {
+ const label = `${failLabel} - (${modifier.supportedMethods})`;
+ return {
+ ...modifier,
+ total: {
+ ...failTotal,
+ label,
+ },
+ additionalDisplayItems: [
+ {
+ ...failPaymentItem,
+ label,
+ },
+ ],
+ };
+});
+// Updates the total for each, and changes the additionalDisplayItems
+const passModifiers = failModifiers.map(modifier => {
+ const label = `${passLabel} - (${modifier.supportedMethods})`;
+ return {
+ ...modifier,
+ total: {
+ ...passTotal,
+ label,
+ },
+ additionalDisplayItems: [
+ {
+ ...passPaymentItem,
+ label,
+ },
+ ],
+ };
+});
+
+// PaymentDetailsInit
+const failDetails = Object.freeze({
+ displayItems: failPaymentItems,
+ id: "this cannot be changed",
+ modifiers: failModifiers,
+ shippingOptions: failShippingOptions,
+ total: failTotal,
+});
+
+const neutralDetails = Object.freeze({
+ displayItems: [],
+ modifiers: [],
+ shippingOptions: [neutralShippingOption],
+ total: neutralTotal,
+});
+
+function smokeTest() {
+ promise_test(async t => {
+ const request = new PaymentRequest(validMethods, failDetails);
+ await promise_rejects_js(
+ t,
+ TypeError,
+ request.show({
+ total: "This throws a TypeError",
+ }),
+ "expected TypeError"
+ );
+ }, "smoke test - checks if the optional details are supported on show() method");
+}
+
+function runUpdateDetailsAlgorithm(
+ buttonElement,
+ details,
+ options = {
+ requestShipping: true,
+ },
+ initialDetails = failDetails,
+) {
+ const testAssertion = buttonElement.textContent.trim();
+ buttonElement.disabled = true;
+ promise_test(async t => {
+ const request = new PaymentRequest(validMethods, initialDetails, options);
+ const detailsPromise = Promise.resolve(details);
+ const acceptPromise = request.show(detailsPromise);
+ assert_equals(request.id, "this cannot be changed", "id must never change.");
+ await promise_rejects_dom(
+ t,
+ "AbortError",
+ acceptPromise,
+ "expected AbortError"
+ );
+ }, testAssertion);
+}
+</script>
+<h2>
+ PaymentRequest <code>.show(optional detailsPromise)</code> tests
+</h2>
+<p>
+ These test cause <code>detailsPromise</code> to resolve successfully with some updated value. As such, that will cause
+ something in the payment sheet to change. Each test describes what is expected to change - if anything.
+</p>
+<p>
+ <strong>Instructions:</strong> Click on each button in sequence from top to bottom without refreshing the page. The payment
+ sheet will be shown. If required, confirm that the expected value appears in the payment sheet. Finally, manually abort/cancel
+ the payment request by closing the payment sheet.
+</p>
+<ol>
+ <li><button onclick="smokeTest()">If the payment sheet is shown, the test has failed.</button></li>
+ <li><button onclick="
+ const details = {
+ ...neutralDetails,
+ id: 'fail',
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, the provided `id` must have no effect on the payment request.
+ </button></li>
+ <li><button onclick="
+ const details = {
+ ...neutralDetails,
+ total: passTotal
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, the total must be CAD$50 with the label "✅ TEST HAS PASSED ✅".
+ </button></li>
+ <li><button onclick="
+ const details = {
+ ...neutralDetails,
+ displayItems: passPaymentItems,
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, there must be a one display item with a value of CAD$50 with the label "✅ TEST HAS PASSED ✅".
+ </button>
+ </li>
+ <li><button onclick="
+ const auItem = {
+ ...passPaymentItem,
+ amount: { value: '40', currency: 'AUD'},
+ pending: true
+ }
+ const details = {
+ ...neutralDetails,
+ displayItems: passPaymentItems.concat(auItem),
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, there must be
+ two display items: One with a value of CAD$50, another with
+ value AUD$40 that is pending.
+ </button>
+ </li>
+ <li><button onclick="
+ const details = {
+ ...neutralDetails,
+ shippingOptions: [updatedShippingOption1],
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, there must be a one shipping option
+ with a value of CAD$50.
+ </button>
+ </li>
+ <li><button onclick="
+ const details = {
+ ...neutralDetails,
+ shippingOptions: passShippingOptions,
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, there must be
+ two shipping options: One with a value of CAD$50, another with
+ value AUD$40 that is selected.
+ </button>
+ </li>
+ <li><button onclick="
+ const details = {
+ ...neutralDetails,
+ modifiers: passModifiers,
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, the total should be CAD$50.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const details = {
+ ...neutralDetails,
+ shippingOptions: [],
+ error: passLabel,
+ };
+ runUpdateDetailsAlgorithm(this, details);
+ ">
+ When the payment sheet is shown, the string "✅ TEST HAS PASSED ✅" should be shown
+ somewhere in the user interface. Alternatively, the payment sheet must indicate to the
+ end user that it's not possible to ship their order.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const details = {
+ ...neutralDetails,
+ error: failLabel,
+ };
+ runUpdateDetailsAlgorithm(this, details, {requestShipping: false});
+ ">
+ When the payment sheet is shown, there should not be any errors shown.
+ </button>
+ </li>
+ <li>
+ <button onclick="
+ const initialDetails = {total: passTotal, id: 'this cannot be changed'};
+ const updatedDetails = {};
+ runUpdateDetailsAlgorithm(
+ this, updatedDetails, {requestShipping: false}, initialDetails);
+ ">
+ Resolving the show promise with empty details will preserve the details from
+ the constructor. When the payment sheet is shown, the string
+ "✅ TEST HAS PASSED ✅" should be shown.
+ </button>
+ </li>
+ <li>
+ <button onclick="done();">Done!</button>
+ </li>
+</ol>
+
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/updateWith-method-pmi-handling-manual.https.html b/tests/wpt/tests/payment-request/updateWith-method-pmi-handling-manual.https.html
new file mode 100644
index 00000000000..1a52978ca3e
--- /dev/null
+++ b/tests/wpt/tests/payment-request/updateWith-method-pmi-handling-manual.https.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for validity of payment method identifiers when calling updateWith() method</title>
+<link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+"use strict";
+setup({
+ explicit_done: true,
+ explicit_timeout: true,
+ allow_uncaught_exception: true,
+});
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ },
+});
+const validMethod = Object.freeze({
+ supportedMethods: "https://:@wpt.fyi:443/payment-request",
+});
+
+const validMethods = Object.freeze([
+ validMethod,
+ applePay,
+ { supportedMethods: "basic-card" },
+]);
+
+const validAmount = Object.freeze({
+ currency: "USD",
+ value: "1.0",
+});
+
+const validTotal = Object.freeze({
+ label: "Default Total",
+ amount: validAmount,
+});
+
+const validShippingOption = Object.freeze({
+ id: "standard",
+ label: "Shipping option",
+ amount: validAmount,
+ selected: true,
+});
+
+const validDetails = Object.freeze({
+ total: validTotal,
+ shippingOptions: [validShippingOption],
+});
+
+const validModifier = Object.freeze({
+ supportedMethods: "basic-card",
+ total: validTotal,
+});
+
+function manualTest(button, { invalidMethod }) {
+ button.disabled = true;
+ promise_test(async t => {
+ const request = new PaymentRequest(validMethods, validDetails, {
+ requestShipping: true,
+ });
+ const listener = ev => {
+ const invalidModifier = Object.assign({}, validModifier, {
+ supportedMethods: invalidMethod,
+ });
+ const invalidDetails = Object.assign({}, validDetails, {
+ modifiers: [validModifier, invalidModifier],
+ });
+ ev.updateWith(invalidDetails);
+ };
+ // We test against a valid and an invalid modifier
+ request.addEventListener("shippingaddresschange", listener, { once: true });
+ const showPromise = request.show();
+ await promise_rejects_js(t, RangeError, showPromise);
+ }, button.textContent.trim());
+}
+</script>
+<h2>updateWith() method: test validity of payment method identifiers.</h2>
+<p>
+ When shown a payment sheet, select a different address.
+</p>
+<ol>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'https://:password@example.com'});">
+ Must throw if the URL has a password.
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'https://username@example.com'});">
+ Must throw if the URL has a username.
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'https://username:password@example.com/pay'});">
+ Must throw if the URL has a username and a password.
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'http://username:password@example.com/pay'});">
+ Must throw if it's http, and has a username and password.
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'http://foo.com:100000000/pay'});">
+ Must throw if the URL is invalid (port range).
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'basic-💳'});">
+ Must throw if the PMI contains characters that are out of range.
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'not-https://wpt.fyi/payment-request'});">
+ Must throw if not https.
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: '¡basic-*-card!'});">
+ Must throw if the standardized PMI contains characters outside the ascii range.
+ </button>
+ </li>
+ <li>
+ <button onclick="manualTest(this, {invalidMethod: 'Basic-Card'});">
+ Must throw if standardized PMI has uppercase characters.
+ </button>
+ </li>
+ <li>
+ <button onclick="done();">Done!</button>
+ </li>
+</ol>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/payment-request/user-accepts-payment-request-algo-manual.https.html b/tests/wpt/tests/payment-request/user-accepts-payment-request-algo-manual.https.html
new file mode 100644
index 00000000000..300f04811fd
--- /dev/null
+++ b/tests/wpt/tests/payment-request/user-accepts-payment-request-algo-manual.https.html
@@ -0,0 +1,230 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#user-accepts-the-payment-request-algorithm">
+<title>
+ User accepts the payment request algorithm
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({ explicit_done: true, explicit_timeout: true });
+const applePay = Object.freeze({
+ supportedMethods: "https://apple.com/apple-pay",
+ data: {
+ version: 3,
+ merchantIdentifier: "merchant.com.example",
+ countryCode: "US",
+ merchantCapabilities: ["supports3DS"],
+ supportedNetworks: ["visa"],
+ }
+});
+const basicCardMethod = Object.freeze({
+ supportedMethods: "basic-card",
+});
+const validMethod = Object.freeze({
+ supportedMethods: "this-is-just-for-testings-will-never-match",
+});
+const methods = Object.freeze([basicCardMethod, validMethod, applePay]);
+const validAmount = Object.freeze({
+ currency: "USD",
+ value: "5.00",
+});
+const shippingOptions = [
+ Object.freeze({
+ id: "option1",
+ label: "Option 1",
+ amount: validAmount,
+ selected: false,
+ }),
+ Object.freeze({
+ id: "option 2",
+ label: "Option 2",
+ amount: validAmount,
+ selected: true,
+ }),
+];
+
+const detailsNoShippingOptions = Object.freeze({
+ total: {
+ label: "Total due",
+ amount: validAmount,
+ },
+});
+
+const detailsWithShippingOptions = Object.assign({}, detailsNoShippingOptions, {
+ shippingOptions,
+});
+
+const optionsRequestNothing = Object.freeze({
+ requestShipping: false,
+ requestPayerEmail: false,
+ requestPayerName: false,
+ requestPayerPhone: false,
+});
+
+const optionsRequestEverything = Object.freeze({
+ requestShipping: true,
+ requestPayerEmail: true,
+ requestPayerName: true,
+ requestPayerPhone: true,
+});
+
+test(() => {
+ // smoke test
+ try {
+ new PaymentRequest(methods, detailsNoShippingOptions);
+ } catch (err) {
+ done();
+ throw err;
+ }
+}, "Must be able to construct a payment request (smoke test)");
+
+function testAcceptRequestAlgorithm(
+ button,
+ details,
+ options = {},
+ expectedResponse = {}
+) {
+ button.disabled = true;
+ promise_test(async t => {
+ const request = new PaymentRequest(methods, details, options);
+ const response = await request.show();
+ assert_true(
+ response instanceof PaymentResponse,
+ "Expected an instance of PaymentResponse."
+ );
+ // Response [[calledComplete]] is false, so this shouldn't throw
+ await response.complete("success");
+ // For good measure, test that subsequent complete()
+ for (const state of [undefined, "success", "fail", "unknown"]) {
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ response.complete(state),
+ "Response [[calledComplete]] is true, so InvalidStateError"
+ );
+ }
+ assert_equals(
+ request.id,
+ response.requestId,
+ "Request and response ids must match."
+ );
+ assert_true(response.details instanceof Object, "Expected an object");
+ assert_equals(
+ response.shippingAddress,
+ request.shippingAddress,
+ "Request and response must reference same shippingAddress (or both null)."
+ );
+ assert_equals(
+ response.shippingOption,
+ request.shippingOption,
+ "Request and response must be the same value (or both null)."
+ );
+ if (options.requestShipping === true) {
+ assert_true(
+ response.shippingAddress instanceof ContactAddress,
+ "Expected an instance of ContactAddress."
+ );
+ }
+ const expected = {
+ methodName: "basic-card",
+ payerEmail: options.requestPayerEmail
+ ? expectedResponse.payerEmail
+ : null,
+ payerName: options.requestPayerName ? expectedResponse.payerName : null,
+ payerPhone: options.requestPayerPhone
+ ? expectedResponse.payerPhone
+ : null,
+ };
+ for (const [attr, expectedValue] of Object.entries(expected)) {
+ assert_equals(
+ response[attr],
+ expectedValue,
+ `response.${attr} must be ${expectedValue}`
+ );
+ }
+ await promise_rejects_dom(
+ t,
+ "InvalidStateError",
+ request.show(),
+ "Request [[state]] is closed, so InvalidStateError"
+ );
+ }, button.textContent.trim());
+}
+
+</script>
+
+<section>
+ <h2 id="user-accepts-payment-request">User accepts payment request</h2>
+ <p>
+ Click on each button in sequence from top to bottom without refreshing the page.
+ Each button will bring up the Payment Request UI window.
+ </p>
+ <p>
+ When shown the payment sheet, please input a credit card and select Pay.
+ </p>
+ <ol>
+ <li>
+ <button onclick="
+ const detailsWithId = Object.assign({}, detailsNoShippingOptions, { id: 'pass' });
+ testAcceptRequestAlgorithm(this, detailsWithId, optionsRequestNothing);">
+ User accepts payment request, but not shipping is requested.
+ </button> Use any credit card to pay.
+ </li>
+ <li>
+ <button onclick="
+ const requestShipping = Object.assign({}, optionsRequestNothing, {requestShipping: true});
+ const expectedValues = { shippingOption: 'option 2' };
+ testAcceptRequestAlgorithm(this, detailsWithShippingOptions, requestShipping, expectedValues);">
+ User accepts payment request, merchant requests shipping.
+ </button> Select any shipping option, and use any credit card to pay.
+ </li>
+ <li>
+ <button onclick="
+ const requestPayerEmail = Object.assign({}, optionsRequestNothing, {requestPayerEmail: true});
+ const expectValues = { payerEmail: 'wpt@w3.org' };
+ testAcceptRequestAlgorithm(this, detailsNoShippingOptions, requestPayerEmail, expectValues);">
+ User accepts payment request, merchant requests email.
+ </button>
+ When prompted, please use "wpt@w3.org" as the email.
+ </li>
+ <li>
+ <button onclick="
+ const requestPayerPhone = Object.assign({}, optionsRequestNothing, {requestPayerPhone: true});
+ const expectValues = { payerPhone: '+12345678910' };
+ testAcceptRequestAlgorithm(this, detailsNoShippingOptions, requestPayerPhone, expectValues);">
+ User accepts payment request, merchant requests phone.
+ </button>
+ When prompted, please use "+12345678910" as the phone number.
+ </li>
+ <li>
+ <button onclick="
+ const requestPayerName = Object.assign({}, optionsRequestNothing, {requestPayerName: true});
+ const expectValues = { payerName: 'web platform test' };
+ testAcceptRequestAlgorithm(this, detailsNoShippingOptions, requestPayerName, expectValues);">
+ User accepts payment request, merchant requests payer's name.
+ </button>
+ When prompted, please use "web platform test" as the payer name.
+ </li>
+ <li>
+ <button onclick="
+ const expectValues = {
+ payerEmail: 'wpt@w3.org',
+ payerName: 'web platform test',
+ payerPhone: '+12345678910',
+ shippingOption: 'option 2',
+ };
+ testAcceptRequestAlgorithm(this, detailsWithShippingOptions, optionsRequestEverything, expectValues);">
+ User accepts payment request, merchant requests everything.
+ </button>
+ When prompted, please use: "+12345678910" as the phone number, "web platform test" as the payer name, and "wpt@w3.org" as the email. Then press Pay.
+ </li>
+ <li>
+ <button onclick="done()">Done</button>
+ </li>
+ </ol>
+</section>
+<small>
+ If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a>
+ and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>.
+</small>
diff --git a/tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js b/tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js
index 42bda12919d..2a313fe7b14 100644
--- a/tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js
+++ b/tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js
@@ -53,8 +53,7 @@ promise_test(async t => {
'url': null,
'src': rc1_child_url,
'id': 'test-id',
- // Iframes that are generated by addIframe have an empty name.
- 'name': '',
+ 'name': null,
'reasons': null,
'children': null
}]);
diff --git a/tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js b/tests/wpt/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js
new file mode 100644
index 00000000000..cda0ac43944
--- /dev/null
+++ b/tests/wpt/tests/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/tests/wpt/tests/performance-timeline/not-restored-reasons/test-helper.js b/tests/wpt/tests/performance-timeline/not-restored-reasons/test-helper.js
index 826b0ccb2be..ba9a4c0342f 100644
--- a/tests/wpt/tests/performance-timeline/not-restored-reasons/test-helper.js
+++ b/tests/wpt/tests/performance-timeline/not-restored-reasons/test-helper.js
@@ -27,9 +27,9 @@ function assertReasonsStructEquals(
} else {
for (let j = 0; j < children.length; j++) {
assertReasonsStructEquals(
- result.children[0], children[0].url,
- children[0].src, children[0].id, children[0].name, children[0].reasons,
- children[0].children);
+ result.children[j], children[j].url,
+ children[j].src, children[j].id, children[j].name, children[j].reasons,
+ children[j].children);
}
}
}
diff --git a/tests/wpt/tests/pointerlock/WEB_FEATURES.yml b/tests/wpt/tests/pointerlock/WEB_FEATURES.yml
new file mode 100644
index 00000000000..61ab30b26b6
--- /dev/null
+++ b/tests/wpt/tests/pointerlock/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: pointer-lock
+ files: "**"
diff --git a/tests/wpt/tests/preload/resources/A4.ogv b/tests/wpt/tests/preload/resources/A4.ogv
deleted file mode 100644
index de99616eceb..00000000000
--- a/tests/wpt/tests/preload/resources/A4.ogv
+++ /dev/null
Binary files differ
diff --git a/tests/wpt/tests/resource-timing/initiator-type/video.html b/tests/wpt/tests/resource-timing/initiator-type/video.html
index 16f3b3dea5f..2d8c9dcc474 100644
--- a/tests/wpt/tests/resource-timing/initiator-type/video.html
+++ b/tests/wpt/tests/resource-timing/initiator-type/video.html
@@ -19,14 +19,14 @@
src="/resource-timing/resources/empty.py?id=track">
</video>
<video autoplay="true">
- <source src="/media/test.ogv?id=source-ogv" type="video/ogg">
+ <source src="/media/test.webm?id=source-webm" type="video/webm">
</video>
<script>
initiator_type_test("blue.png?id=poster", "video", "<video poster>");
initiator_type_test("media/test.mp4?id=src", "video", "<video src>");
initiator_type_test("media/test.mp4?id=source-mp4", "video", "<source src> with type=\"video/mp4\"");
initiator_type_test("empty.py?id=track", "track", "<track src>");
- initiator_type_test("media/test.ogv?id=source-ogv", "video", "<source src> with type=\"video/ogg\"");
+ initiator_type_test("media/test.webm?id=source-webm", "video", "<source src> with type=\"video/webm\"");
</script>
</body>
</html>
diff --git a/tests/wpt/tests/scroll-animations/WEB_FEATURES.yml b/tests/wpt/tests/scroll-animations/WEB_FEATURES.yml
new file mode 100644
index 00000000000..adf4bf2ac17
--- /dev/null
+++ b/tests/wpt/tests/scroll-animations/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: scroll-driven-animations
+ files: "**"
diff --git a/tests/wpt/tests/selection/crashtests/selection-modify-line-next-to-input-and-make-it-invisible.html b/tests/wpt/tests/selection/crashtests/selection-modify-line-next-to-input-and-make-it-invisible.html
new file mode 100644
index 00000000000..09e018f5819
--- /dev/null
+++ b/tests/wpt/tests/selection/crashtests/selection-modify-line-next-to-input-and-make-it-invisible.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script>
+document.addEventListener("DOMContentLoaded", () => {
+ getSelection().addRange(document.createRange());
+ const range = document.createRange();
+ range.selectNode(document.querySelector("meter"));
+ getSelection().addRange(range);
+ getSelection().modify('move', 'left', 'line');
+ document.querySelector("input").style.display = "none";
+ getSelection().modify('move', 'left', 'lineboundary');
+})
+</script>
+</head>
+<body>
+<input>
+<div contenteditable>
+<meter>
+</div>
+</body>
+</html>
diff --git a/tests/wpt/tests/selection/selection-nested-video.html b/tests/wpt/tests/selection/selection-nested-video.html
new file mode 100644
index 00000000000..9777d7d9927
--- /dev/null
+++ b/tests/wpt/tests/selection/selection-nested-video.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Selection with nested videos doesn't crash</title>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1887963">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+async_test(t => {
+ document.addEventListener("DOMContentLoaded", () => {
+ let c = a.attachShadow({mode: "open"});
+ const sel = window.getSelection();
+
+ sel.setBaseAndExtent(b, 0, c, 0);
+
+ assert_equals(sel.anchorNode, b);
+ assert_equals(sel.anchorOffset, 0);
+ assert_equals(sel.focusNode, b);
+ assert_equals(sel.focusOffset, 0);
+ t.done();
+ })
+});
+</script>
+<div id="a">A</div>
+<video>
+<video id="b">
diff --git a/tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6-ref.html b/tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6-ref.html
new file mode 100644
index 00000000000..b7441c7bbca
--- /dev/null
+++ b/tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<span id="span">Start
+<div>
+ <span id="inner1">inner1</p>
+ <span id="inner2">inner2</p>
+</div>
+</span>
+<script>
+ const start = document.getElementById("span").firstChild;
+ const end = document.getElementById("inner2");
+ window.getSelection().setBaseAndExtent(start, 3, end.firstChild, 3);
+</script>
diff --git a/tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6.html b/tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6.html
new file mode 100644
index 00000000000..cc264a66686
--- /dev/null
+++ b/tests/wpt/tests/selection/shadow-dom/cross-shadow-boundary-6.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<html class="reftest-wait">
+<head>
+<link rel=match href="cross-shadow-boundary-6-ref.html">
+</head>
+<span id="span">Start
+<div>
+ <template shadowrootmode="open">
+ <span id="inner1">inner1</p>
+ <span id="inner2">inner2</p>
+ </template>
+</div>
+</span>
+<script>
+ const start = document.getElementById("span").firstChild;
+ const end = document.querySelector('div').shadowRoot.getElementById("inner2");
+
+ async function waitForRAFs() {
+ return new Promise(resolve => {
+ window.requestAnimationFrame(() => {
+ window.requestAnimationFrame(() => {
+ window.requestAnimationFrame(() => {
+ resolve();
+ });
+ });
+ });
+ });
+ }
+
+ async function runTest() {
+ window.getSelection().setBaseAndExtent(start, 3, end.firstChild, 3);
+ await waitForRAFs();
+
+ window.getSelection().removeAllRanges();
+ await waitForRAFs();
+
+ window.getSelection().setBaseAndExtent(start, 3, end.firstChild, 3);
+ await waitForRAFs();
+ document.documentElement.className = "";
+ }
+
+ runTest();
+</script>
+</html>
diff --git a/tests/wpt/tests/server-timing/WEB_FEATURES.yml b/tests/wpt/tests/server-timing/WEB_FEATURES.yml
new file mode 100644
index 00000000000..8b00e934ea6
--- /dev/null
+++ b/tests/wpt/tests/server-timing/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: server-timing
+ files: "**"
diff --git a/tests/wpt/tests/shadow-dom/declarative/gethtml.html b/tests/wpt/tests/shadow-dom/declarative/gethtml.html
index c03749c815c..d950ca7734e 100644
--- a/tests/wpt/tests/shadow-dom/declarative/gethtml.html
+++ b/tests/wpt/tests/shadow-dom/declarative/gethtml.html
@@ -1,5 +1,6 @@
<!DOCTYPE html>
<title>getHTML behavior</title>
+<meta name="timeout" content="long">
<link rel='author' href='mailto:masonf@chromium.org'>
<link rel='help' href='https://github.com/whatwg/html/issues/8867'>
<script src='/resources/testharness.js'></script>
@@ -9,7 +10,9 @@
<body>
<script>
-function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable, clonable) {
+function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot,
+ lightDOMContent, declarativeShadowDom, mode, delegatesFocus,
+ serializable, clonable) {
const t = test(t => {
// Create and attach element
let wrapper;
@@ -40,7 +43,9 @@ function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, d
const clonableAttr = clonable ? ' shadowrootclonable=""' : '';
if (allowsShadowDom && declarativeShadowDom) {
- const html = `<${elementType}><template shadowrootmode=${mode}${delegatesAttr}${serializableAttr}${clonableAttr}>`;
+ const html = `<${elementType}>${lightDOMContent}<template ` +
+ `shadowrootmode=${mode}${delegatesAttr}${serializableAttr}` +
+ `${clonableAttr}>`;
wrapper.setHTMLUnsafe(html);
if (isOpen) {
shadowRoot = wrapper.firstElementChild.shadowRoot;
@@ -52,6 +57,9 @@ function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, d
// Imperative shadow dom
const element = document.createElement(elementType);
wrapper.appendChild(element);
+ const temp = document.createElement('div');
+ temp.innerHTML = lightDOMContent;
+ element.append(...temp.childNodes);
if (allowsShadowDom) {
shadowRoot = element.attachShadow(initDict);
}
@@ -59,28 +67,39 @@ function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, d
assert_true(!allowsShadowDom || !!shadowRoot);
if (allowsShadowDom) {
- const correctShadowHtml = `<template shadowrootmode="${mode}"${delegatesAttr}${serializableAttr}${clonableAttr}><slot></slot></template>`;
- const correctHtml = `<${elementType}>${correctShadowHtml}</${elementType}>`;
+ const correctShadowHtml = `<template shadowrootmode="${mode}"` +
+ `${delegatesAttr}${serializableAttr}${clonableAttr}><slot></slot>` +
+ `</template>`;
+ const correctHtml = `<${elementType}>${correctShadowHtml}` +
+ `${lightDOMContent}</${elementType}>`;
assert_equals(shadowRoot.mode,mode);
assert_equals(shadowRoot.delegatesFocus,delegatesFocus);
assert_equals(shadowRoot.serializable,expectedSerializable);
assert_equals(shadowRoot.clonable,clonable);
shadowRoot.appendChild(document.createElement('slot'));
- const emptyElement = `<${elementType}></${elementType}>`;
+ const emptyElement = `<${elementType}>${lightDOMContent}</${elementType}>`;
if (isOpen) {
if (expectedSerializable) {
- assert_equals(wrapper.getHTML({serializableShadowRoots: true}), correctHtml);
+ assert_equals(wrapper.getHTML({serializableShadowRoots: true}),
+ correctHtml);
+ assert_equals(wrapper.firstElementChild.getHTML({
+ serializableShadowRoots: true}),
+ `${correctShadowHtml}${lightDOMContent}`);
} else {
assert_equals(wrapper.getHTML({serializableShadowRoots: true}), emptyElement);
}
} else {
// Closed shadow roots should not be returned unless shadowRoots specifically contains the shadow root:
- assert_equals(wrapper.getHTML({serializableShadowRoots: true}), emptyElement);
- assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots: []}), emptyElement);
+ assert_equals(wrapper.getHTML({serializableShadowRoots: true}),
+ emptyElement);
+ assert_equals(wrapper.getHTML({serializableShadowRoots: true,
+ shadowRoots: []}), emptyElement);
}
// If we provide the shadow root, serialize it, regardless of serializableShadowRoots.
- assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots: [shadowRoot]}),correctHtml);
- assert_equals(wrapper.getHTML({serializableShadowRoots: false, shadowRoots: [shadowRoot]}),correctHtml);
+ assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots:
+ [shadowRoot]}),correctHtml);
+ assert_equals(wrapper.getHTML({serializableShadowRoots: false, shadowRoots:
+ [shadowRoot]}),correctHtml);
assert_equals(wrapper.getHTML({shadowRoots: [shadowRoot]}),correctHtml);
} else {
// For non-shadow hosts, getHTML() should also match .innerHTML
@@ -88,11 +107,17 @@ function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, d
}
// Either way, make sure getHTML({serializableShadowRoots: false}) matches .innerHTML
- assert_equals(wrapper.getHTML({serializableShadowRoots: false}),wrapper.innerHTML,'getHTML() with serializableShadowRoots false should return the same as .innerHTML');
+ assert_equals(wrapper.getHTML({serializableShadowRoots: false}),wrapper.innerHTML,
+ 'getHTML() with serializableShadowRoots false should return the same as .innerHTML');
// ...and that the default for serializableShadowRoots is false.
- assert_equals(wrapper.getHTML(),wrapper.innerHTML,'The default for serializableShadowRoots should be false');
+ assert_equals(wrapper.getHTML(),wrapper.innerHTML,
+ 'The default for serializableShadowRoots should be false');
- }, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on <${elementType}>${allowsShadowDom ? `, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, mode=${mode}, delegatesFocus=${delegatesFocus}, serializable=${serializable}, clonable=${clonable}.` : ''}`);
+ }, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on ` +
+ `<${elementType}>${lightDOMContent}${allowsShadowDom ?
+ `, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, ` +
+ `mode=${mode}, delegatesFocus=${delegatesFocus}, ` +
+ `serializable=${serializable}, clonable=${clonable}.` : ''}`);
}
function runAllTests() {
@@ -101,20 +126,25 @@ function runAllTests() {
for (const elementName of allElements) {
const allowsShadowDom = safelisted.includes(elementName);
for (const runGetHTMLOnShadowRoot of [false, true]) {
- if (allowsShadowDom) {
- for (const declarativeShadowDom of [false, true]) {
- for (const delegatesFocus of [false, true]) {
- for (const clonable of [false, true]) {
- for (const mode of ['open', 'closed']) {
- for (const serializable of [undefined, 'false', 'true']) {
- testElementType(true, elementName, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable, clonable);
+ for (const lightDOMContent of ['','<span>light</span>']) {
+ if (allowsShadowDom) {
+ for (const declarativeShadowDom of [false, true]) {
+ for (const delegatesFocus of [false, true]) {
+ for (const clonable of [false, true]) {
+ for (const mode of ['open', 'closed']) {
+ for (const serializable of [undefined, 'false', 'true']) {
+ testElementType(true, elementName, runGetHTMLOnShadowRoot,
+ lightDOMContent, declarativeShadowDom, mode,
+ delegatesFocus, serializable, clonable);
+ }
}
}
}
}
+ } else {
+ testElementType(false, elementName, runGetHTMLOnShadowRoot,
+ lightDOMContent);
}
- } else {
- testElementType(false, elementName, runGetHTMLOnShadowRoot);
}
}
}
diff --git a/tests/wpt/tests/shadow-dom/host-with-namespace.xhtml b/tests/wpt/tests/shadow-dom/host-with-namespace.xhtml
new file mode 100644
index 00000000000..243728a015d
--- /dev/null
+++ b/tests/wpt/tests/shadow-dom/host-with-namespace.xhtml
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" lang="en" >
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="default-namespace"></div>
+ <xhtml:div id="explicit-namespace"></xhtml:div>
+
+ <script>
+ <![CDATA[
+ test(function() {
+ const defaultNamespaceHost = document.getElementById('default-namespace');
+ defaultNamespaceHost.attachShadow({mode: 'open'});
+ assert_not_equals(defaultNamespaceHost.shadowRoot, null, "attachShadow should work on node with default namespace");
+ }, 'attachShadow with a default-namespaced element');
+
+ test(function() {
+ const explicitNamespaceHost = document.getElementById('explicit-namespace');
+ explicitNamespaceHost.attachShadow({mode: 'open'});
+ assert_not_equals(explicitNamespaceHost.shadowRoot, null, "attachShadow should work on node with explicit namespace");
+ }, 'Test attachShadow with a namespaced element');
+ ]]>
+ </script>
+</body>
+</html>
diff --git a/tests/wpt/tests/speculation-rules/prerender/resources/request-picture-in-picture.html b/tests/wpt/tests/speculation-rules/prerender/resources/request-picture-in-picture.html
index 9aea3d33d5c..360cf3b2007 100644
--- a/tests/wpt/tests/speculation-rules/prerender/resources/request-picture-in-picture.html
+++ b/tests/wpt/tests/speculation-rules/prerender/resources/request-picture-in-picture.html
@@ -3,7 +3,7 @@
<script src="/resources/testharnessreport.js"></script>
<script src="/speculation-rules/prerender/resources/utils.js"></script>
<video id="target"
- onloadstart="loadstart()" src="/media/test.ogv"></video>
+ onloadstart="loadstart()" src="/media/test.webm"></video>
<script>
assert_true(document.prerendering);
diff --git a/tests/wpt/tests/streams/piping/crashtests/cross-piping2.https.html b/tests/wpt/tests/streams/piping/crashtests/cross-piping2.https.html
new file mode 100644
index 00000000000..4616aef479c
--- /dev/null
+++ b/tests/wpt/tests/streams/piping/crashtests/cross-piping2.https.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script type="module">
+ let a = self.open()
+ let d = await a.navigator.storage.getDirectory()
+ let h = await d.getFileHandle("c5c9960b-a637-4232-be3d-3ccc5704852f", {"create": true})
+ let r = new ReadableStream({
+ start(c) {
+ c.enqueue(h)
+ c.close();
+ }
+ });
+ let w = await h.createWritable({ })
+ r.pipeThrough({"readable": r, "writable": w}, {})
+</script>
diff --git a/tests/wpt/tests/svg/linking/reftests/url-processing-invalid-base.svg b/tests/wpt/tests/svg/linking/reftests/url-processing-invalid-base.svg
deleted file mode 100644
index fc5d7c6b062..00000000000
--- a/tests/wpt/tests/svg/linking/reftests/url-processing-invalid-base.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg"
- xmlns:html="http://www.w3.org/1999/xhtml">
- <title>An invalid base URL makes all internal references invalid</title>
- <html:link rel="help" href="https://svgwg.org/svg2-draft/linking.html#processingURL"/>
- <html:link rel="help" href="https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint"/>
- <html:link rel="help" href="https://svgwg.org/svg2-draft/pservers.html#PaintServerTemplates"/>
- <html:link rel="match" href="reference/green-100x100.svg"/>
- <html:base href="invalid:"/>
- <linearGradient id="p2">
- <stop stop-color="orange"/>
- </linearGradient>
- <linearGradient id="p" href="#p2"/>
- <rect width="100" height="100" fill="red"/>
- <rect width="100" height="100" fill="url(#p) green"/>
-</svg>
diff --git a/tests/wpt/tests/tools/web_features/manifest.py b/tests/wpt/tests/tools/web_features/manifest.py
index 0f442fe7f63..15ec5362b4e 100644
--- a/tests/wpt/tests/tools/web_features/manifest.py
+++ b/tests/wpt/tests/tools/web_features/manifest.py
@@ -187,7 +187,7 @@ def write_manifest_file(path: str, web_features_map: WebFeaturesMap) -> None:
{
"version": 1,
"data": web_features_map
- }, cls=WebFeatureManifestEncoder))
+ }, cls=WebFeatureManifestEncoder, sort_keys=True))
def main(venv: Any = None, **kwargs: Any) -> int:
diff --git a/tests/wpt/tests/tools/web_features/tests/test_manifest.py b/tests/wpt/tests/tools/web_features/tests/test_manifest.py
index 8b656876ff3..b83829308a2 100644
--- a/tests/wpt/tests/tools/web_features/tests/test_manifest.py
+++ b/tests/wpt/tests/tools/web_features/tests/test_manifest.py
@@ -202,8 +202,7 @@ def test_valid_schema():
write_manifest_file("test_file.json", web_features_map)
mock_file.assert_called_once_with("test_file.json", "w")
mock_file.return_value.write.assert_called_once_with(
- ('{"version": 1,'
- ' "data": {"grid": ["/grid_test1.js", "/grid_test2.js"], "avif": ["/avif_test1.js"]}}'))
+ '{"data": {"avif": ["/avif_test1.js"], "grid": ["/grid_test1.js", "/grid_test2.js"]}, "version": 1}')
args = mock_file.return_value.write.call_args
file_dict = json.loads(args[0][0])
# Should not throw an exception
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/base.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/base.py
index 180e3fb9592..6b1465cde8a 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/base.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/base.py
@@ -1,5 +1,3 @@
-# mypy: allow-untyped-defs
-
import enum
import errno
import os
@@ -8,16 +6,21 @@ import socket
import time
import traceback
from abc import ABCMeta, abstractmethod
+from typing import cast, Any, List, Mapping, Optional, Tuple, Type
import mozprocess
+from mozdebug import DebuggerInfo
+from mozlog.structuredlog import StructuredLogger
from ..environment import wait_for_service
+from ..testloader import GroupMetadata
from ..wptcommandline import require_arg # noqa: F401
+from ..wpttest import Test
here = os.path.dirname(__file__)
-def cmd_arg(name, value=None):
+def cmd_arg(name: str, value: Optional[str] = None) -> str:
prefix = "-" if platform.system() == "Windows" else "--"
rv = prefix + name
if value is not None:
@@ -25,7 +28,7 @@ def cmd_arg(name, value=None):
return rv
-def maybe_add_args(required_args, current_args):
+def maybe_add_args(required_args: List[str], current_args: List[str]) -> List[str]:
for required_arg in required_args:
# If the arg is in the form of "variable=value", only add it if
# no arg with another value for "variable" is already there.
@@ -39,15 +42,16 @@ def maybe_add_args(required_args, current_args):
return current_args
-def certificate_domain_list(list_of_domains, certificate_file):
+def certificate_domain_list(list_of_domains: List[str],
+ certificate_file: str) -> List[Mapping[str, Any]]:
"""Build a list of domains where certificate_file should be used"""
- cert_list = []
+ cert_list: List[Mapping[str, Any]] = []
for domain in list_of_domains:
cert_list.append({"host": domain, "certificateFile": certificate_file})
return cert_list
-def get_free_port():
+def get_free_port() -> int:
"""Get a random unbound port"""
while True:
s = socket.socket()
@@ -56,27 +60,27 @@ def get_free_port():
except OSError:
continue
else:
- return s.getsockname()[1]
+ return cast(int, s.getsockname()[1])
finally:
s.close()
-def get_timeout_multiplier(test_type, run_info_data, **kwargs):
+def get_timeout_multiplier(test_type: str, run_info_data: Mapping[str, Any], **kwargs: Any) -> float:
if kwargs["timeout_multiplier"] is not None:
- return kwargs["timeout_multiplier"]
+ return cast(float, kwargs["timeout_multiplier"])
return 1
-def browser_command(binary, args, debug_info):
+def browser_command(binary: str,
+ args: List[str],
+ debug_info: DebuggerInfo) -> Tuple[List[str], List[str]]:
if debug_info:
if debug_info.requiresEscapedArgs:
args = [item.replace("&", "\\&") for item in args]
debug_args = [debug_info.path] + debug_info.args
else:
debug_args = []
-
command = [binary] + args
-
return debug_args, command
@@ -84,6 +88,9 @@ class BrowserError(Exception):
pass
+BrowserSettings = Mapping[str, Any]
+
+
class Browser:
"""Abstract class serving as the basis for Browser implementations.
@@ -94,17 +101,16 @@ class Browser:
"""
__metaclass__ = ABCMeta
- process_cls = None
- init_timeout = 30
+ init_timeout: float = 30
- def __init__(self, logger):
+ def __init__(self, logger: StructuredLogger):
self.logger = logger
- def setup(self):
+ def setup(self) -> None:
"""Used for browser-specific setup that happens at the start of a test run"""
pass
- def settings(self, test):
+ def settings(self, test: Test) -> BrowserSettings:
"""Dictionary of metadata that is constant for a specific launch of a browser.
This is used to determine when the browser instance configuration changes, requiring
@@ -114,64 +120,66 @@ class Browser:
return {}
@abstractmethod
- def start(self, group_metadata, **kwargs):
+ def start(self, group_metadata: GroupMetadata, **kwargs: Any) -> None:
"""Launch the browser object and get it into a state where is is ready to run tests"""
pass
@abstractmethod
- def stop(self, force=False):
- """Stop the running browser process."""
+ def stop(self, force: bool = False) -> bool:
+ """Stop the running browser process.
+
+ Return True iff the browser was successfully stopped.
+ """
pass
+ @property
@abstractmethod
- def pid(self):
+ def pid(self) -> Optional[int]:
"""pid of the browser process or None if there is no pid"""
pass
@abstractmethod
- def is_alive(self):
+ def is_alive(self) -> bool:
"""Boolean indicating whether the browser process is still running"""
pass
- def cleanup(self):
+ def cleanup(self) -> None:
"""Browser-specific cleanup that is run after the testrun is finished"""
pass
- def executor_browser(self):
+ def executor_browser(self) -> Tuple[Type['ExecutorBrowser'], Mapping[str, Any]]:
"""Returns the ExecutorBrowser subclass for this Browser subclass and the keyword arguments
with which it should be instantiated"""
return ExecutorBrowser, {}
- def maybe_parse_tombstone(self):
- """Possibly parse tombstones on Android device for Android target"""
- pass
-
- def check_crash(self, process, test):
+ def check_crash(self, process: int, test: str) -> bool:
"""Check if a crash occured and output any useful information to the
log. Returns a boolean indicating whether a crash occured."""
return False
@property
- def pac(self):
+ def pac(self) -> Optional[str]:
return None
+
class NullBrowser(Browser):
- def __init__(self, logger, **kwargs):
+ def __init__(self, logger: StructuredLogger, **kwargs: Any):
super().__init__(logger)
- def start(self, **kwargs):
+ def start(self, group_metadata: GroupMetadata, **kwargs: Any) -> None:
"""No-op browser to use in scenarios where the TestRunnerManager shouldn't
actually own the browser process (e.g. Servo where we start one browser
per test)"""
pass
- def stop(self, force=False):
- pass
+ def stop(self, force: bool = False) -> bool:
+ return True
- def pid(self):
+ @property
+ def pid(self) -> Optional[int]:
return None
- def is_alive(self):
+ def is_alive(self) -> bool:
return True
@@ -184,7 +192,7 @@ class ExecutorBrowser:
but in some cases it may have more elaborate methods for setting
up the browser from the runner process.
"""
- def __init__(self, **kwargs):
+ def __init__(self, **kwargs: Any):
for k, v in kwargs.items():
setattr(self, k, v)
@@ -233,20 +241,20 @@ class OutputHandler:
but sometimes use a wrapper e.g. mozrunner.
"""
- def __init__(self, logger, command, **kwargs):
+ def __init__(self, logger: StructuredLogger, command: List[str], **kwargs: Any):
self.logger = logger
self.command = command
- self.pid = None
+ self.pid: Optional[int] = None
self.state = OutputHandlerState.BEFORE_PROCESS_START
- self.line_buffer = []
+ self.line_buffer: List[bytes] = []
- def after_process_start(self, pid):
+ def after_process_start(self, pid: int) -> None:
assert self.state == OutputHandlerState.BEFORE_PROCESS_START
self.logger.debug("OutputHandler.after_process_start")
self.pid = pid
self.state = OutputHandlerState.AFTER_PROCESS_START
- def start(self, **kwargs):
+ def start(self, **kwargs: Any) -> None:
assert self.state == OutputHandlerState.AFTER_PROCESS_START
self.logger.debug("OutputHandler.start")
# Need to change the state here before we try to empty the buffer
@@ -254,9 +262,9 @@ class OutputHandler:
self.state = OutputHandlerState.AFTER_HANDLER_START
for item in self.line_buffer:
self(item)
- self.line_buffer = None
+ self.line_buffer.clear()
- def after_process_stop(self, clean_shutdown=True):
+ def after_process_stop(self, clean_shutdown: bool = True) -> None:
# If we didn't get as far as configure, just
# dump all logs with no configuration
self.logger.debug("OutputHandler.after_process_stop")
@@ -264,7 +272,7 @@ class OutputHandler:
self.start()
self.state = OutputHandlerState.AFTER_PROCESS_STOP
- def __call__(self, line):
+ def __call__(self, line: bytes) -> None:
if self.state < OutputHandlerState.AFTER_HANDLER_START:
self.line_buffer.append(line)
return
@@ -282,9 +290,17 @@ class OutputHandler:
class WebDriverBrowser(Browser):
__metaclass__ = ABCMeta
- def __init__(self, logger, binary=None, webdriver_binary=None,
- webdriver_args=None, host="127.0.0.1", port=None, base_path="/",
- env=None, supports_pac=True, **kwargs):
+ def __init__(self,
+ logger: StructuredLogger,
+ binary: Optional[str] = None,
+ webdriver_binary: Optional[str] = None,
+ webdriver_args: Optional[List[str]] = None,
+ host: str = "127.0.0.1",
+ port: Optional[int] = None,
+ base_path: str = "/",
+ env: Optional[Mapping[str, str]] = None,
+ supports_pac: bool = True,
+ **kwargs: Any):
super().__init__(logger)
if webdriver_binary is None:
@@ -303,17 +319,17 @@ class WebDriverBrowser(Browser):
self.env = os.environ.copy() if env is None else env
self.webdriver_args = webdriver_args if webdriver_args is not None else []
- self.init_deadline = None
- self._output_handler = None
+ self.init_deadline: Optional[float] = None
+ self._output_handler: Optional[OutputHandler] = None
self._cmd = None
- self._proc = None
+ self._proc: Optional[mozprocess.ProcessHandler] = None
self._pac = None
- def make_command(self):
+ def make_command(self) -> List[str]:
"""Returns the full command for starting the server process as a list."""
return [self.webdriver_binary] + self.webdriver_args
- def start(self, group_metadata, **kwargs):
+ def start(self, group_metadata: GroupMetadata, **kwargs: Any) -> None:
self.init_deadline = time.time() + self.init_timeout
try:
self._run_server(group_metadata, **kwargs)
@@ -321,14 +337,15 @@ class WebDriverBrowser(Browser):
self.stop()
raise
- def create_output_handler(self, cmd):
+ def create_output_handler(self, cmd: List[str]) -> OutputHandler:
"""Return an instance of the class used to handle application output.
This can be overridden by subclasses which have particular requirements
for parsing, or otherwise using, the output."""
return OutputHandler(self.logger, cmd)
- def _run_server(self, group_metadata, **kwargs):
+ def _run_server(self, group_metadata: GroupMetadata, **kwargs: Any) -> None:
+ assert self.init_deadline is not None
cmd = self.make_command()
self._output_handler = self.create_output_handler(cmd)
@@ -365,17 +382,18 @@ class WebDriverBrowser(Browser):
self._output_handler.start(group_metadata=group_metadata, **kwargs)
self.logger.debug("_run complete")
- def stop(self, force=False):
+ def stop(self, force: bool = False) -> bool:
self.logger.debug("Stopping WebDriver")
clean = True
if self.is_alive():
+ proc = cast(mozprocess.ProcessHandler, self._proc)
# Pass a timeout value to mozprocess Processhandler.kill()
# to ensure it always returns within it.
# See https://bugzilla.mozilla.org/show_bug.cgi?id=1760080
- kill_result = self._proc.kill(timeout=5)
+ kill_result = proc.kill(timeout=5)
if force and kill_result != 0:
clean = False
- self._proc.kill(9, timeout=5)
+ proc.kill(9, timeout=5)
success = not self.is_alive()
if success and self._output_handler is not None:
# Only try to do output post-processing if we managed to shut down
@@ -383,42 +401,41 @@ class WebDriverBrowser(Browser):
self._output_handler = None
return success
- def is_alive(self):
- return hasattr(self._proc, "proc") and self._proc.poll() is None
+ def is_alive(self) -> bool:
+ return self._proc is not None and hasattr(self._proc, "proc") and self._proc.poll() is None
@property
- def url(self):
+ def url(self) -> str:
if self.port is not None:
return f"http://{self.host}:{self.port}{self.base_path}"
raise ValueError("Can't get WebDriver URL before port is assigned")
@property
- def pid(self):
- if self._proc is not None:
- return self._proc.pid
+ def pid(self) -> Optional[int]:
+ return self._proc.pid if self._proc is not None else None
@property
- def port(self):
+ def port(self) -> int:
# If no port is supplied, we'll get a free port right before we use it.
# Nothing guarantees an absence of race conditions here.
if self._port is None:
self._port = get_free_port()
return self._port
- def cleanup(self):
+ def cleanup(self) -> None:
self.stop()
- def executor_browser(self):
+ def executor_browser(self) -> Tuple[Type[ExecutorBrowser], Mapping[str, Any]]:
return ExecutorBrowser, {"webdriver_url": self.url,
"host": self.host,
"port": self.port,
"pac": self.pac,
"env": self.env}
- def settings(self, test):
+ def settings(self, test: Test) -> BrowserSettings:
self._pac = test.environment.get("pac", None) if self._supports_pac else None
return {"pac": self._pac}
@property
- def pac(self):
+ def pac(self) -> Optional[str]:
return self._pac
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py
index 85c98f29942..d3beb449e24 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py
@@ -4,7 +4,9 @@ from .base import WebDriverBrowser, require_arg
from .base import get_timeout_multiplier # noqa: F401
from ..executors import executor_kwargs as base_executor_kwargs
from ..executors.base import WdspecExecutor # noqa: F401
-from ..executors.executorwebdriver import (WebDriverTestharnessExecutor, # noqa: F401
+from ..executors.executorchrome import ChromeDriverPrintRefTestExecutor # noqa: F401
+from ..executors.executorwebdriver import (WebDriverCrashtestExecutor, # noqa: F401
+ WebDriverTestharnessExecutor, # noqa: F401
WebDriverRefTestExecutor) # noqa: F401
@@ -12,7 +14,9 @@ __wptrunner__ = {"product": "chrome_ios",
"check_args": "check_args",
"browser": "ChromeiOSBrowser",
"executor": {"testharness": "WebDriverTestharnessExecutor",
- "reftest": "WebDriverRefTestExecutor"},
+ "reftest": "WebDriverRefTestExecutor",
+ "print-reftest": "ChromeDriverPrintRefTestExecutor",
+ "crashtest": "WebDriverCrashtestExecutor"},
"browser_kwargs": "browser_kwargs",
"executor_kwargs": "executor_kwargs",
"env_extras": "env_extras",
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py
index 6bcbef7c477..814b8b8d758 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -864,6 +864,7 @@ class FirefoxBrowser(Browser):
self.instance_manager.stop_current(force)
self.logger.debug("stopped")
+ @property
def pid(self):
return self.instance.pid()
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py
index 0e90c8a6e48..7c158902e1a 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py
@@ -323,6 +323,7 @@ class FirefoxAndroidBrowser(Browser):
self.runner.cleanup()
self.logger.debug("stopped")
+ @property
def pid(self):
if self.runner.process_handler is None:
return None
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/sauce.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/sauce.py
index 0f21afd38f2..465aac6e497 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/sauce.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/sauce.py
@@ -231,6 +231,7 @@ class SauceBrowser(Browser):
def stop(self, force=False):
pass
+ @property
def pid(self):
return None
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/servodriver.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/servodriver.py
index 5195fa64422..2cb638be156 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/servodriver.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/servodriver.py
@@ -161,6 +161,7 @@ class ServoWebDriverBrowser(Browser):
if self.output_handler is not None:
self.output_handler.after_process_stop()
+ @property
def pid(self):
if self.proc is None:
return None
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/wktr.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/wktr.py
index 8d429f357b3..d65f35ccb33 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/wktr.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/wktr.py
@@ -186,6 +186,7 @@ class WKTRBrowser(Browser):
def is_alive(self):
return self._proc is not None and self._proc.poll() is None
+ @property
def pid(self):
return self._proc.pid if self._proc else None
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/environment.py b/tests/wpt/tests/tools/wptrunner/wptrunner/environment.py
index e206e427546..37951f41d2e 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/environment.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/environment.py
@@ -1,5 +1,6 @@
# mypy: allow-untyped-defs
+import contextlib
import errno
import json
import os
@@ -7,8 +8,11 @@ import signal
import socket
import sys
import time
+from typing import Optional
+import mozprocess
from mozlog import get_default_logger, handlers
+from mozlog.structuredlog import StructuredLogger
from . import mpcontext
from .wptlogging import LogLevelRewriter, QueueHandler, LogQueueThread
@@ -110,6 +114,7 @@ class TestEnvironment:
self.options = options if options is not None else {}
mp_context = mpcontext.get_context()
+ self._stack = contextlib.ExitStack()
self.cache_manager = mp_context.Manager()
self.stash = serve.stash.StashServer(mp_context=mp_context)
self.env_extras = env_extras
@@ -121,13 +126,13 @@ class TestEnvironment:
self.suppress_handler_traceback = suppress_handler_traceback
def __enter__(self):
- server_log_handler = self.server_logging_ctx.__enter__()
+ server_log_handler = self._stack.enter_context(self.server_logging_ctx)
self.config_ctx = self.build_config()
- self.config = self.config_ctx.__enter__()
+ self.config = self._stack.enter_context(self.config_ctx)
- self.stash.__enter__()
- self.cache_manager.__enter__()
+ self._stack.enter_context(self.stash)
+ self._stack.enter_context(self.cache_manager)
assert self.env_extras_cms is None, (
"A TestEnvironment object cannot be nested")
@@ -136,7 +141,7 @@ class TestEnvironment:
for env in self.env_extras:
cm = env(self.options, self.config)
- cm.__enter__()
+ self._stack.enter_context(cm)
self.env_extras_cms.append(cm)
self.servers = serve.start(self.server_logger,
@@ -147,33 +152,27 @@ class TestEnvironment:
webtransport_h3=self.enable_webtransport)
if self.options.get("supports_debugger") and self.debug_info and self.debug_info.interactive:
- self.ignore_interrupts()
+ self._stack.enter_context(self.ignore_interrupts())
return self
def __exit__(self, exc_type, exc_val, exc_tb):
- self.process_interrupts()
-
for servers in self.servers.values():
for _, server in servers:
server.request_shutdown()
for servers in self.servers.values():
for _, server in servers:
server.wait()
- for cm in self.env_extras_cms:
- cm.__exit__(exc_type, exc_val, exc_tb)
+ self._stack.__exit__(exc_type, exc_val, exc_tb)
self.env_extras_cms = None
- self.cache_manager.__exit__(exc_type, exc_val, exc_tb)
- self.stash.__exit__()
- self.config_ctx.__exit__(exc_type, exc_val, exc_tb)
- self.server_logging_ctx.__exit__(exc_type, exc_val, exc_tb)
-
+ @contextlib.contextmanager
def ignore_interrupts(self):
- signal.signal(signal.SIGINT, signal.SIG_IGN)
-
- def process_interrupts(self):
- signal.signal(signal.SIGINT, signal.SIG_DFL)
+ prev_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
+ try:
+ yield
+ finally:
+ signal.signal(signal.SIGINT, prev_handler)
def build_config(self):
override_path = os.path.join(serve_path(self.test_paths), "config.json")
@@ -331,7 +330,11 @@ class TestdriverLoader:
return self._handler(request, response)
-def wait_for_service(logger, host, port, timeout=60, server_process=None):
+def wait_for_service(logger: StructuredLogger,
+ host: str,
+ port: int,
+ timeout: float = 60,
+ server_process: Optional[mozprocess.ProcessHandler] = None) -> bool:
"""Waits until network service given as a tuple of (host, port) becomes
available, `timeout` duration is reached, or the `server_process` exits at
which point ``socket.error`` is raised."""
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/testloader.py b/tests/wpt/tests/tools/wptrunner/wptrunner/testloader.py
index aa266548d7e..098e443b5ce 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/testloader.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/testloader.py
@@ -530,6 +530,7 @@ def get_test_queue_builder(**kwargs: Any) -> Tuple[TestQueueBuilder, Mapping[str
TestGroup = namedtuple("TestGroup", ["group", "subsuite", "test_type", "metadata"])
+GroupMetadata = Mapping[str, Any]
class TestQueueBuilder:
@@ -573,7 +574,7 @@ class TestQueueBuilder:
def tests_by_group(self, tests_by_type: TestsByType) -> Mapping[str, List[str]]:
pass
- def group_metadata(self, state: Mapping[str, Any]) -> Mapping[str, Any]:
+ def group_metadata(self, state: Mapping[str, Any]) -> GroupMetadata:
return {"scope": "/"}
def process_count(self, requested_processes: int, num_test_groups: int) -> int:
@@ -654,7 +655,7 @@ class PathGroupedSource(TestQueueBuilder):
groups[group_name].append(test.id)
return groups
- def group_metadata(self, state: Mapping[str, Any]) -> Mapping[str, Any]:
+ def group_metadata(self, state: Mapping[str, Any]) -> GroupMetadata:
return {"scope": "/%s" % "/".join(state["prev_group_key"][2])}
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py b/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py
index 28d06f88ee1..70da22f5b7a 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py
@@ -773,6 +773,8 @@ class TestRunnerManager(threading.Thread):
test.max_assertion_count)
file_result.extra["test_timeout"] = test.timeout * self.executor_kwargs['timeout_multiplier']
+ if self.browser.browser_pid:
+ file_result.extra["browser_pid"] = self.browser.browser_pid
self.logger.test_end(test.id,
status,
diff --git a/tests/wpt/tests/tools/wptserve/wptserve/constants.py b/tests/wpt/tests/tools/wptserve/wptserve/constants.py
index 584f2cc1c70..292f0085dc2 100644
--- a/tests/wpt/tests/tools/wptserve/wptserve/constants.py
+++ b/tests/wpt/tests/tools/wptserve/wptserve/constants.py
@@ -25,7 +25,6 @@ content_types = utils.invert_dict({
"text/plain": ["txt", "md"],
"text/vtt": ["vtt"],
"video/mp4": ["mp4", "m4v"],
- "video/ogg": ["ogg", "ogv"],
"video/webm": ["webm"],
})
diff --git a/tests/wpt/tests/tools/wptserve/wptserve/utils.py b/tests/wpt/tests/tools/wptserve/wptserve/utils.py
index e711e40725b..403c359d27f 100644
--- a/tests/wpt/tests/tools/wptserve/wptserve/utils.py
+++ b/tests/wpt/tests/tools/wptserve/wptserve/utils.py
@@ -141,6 +141,7 @@ def is_bad_port(port: int) -> bool:
2049, # nfs
3659, # apple-sasl
4045, # lockd
+ 4190, # sieve
5060, # sip
5061, # sips
6000, # x11
@@ -150,6 +151,7 @@ def is_bad_port(port: int) -> bool:
6667, # irc (default)
6668, # irc (alternate)
6669, # irc (alternate)
+ 6679, # osaut
6697, # irc+tls
10080, # amanda
]
diff --git a/tests/wpt/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html b/tests/wpt/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html
index 2ad47555a92..40310771e4d 100644
--- a/tests/wpt/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html
+++ b/tests/wpt/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html
@@ -19,7 +19,14 @@
setInterval(script);
}, "window.setInterval assigned via policy (successful Script transformation).");
- trustedTypes.createPolicy("default", {createScript: s => "window." + s + ".done()"});
+ trustedTypes.createPolicy("default", {createScript: (s, _, sink) => {
+ if (s === "timeoutStringTest") {
+ assert_equals(sink, 'Window setTimeout');
+ } else if (s === "intervalStringTest") {
+ assert_equals(sink, 'Window setInterval');
+ }
+ return "window." + s + ".done()";
+ }});
async_test(t => {
window.timeoutStringTest = t;
diff --git a/tests/wpt/tests/trusted-types/WEB_FEATURES.yml b/tests/wpt/tests/trusted-types/WEB_FEATURES.yml
new file mode 100644
index 00000000000..6ea31f1ef8d
--- /dev/null
+++ b/tests/wpt/tests/trusted-types/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: trusted-types
+ files: "**"
diff --git a/tests/wpt/tests/uievents/mouse/mouse_boundary_events_after_reappending_last_over_target.tentative.html b/tests/wpt/tests/uievents/mouse/mouse_boundary_events_after_reappending_last_over_target.tentative.html
new file mode 100644
index 00000000000..f75404ad782
--- /dev/null
+++ b/tests/wpt/tests/uievents/mouse/mouse_boundary_events_after_reappending_last_over_target.tentative.html
@@ -0,0 +1,152 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Temporary removal of "mouseover" target should not be considered as the last over element is changed</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-actions.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script>
+"use strict";
+
+function stringifyEvents(eventArray) {
+ if (!eventArray.length) {
+ return "[]";
+ }
+ let result = "";
+ eventArray.forEach(event => {
+ if (result != "") {
+ result += ", ";
+ }
+ result += `${event.type}@${
+ event.target?.nodeType == Node.ELEMENT_NODE
+ ? `${event.target.localName}${
+ event.target.id ? `#${event.target.id}` : ""
+ }`
+ : event.target?.localName
+ }`;
+ });
+ return result;
+}
+
+addEventListener("load", () => {
+ function promiseTick() {
+ return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+ }
+
+ function append3NestedDivElementsToBody() {
+ const div1 = document.createElement("div");
+ div1.setAttribute("id", "grandparent");
+ div1.setAttribute("style", "width: 32px; height: 32px; margin: 32px");
+ const div2 = document.createElement("div");
+ div2.setAttribute("id", "parent");
+ div2.setAttribute("style", "width: 32px; height: 32px");
+ const div3 = document.createElement("div");
+ div3.setAttribute("id", "child");
+ div3.setAttribute("style", "width: 32px; height: 32px");
+ div1.appendChild(div2);
+ div2.appendChild(div3);
+ document.body.appendChild(div1);
+ return { div1, div2, div3 };
+ }
+
+ promise_test(async () => {
+ const {div1, div2, div3} = append3NestedDivElementsToBody();
+ const bodyRect = document.body.getBoundingClientRect();
+ const div3Rect = div3.getBoundingClientRect();
+ let events = [];
+ for (const type of ["mouseenter", "mouseleave", "mouseover", "mouseout", "mousemove"]) {
+ for (const node of [document.body, div1, div2, div3]) {
+ node.addEventListener(type, event => {
+ if (event.target == node) {
+ events.push({type: event.type, target: event.target});
+ }
+ }, {capture: true});
+ }
+ }
+ let firstMouseOver = true;
+ div3.addEventListener("mouseover", event => {
+ // Temporarily remove div3 from div2, but append to the same position.
+ div2.appendChild(div3);
+ events = [];
+ events.push({type: event.type, target: event.target});
+ }, {once: true});
+ await new test_driver.Actions()
+ .pointerMove(1, 1, {})
+ .pointerMove(div3Rect.x + 10, div3Rect.y + 10, {})
+ .send();
+ const expectedEvents = [
+ { type: "mouseover", target: div3 },
+ { type: "mouseenter", target: document.body },
+ { type: "mouseenter", target: div1 },
+ { type: "mouseenter", target: div2 },
+ { type: "mouseenter", target: div3 },
+ { type: "mousemove", target: div3 },
+ // Removing the node temporarily should not cause mouse boundary events.
+ ];
+ assert_equals(
+ stringifyEvents(events),
+ stringifyEvents(expectedEvents),
+ );
+ div1.remove();
+ await new test_driver.Actions()
+ .pointerMove(1, 1, {})
+ .send();
+ },
+ "After re-appending the last over element at mouseover, " +
+ "mouse boundary events should be fired as just over on the target");
+
+ promise_test(async () => {
+ const {div1, div2, div3} = append3NestedDivElementsToBody();
+ const bodyRect = document.body.getBoundingClientRect();
+ const div3Rect = div3.getBoundingClientRect();
+ let events = [];
+ for (const type of ["mouseenter", "mouseleave", "mouseover", "mouseout", "mousemove"]) {
+ for (const node of [document.body, div1, div2, div3]) {
+ node.addEventListener(type, event => {
+ if (event.target == node) {
+ events.push({type: event.type, target: event.target});
+ }
+ }, {capture: true});
+ }
+ }
+ div3.addEventListener("mouseover", event => {
+ div3.addEventListener("mouseenter", () => {
+ // Temporarily remove div3 from div2, but append to the same position.
+ div2.appendChild(div3);
+ }, {once: true});
+ events = [];
+ events.push({type: event.type, target: event.target});
+ }, {once: true});
+ await new test_driver.Actions()
+ .pointerMove(1, 1, {})
+ .pointerMove(div3Rect.x + 10, div3Rect.y + 10, {})
+ .send();
+ const expectedEvents = [
+ { type: "mouseover", target: div3 },
+ { type: "mouseenter", target: document.body },
+ { type: "mouseenter", target: div1 },
+ { type: "mouseenter", target: div2 },
+ { type: "mouseenter", target: div3 },
+ { type: "mousemove", target: div3 },
+ // Removing the node temporarily should not cause mouse boundary events.
+ ];
+ assert_equals(
+ stringifyEvents(events),
+ stringifyEvents(expectedEvents),
+ );
+ div1.remove();
+ await new test_driver.Actions()
+ .pointerMove(1, 1, {})
+ .send();
+ },
+ "After re-appending the last over element at mouseenter, " +
+ "mouse boundary events should not be fired");
+}, {once: true});
+</script>
+</head>
+<body style="padding-top: 32px"></body>
+</html>
diff --git a/tests/wpt/tests/url/resources/urltestdata.json b/tests/wpt/tests/url/resources/urltestdata.json
index 9f1be0449c6..b9a199daab6 100644
--- a/tests/wpt/tests/url/resources/urltestdata.json
+++ b/tests/wpt/tests/url/resources/urltestdata.json
@@ -3590,6 +3590,34 @@
"search": "",
"hash": ""
},
+ {
+ "input": "file:.",
+ "base": null,
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
+ {
+ "input": "file:.",
+ "base": "http://www.example.com/test",
+ "href": "file:///",
+ "protocol": "file:",
+ "username": "",
+ "password": "",
+ "host": "",
+ "hostname": "",
+ "port": "",
+ "pathname": "/",
+ "search": "",
+ "hash": ""
+ },
"# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/host.html",
"Basic canonicalization, uppercase should be converted to lowercase",
{
diff --git a/tests/wpt/tests/visual-viewport/WEB_FEATURES.yml b/tests/wpt/tests/visual-viewport/WEB_FEATURES.yml
new file mode 100644
index 00000000000..4eecec544c9
--- /dev/null
+++ b/tests/wpt/tests/visual-viewport/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: visual-viewport
+ files: "**"
diff --git a/tests/wpt/tests/wasm/jsapi/module/moduleSource.tentative.any.js b/tests/wpt/tests/wasm/jsapi/module/moduleSource.tentative.any.js
index a3d09d55d6e..8a94cdd48cd 100644
--- a/tests/wpt/tests/wasm/jsapi/module/moduleSource.tentative.any.js
+++ b/tests/wpt/tests/wasm/jsapi/module/moduleSource.tentative.any.js
@@ -9,6 +9,9 @@ setup(() => {
test(() => {
assert_equals(typeof AbstractModuleSource, "undefined");
+}, "AbstractModuleSource is not a global");
+
+test(() => {
const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
assert_equals(AbstractModuleSource.name, "AbstractModuleSource");
assert_not_equals(AbstractModuleSource, Function);
@@ -16,22 +19,13 @@ test(() => {
test(() => {
const AbstractModuleSourceProto = Object.getPrototypeOf(WebAssembly.Module.prototype);
- assert_not_equals(AbstractModuleSourceProto, Object);
+ assert_not_equals(AbstractModuleSourceProto, Object.prototype);
const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
assert_equals(AbstractModuleSource.prototype, AbstractModuleSourceProto);
}, "AbstractModuleSourceProto intrinsic");
test(() => {
- const builder = new WasmModuleBuilder();
-
- builder
- .addFunction("fn", kSig_v_v)
- .addBody([])
- .exportFunc();
- builder.addMemory(0, 256, true);
-
- const buffer = builder.toBuffer()
- const module = new WebAssembly.Module(buffer);
+ const module = new WebAssembly.Module(emptyModuleBinary);
const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
const toStringTag = Object.getOwnPropertyDescriptor(AbstractModuleSource.prototype, Symbol.toStringTag).get;
diff --git a/tests/wpt/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js b/tests/wpt/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js
index c7a4f0d437c..e1c2703899a 100644
--- a/tests/wpt/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js
+++ b/tests/wpt/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js
@@ -1,3 +1,10 @@
import source modSource from "./worker.wasm";
+import { pm } from "./worker-helper.js";
assert_true(modSource instanceof WebAssembly.Module);
assert_true(await import.source("./worker.wasm") === modSource);
+
+await WebAssembly.instantiate(modSource, {
+ "./worker-helper.js": {
+ "pm": pm
+ }
+}); \ No newline at end of file
diff --git a/tests/wpt/tests/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html b/tests/wpt/tests/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html
new file mode 100644
index 00000000000..07faf9a5a1c
--- /dev/null
+++ b/tests/wpt/tests/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>script-src allows Wasm execution</title>
+<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Importing a WebAssembly module should be allowed by script-src CSP.");
+
+ window.log = [];
+ document.addEventListener("securitypolicyviolation", (e) => {
+ window.log.push(e.violatedDirective);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["executed"]);
+ }));
+</script>
+<script type="module" src="./resources/execute-start.wasm"></script>
diff --git a/tests/wpt/tests/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.html b/tests/wpt/tests/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.html
new file mode 100644
index 00000000000..c1232730eab
--- /dev/null
+++ b/tests/wpt/tests/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>script-src blocks Wasm execution</title>
+<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline';">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Importing a WebAssembly module should be guarded by script-src CSP.");
+
+ window.log = [];
+ document.addEventListener("securitypolicyviolation", (e) => {
+ window.log.push(e.violatedDirective);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["script-src-elem"]);
+ }));
+</script>
+<script type="module" src="./resources/execute-start.wasm"></script>
diff --git a/tests/wpt/tests/wasm/webapi/esm-integration/source-phase.tentative.html b/tests/wpt/tests/wasm/webapi/esm-integration/source-phase.tentative.html
index eb415a4c6f1..870b16bd0a0 100644
--- a/tests/wpt/tests/wasm/webapi/esm-integration/source-phase.tentative.html
+++ b/tests/wpt/tests/wasm/webapi/esm-integration/source-phase.tentative.html
@@ -13,7 +13,7 @@ const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
assert_equals(AbstractModuleSource.name, "AbstractModuleSource");
assert_true(exportedNamesSource instanceof AbstractModuleSource);
-assert_array_equals(WebAssembly.Module.exports(exportedNamesSource).sort(),
+assert_array_equals(WebAssembly.Module.exports(exportedNamesSource).map(({ name }) => name).sort(),
["func", "glob", "mem", "tab"]);
const wasmImportFromWasmSource = await import.source("./resources/wasm-import-from-wasm.wasm");
diff --git a/tests/wpt/tests/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html b/tests/wpt/tests/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html
new file mode 100644
index 00000000000..e193f3efc69
--- /dev/null
+++ b/tests/wpt/tests/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>Testing import of WebAssembly source phase from JavaScript worker</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+const worker = new Worker("resources/worker-source-phase.js", { type: "module" });
+worker.onmessage = (msg) => {
+ assert_equals(msg.data, 42);
+ done();
+}
+</script>
diff --git a/tests/wpt/tests/webdriver/tests/classic/element_click/scroll_into_view.py b/tests/wpt/tests/webdriver/tests/classic/element_click/scroll_into_view.py
index 591847e8816..041f0dee6a3 100644
--- a/tests/wpt/tests/webdriver/tests/classic/element_click/scroll_into_view.py
+++ b/tests/wpt/tests/webdriver/tests/classic/element_click/scroll_into_view.py
@@ -31,9 +31,9 @@ def test_scroll_into_view(session, inline):
assert session.execute_script("""
let input = arguments[0];
rect = input.getBoundingClientRect();
- return rect["top"] >= 0 && rect["left"] >= 0 &&
- (rect["top"] + rect["height"]) <= window.innerHeight &&
- (rect["left"] + rect["width"]) <= window.innerWidth;
+ return rect.top >= 0 && rect.left >= 0 &&
+ Math.floor(rect.bottom) <= window.innerHeight &&
+ Math.floor(rect.right) <= window.innerWidth;
""", args=(element,)) is True
@@ -69,4 +69,4 @@ def test_partially_visible_does_not_scroll(session, offset, inline):
assert_success(response)
assert session.execute_script("return window.scrollY || document.documentElement.scrollTop") == 0
click_point = assert_one_click(session)
- assert click_point == center_point(target)
+ assert click_point == pytest.approx(center_point(target), abs=1.0)
diff --git a/tests/wpt/tests/webmidi/WEB_FEATURES.yml b/tests/wpt/tests/webmidi/WEB_FEATURES.yml
new file mode 100644
index 00000000000..76ddf996a24
--- /dev/null
+++ b/tests/wpt/tests/webmidi/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: web-midi
+ files: "**"
diff --git a/tests/wpt/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js b/tests/wpt/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js
new file mode 100644
index 00000000000..42b123a97e5
--- /dev/null
+++ b/tests/wpt/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js
@@ -0,0 +1,50 @@
+// META: title=ensure WebNN MLContext.compute() rejecting detached buffers
+// META: global=window,dedicatedworker
+// META: script=../resources/utils_validation.js
+
+// These tests are used to reproduce the Chromium issue:
+// https://issues.chromium.org/issues/332002364
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.input('b', {dataType: 'float32', dimensions: [2]});
+ const c = builder.add(a, b);
+ const graph = await builder.build({c});
+ const arraybuffer = new ArrayBuffer(100);
+ const aBuffer = new Float32Array(arraybuffer, 0, 2);
+ const bBuffer = new Float32Array(arraybuffer, 8, 2);
+ const cBuffer = new Float32Array(2);
+ const promise = context.compute(graph, {'a': aBuffer, 'b': bBuffer}, {'c': cBuffer});
+ promise_rejects_js(t, TypeError, promise);
+ }, 'Throw if two input ArrayBufferViews sharing the same ArrayBuffer');
+
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const [b, c] = builder.split(a, 2);
+ const graph = await builder.build({b, c});
+ const aBuffer = new Float32Array(2);
+ const arraybuffer = new ArrayBuffer(100);
+ const bBuffer = new Float32Array(arraybuffer, 0, 1);
+ const cBuffer = new Float32Array(arraybuffer, 4, 1);
+ const promise = context.compute(graph, {'a': aBuffer}, {'b': bBuffer, 'c': cBuffer});
+ promise_rejects_js(t, TypeError, promise);
+}, 'Throw if two output ArrayBufferViews sharing the same ArrayBuffer');
+
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.relu(a);
+ const graph = await builder.build({b});
+ const arraybuffer = new ArrayBuffer(100);
+ const aBuffer = new Float32Array(arraybuffer, 0, 2);
+ const bBuffer = new Float32Array(arraybuffer, 8, 2);
+ const promise = context.compute(graph, {'a': aBuffer}, {'b': bBuffer});
+ promise_rejects_js(t, TypeError, promise);
+}, 'Throw if input and output ArrayBufferViews sharing the same ArrayBuffer');
+
+promise_test(async t => {
+ const a = builder.input('a', {dataType: 'float32', dimensions: [2]});
+ const b = builder.relu(a);
+ const graph = await builder.build({b});
+ const buffer = new Float32Array(2);
+ const promise = context.compute(graph, {'a': buffer}, {'b': buffer});
+ promise_rejects_js(t, TypeError, promise);
+}, 'Throw if input and output are the same ArrayBufferView');
diff --git a/tests/wpt/tests/webnn/validation_tests/gruCell.https.any.js b/tests/wpt/tests/webnn/validation_tests/gruCell.https.any.js
index 5056f8be68c..3cd9d32b073 100644
--- a/tests/wpt/tests/webnn/validation_tests/gruCell.https.any.js
+++ b/tests/wpt/tests/webnn/validation_tests/gruCell.https.any.js
@@ -14,6 +14,8 @@ const kValidHiddenStateDimensions = [batchSize, hiddenSize];
// Dimensions required of optional inputs.
const kValidBiasDimensions = [3 * hiddenSize];
const kValidRecurrentBiasDimensions = [3 * hiddenSize];
+// Dimensions required of required output.
+const kValidOutputDimensions = [batchSize, hiddenSize];
// Example descriptors which are valid according to the above dimensions.
const kExampleInputDescriptor = {
@@ -40,6 +42,320 @@ const kExampleRecurrentBiasDescriptor = {
dataType: 'float32',
dimensions: kValidRecurrentBiasDimensions
};
+const kExampleOutputDescriptor = {
+ dataType: 'float32',
+ dimensions: kValidOutputDimensions
+ };
+
+const tests = [
+ {
+ name: '[gruCell] Test with default options',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ output: kExampleOutputDescriptor
+ },
+ {
+ name: '[gruCell] Test with given options',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: {
+ bias: kExampleBiasDescriptor,
+ recurrentBias: kExampleRecurrentBiasDescriptor,
+ restAfter: true,
+ layout: 'rzn',
+ activations: ['sigmoid', 'relu']
+ },
+ output: kExampleOutputDescriptor
+ },
+ {
+ name: '[gruCell] Throw if hiddenSize equals to zero',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: 0
+ },
+ {
+ name: '[gruCell] Throw if hiddenSize is too large',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: 4294967295,
+ },
+ {
+ name:
+ '[gruCell] Throw if the data type of the inputs is not one of the floating point types',
+ input: { dataType: 'uint32', dimensions: kValidInputDimensions },
+ weight: { dataType: 'uint32', dimensions: kValidWeightDimensions },
+ recurrentWeight: {
+ dataType: 'uint32',
+ dimensions: kValidRecurrentWeightDimensions
+ },
+ hiddenState: {
+ dataType: 'uint32',
+ dimensions: kValidHiddenStateDimensions
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of input is not 2',
+ input: { dataType: 'float32', dimensions: [batchSize] },
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the input.dimensions[1] is incorrect',
+ input: { dataType: 'float32', dimensions: [inputSize, inputSize] },
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if data type of weight is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'int8',
+ dimensions: [3 * hiddenSize, inputSize]
+ },
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if rank of weight is not 2',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'float32',
+ dimensions: [3 * hiddenSize]
+ },
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if weight.dimensions[0] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: {
+ dataType: 'float32',
+ dimensions: [4 * hiddenSize, inputSize]
+ },
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name: '[gruCell] Throw if data type of recurrentWeight is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: {
+ dataType: 'int32',
+ dimensions: [3 * hiddenSize, hiddenSize]
+ },
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of recurrentWeight is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ { dataType: 'float32', dimensions: [3 * hiddenSize] },
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the recurrentWeight.dimensions is invalid',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ { dataType: 'float32', dimensions: [4 * hiddenSize, inputSize] },
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if data type of hiddenState is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ kExampleRecurrentWeightDescriptor,
+ hiddenState: {
+ dataType: 'uint32',
+ dimensions: [batchSize, hiddenSize]
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of hiddenState is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight:
+ kExampleRecurrentWeightDescriptor,
+ hiddenState: {
+ dataType: 'float32',
+ dimensions: [hiddenSize]
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the hiddenState.dimensions is invalid',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: {
+ dataType: 'float32',
+ dimensions: [batchSize, 3 * hiddenSize]
+ },
+ hiddenSize: hiddenSize
+ },
+ {
+ name:
+ '[gruCell] Throw if the size of options.activations is not 2',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { activations: ['sigmoid', 'tanh', 'relu'] }
+ },
+ {
+ name:
+ '[gruCell] Throw if data type of options.bias is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { bias: { dataType: 'uint8', dimensions: [3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of options.bias is not 1',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { bias: { dataType: 'float32', dimensions: [batchSize, 3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if options.bias.dimensions[0] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { bias: { dataType: 'float32', dimensions: [2 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if data type of options.recurrentBias is not one of the floating point types',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { recurrentBias: { dataType: 'int8', dimensions: [3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if the rank of options.recurrentBias is not 1',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: { recurrentBias: { dataType: 'float32', dimensions: [batchSize, 3 * hiddenSize] } }
+ },
+ {
+ name:
+ '[gruCell] Throw if options.recurrentBias.dimensions[0] is not 3 * hiddenSize',
+ input: kExampleInputDescriptor,
+ weight: kExampleWeightDescriptor,
+ recurrentWeight: kExampleRecurrentWeightDescriptor,
+ hiddenState: kExampleHiddenStateDescriptor,
+ hiddenSize: hiddenSize,
+ options: {
+ recurrentBias: { dataType: 'float16', dimensions: [4 * hiddenSize] }
+ }
+ }
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ { dataType: test.input.dataType, dimensions: test.input.dimensions });
+ const weight = builder.input(
+ 'weight',
+ { dataType: test.weight.dataType, dimensions: test.weight.dimensions });
+ const recurrentWeight = builder.input('recurrentWeight', {
+ dataType: test.recurrentWeight.dataType,
+ dimensions: test.recurrentWeight.dimensions
+ });
+ const hiddenState = builder.input('hiddenState', {
+ dataType: test.hiddenState.dataType,
+ dimensions: test.hiddenState.dimensions
+ });
+
+ const options = {};
+ if (test.options) {
+ if (test.options.bias) {
+ options.bias = builder.input('bias', {
+ dataType: test.options.bias.dataType,
+ dimensions: test.options.bias.dimensions
+ });
+ }
+ if (test.options.recurrentBias) {
+ options.bias = builder.input('recurrentBias', {
+ dataType: test.options.recurrentBias.dataType,
+ dimensions: test.options.recurrentBias.dimensions
+ });
+ }
+ if (test.options.resetAfter) {
+ options.resetAfter = test.options.resetAfter;
+ }
+ if (test.options.layout) {
+ options.layout = test.options.layout;
+ }
+ if (test.options.activations) {
+ options.activations = [];
+ test.options.activations.forEach(
+ activation => options.activations.push(builder[activation]()));
+ }
+ }
+
+ if (test.output) {
+ const output = builder.gruCell(
+ input, weight, recurrentWeight, hiddenState, test.hiddenSize,
+ options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError,
+ () => builder.gruCell(
+ input, weight, recurrentWeight, hiddenState, test.hiddenSize,
+ options));
+ }
+ }, test.name));
multi_builder_test(async (t, builder, otherBuilder) => {
const inputFromOtherBuilder =
diff --git a/tests/wpt/tests/webnn/validation_tests/pooling.https.any.js b/tests/wpt/tests/webnn/validation_tests/pooling.https.any.js
index 02d6c4c7bac..e8add0511f9 100644
--- a/tests/wpt/tests/webnn/validation_tests/pooling.https.any.js
+++ b/tests/wpt/tests/webnn/validation_tests/pooling.https.any.js
@@ -10,3 +10,266 @@ kPoolingOperators.forEach((operatorName) => {
validateInputFromAnotherBuilder(
operatorName, {dataType: 'float32', dimensions: [2, 2, 2, 2]});
});
+
+
+const tests = [
+ {
+ name: 'Test pool2d with default options.',
+ input: {dataType: 'float32', dimensions: [1, 3, 4, 4]},
+ output: {dataType: 'float32', dimensions: [1, 3, 1, 1]}
+ },
+ {
+ name: 'Test pool2d with windowDimensions',
+ input: {dataType: 'float32', dimensions: [1, 3, 4, 4]},
+ options: {
+ windowDimensions: [3, 3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 2, 2]}
+ },
+ {
+ name: 'Test pool2d with padding.',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ windowDimensions: [5, 5],
+ padding: [2, 2, 2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 5, 5]}
+ },
+ {
+ name: 'Test pool2d with strides.',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ windowDimensions: [2, 2],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 2, 2]}
+ },
+ {
+ name: 'Test pool2d with strides and padding.',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ windowDimensions: [3, 3],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides and asymmetric padding.',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [2, 1, 2, 1],
+ strides: [2, 2],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 4, 4]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and roundingType="floor".',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ roundingType: 'floor',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and roundingType="ceil".',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ roundingType: 'ceil',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 4, 4]}
+ },
+ {
+ name: 'Test pool2d with explicit outputSizes ignored roundingType',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ roundingType: 'ceil',
+ outputSizes: [3, 3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and outputSizes=[3, 3].',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ outputSizes: [3, 3],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with strides, padding and outputSizes=[4, 4].',
+ input: {dataType: 'float32', dimensions: [1, 3, 7, 7]},
+ options: {
+ windowDimensions: [4, 4],
+ padding: [1, 1, 1, 1],
+ strides: [2, 2],
+ outputSizes: [4, 4],
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 4, 4]}
+ },
+ {
+ name: 'Test pool2d with layout="nchw".',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [3, 3],
+ layout: 'nchw',
+ },
+ output: {dataType: 'float32', dimensions: [1, 2, 3, 3]}
+ },
+ {
+ name: 'Test pool2d with layout="nhwc".',
+ input: {dataType: 'float32', dimensions: [1, 5, 5, 2]},
+ options: {
+ windowDimensions: [3, 3],
+ layout: 'nhwc',
+ },
+ output: {dataType: 'float32', dimensions: [1, 3, 3, 2]}
+ },
+ {
+ name: 'Throw if the input is not a 4-D tensor.',
+ input: {dataType: 'float32', dimensions: [1, 5, 5]},
+ },
+ {
+ name: 'Throw if the output sizes is incorrect.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [2, 2],
+ padding: [2, 2, 2, 2],
+ strides: [2, 2],
+ outputSizes: [3, 3],
+ },
+ },
+ {
+ name: 'Throw if the length of output sizes is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [2, 2],
+ padding: [2, 2, 2, 2],
+ strides: [2, 2],
+ outputSizes: [1, 2, 4, 4],
+ },
+ },
+ {
+ name: 'Throw if the length of window dimensions is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [1, 1, 1, 1],
+ },
+ },
+ {
+ name: 'Throw if any window dimension is lesser than 1.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [0, 2],
+ },
+ },
+ {
+ name:
+ 'Throw if the input height is too small to fill the pool window height.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [8, 2],
+ },
+ },
+ {
+ name:
+ 'Throw if the input width is too small to fill the pool window width.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [2, 8],
+ },
+ },
+ {
+ name: 'Throw if the calculated output height is equal to 0.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [6, 3],
+ },
+ },
+ {
+ name: 'Throw if the calculated output width is equal to 0.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ windowDimensions: [3, 6],
+ },
+ },
+ {
+ name: 'Throw if the length of padding is not 4.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ padding: [2, 2],
+ },
+ },
+ {
+ name: 'Throw if the length of strides is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ strides: [2],
+ },
+ },
+ {
+ name: 'Throw if one stride value is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ strides: [0, 2],
+ },
+ },
+ {
+ name: 'Throw if the length of dilations is not 2.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ dilations: [1, 1, 2],
+ },
+ },
+ {
+ name: 'Throw if one dilation value is smaller than 1.',
+ input: {dataType: 'float32', dimensions: [1, 2, 5, 5]},
+ options: {
+ dilations: [1, 0],
+ },
+ },
+ {
+ name: 'Throw if the padding height value is too large',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ padding: [kMaxUnsignedLong, kMaxUnsignedLong, 0, 0],
+ },
+ },
+ {
+ name: 'Throw if the padding width value is too large',
+ input: {dataType: 'float32', dimensions: [1, 3, 5, 5]},
+ options: {
+ padding: [0, 0, kMaxUnsignedLong, kMaxUnsignedLong],
+ },
+ },
+];
+
+tests.forEach(
+ test => promise_test(async t => {
+ const input = builder.input(
+ 'input',
+ {dataType: test.input.dataType, dimensions: test.input.dimensions});
+ kPoolingOperators.forEach((operatorName) => {
+ if (test.output) {
+ const output = builder[operatorName](input, test.options);
+ assert_equals(output.dataType(), test.output.dataType);
+ assert_array_equals(output.shape(), test.output.dimensions);
+ } else {
+ assert_throws_js(
+ TypeError, () => builder[operatorName](input, test.options));
+ }
+ });
+ }, test.name));
diff --git a/tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk-worker.js b/tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk-worker.js
new file mode 100644
index 00000000000..cc9dee42ac9
--- /dev/null
+++ b/tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk-worker.js
@@ -0,0 +1,13 @@
+onrtctransform = async (event) => {
+ const { port } = event.transformer.options;
+ port.postMessage("started");
+
+ const reader = event.transformer.readable.getReader();
+ const writer = event.transformer.writable.getWriter();
+
+ const { done, value } = await reader.read();
+
+ writer.write(null).catch(err => port.postMessage([err.name, 'null']));
+ writer.write(value).catch(err => port.postMessage([err.name, value.constructor.name]));
+};
+self.postMessage('registered');
diff --git a/tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html b/tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html
new file mode 100644
index 00000000000..a837f627f25
--- /dev/null
+++ b/tests/wpt/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src ="routines.js"></script>
+
+<video id="video1" autoplay></video>
+
+<script>
+promise_test(async (test) => {
+ const {sender, receiver, senderPc, receiverPc} = await createConnectionWithTransform(test, 'RTCRtpScriptTransform-bad-chunk-worker.js', {audio: true});
+
+ assert_array_equals(await getNextMessage(sender.transform.port), ["TypeError", "null"]);
+ assert_array_equals(await getNextMessage(sender.transform.port), ["TypeError", "RTCEncodedAudioFrame"]);
+}, "Writing bad chunks should error the stream");
+</script>
diff --git a/tests/wpt/tests/webrtc/RTCPeerConnection-GC.https.html b/tests/wpt/tests/webrtc/RTCPeerConnection-GC.https.html
index 156a2e1f093..282c362a2d4 100644
--- a/tests/wpt/tests/webrtc/RTCPeerConnection-GC.https.html
+++ b/tests/wpt/tests/webrtc/RTCPeerConnection-GC.https.html
@@ -85,6 +85,36 @@ promise_test(async t => {
await onVideoChange();
assert_not_equals(color, getVideoSignal(destVideo));
}, "GC does not collect a peer connection pipe rendering to a video element");
+
+promise_test(async t => {
+ const pc1 = new RTCPeerConnection();
+ t.add_cleanup(() => pc1.close());
+ const pc2 = new RTCPeerConnection();
+ t.add_cleanup(() => pc2.close());
+
+ const [track, stream] = await createTrackAndStreamWithCleanup(t, "video");
+ pc1.addTrack(track, stream);
+ exchangeIceCandidates(pc1, pc2);
+
+ const metadataToBeLoaded = [];
+ pc2.ontrack = (e) => {
+ const stream = e.streams[0];
+ const v = document.createElement('video');
+ v.autoplay = true;
+ v.srcObject = stream;
+ v.id = stream.id
+ metadataToBeLoaded.push(new Promise((resolve) => {
+ v.addEventListener('loadedmetadata', () => {
+ resolve();
+ });
+ }));
+ };
+ await exchangeOfferAnswer(pc1, pc2);
+
+ garbageCollect();
+
+ await Promise.all(metadataToBeLoaded);
+}, "GC does not collect an HTMLMediaElement playing a video track");
</script>
</body>
</html>
diff --git a/tests/wpt/tests/websockets/Create-blocked-port.any.js b/tests/wpt/tests/websockets/Create-blocked-port.any.js
index 2962312ff5f..b8e3e26445e 100644
--- a/tests/wpt/tests/websockets/Create-blocked-port.any.js
+++ b/tests/wpt/tests/websockets/Create-blocked-port.any.js
@@ -79,6 +79,7 @@ async_test(t => {
2049, // nfs
3659, // apple-sasl
4045, // lockd
+ 4190, // sieve
6000, // x11
6566, // sane-port
6665, // irc (alternate)
@@ -86,6 +87,7 @@ async_test(t => {
6667, // irc (default)
6668, // irc (alternate)
6669, // irc (alternate)
+ 6679, // osaut
6697, // irc+tls
10080, // amanda
].forEach(blockedPort => {
diff --git a/tests/wpt/tests/webtransport/datagram-bad-chunk.https.any.js b/tests/wpt/tests/webtransport/datagram-bad-chunk.https.any.js
new file mode 100644
index 00000000000..92601dec9a5
--- /dev/null
+++ b/tests/wpt/tests/webtransport/datagram-bad-chunk.https.any.js
@@ -0,0 +1,15 @@
+// META: global=window,worker
+// META: script=/common/get-host-info.sub.js
+// META: script=resources/webtransport-test-helpers.sub.js
+// META: script=/common/utils.js
+
+promise_test(async t => {
+ // Establish a WebTransport session.
+ const wt = new WebTransport(webtransport_url('echo.py'));
+ t.add_cleanup(() => wt.close());
+ await wt.ready;
+
+ const writer = wt.datagrams.writable.getWriter();
+ await promise_rejects_js(t, TypeError, writer.write("foo"));
+ await promise_rejects_js(t, TypeError, writer.write(new Uint8Array(0)));
+}, 'Datagram should reject when non-buffer-source data is written');
diff --git a/tests/wpt/tests/webtransport/sendstream-bad-chunk.https.any.js b/tests/wpt/tests/webtransport/sendstream-bad-chunk.https.any.js
new file mode 100644
index 00000000000..013390879bd
--- /dev/null
+++ b/tests/wpt/tests/webtransport/sendstream-bad-chunk.https.any.js
@@ -0,0 +1,18 @@
+// META: global=window,worker
+// META: script=/common/get-host-info.sub.js
+// META: script=resources/webtransport-test-helpers.sub.js
+// META: script=/common/utils.js
+
+promise_test(async t => {
+ // Establish a WebTransport session.
+ const wt = new WebTransport(webtransport_url('echo.py'));
+ t.add_cleanup(() => wt.close());
+ await wt.ready;
+
+ // Create a bidirectional stream
+ const {writable} = await wt.createBidirectionalStream();
+
+ const writer = writable.getWriter();
+ await promise_rejects_js(t, TypeError, writer.write("foo"));
+ await promise_rejects_js(t, TypeError, writer.write(new Uint8Array(0)));
+}, 'WebTransportSendStream should reject when non-buffer-source data is written');