Update web-platform-tests to revision a184aa4fd5cd8f92eb87ce0035f257f2a4c7c0b2

This commit is contained in:
WPT Sync Bot 2021-01-17 08:22:00 +00:00
parent e1065fa22a
commit c7e8937c37
84 changed files with 2653 additions and 218 deletions

View file

@ -4,7 +4,7 @@
expected: TIMEOUT expected: TIMEOUT
[Opening a blob URL in a new window immediately before revoking it works.] [Opening a blob URL in a new window immediately before revoking it works.]
expected: TIMEOUT expected: FAIL
[Fetching a blob URL immediately before revoking it works in an iframe.] [Fetching a blob URL immediately before revoking it works in an iframe.]
expected: FAIL expected: FAIL

View file

@ -1,4 +0,0 @@
[hit-test-floats-005.html]
[Miss clipped float]
expected: FAIL

View file

@ -309,15 +309,6 @@
[Response: combined response Content-Type: text/html;" \\" text/plain ";charset=GBK] [Response: combined response Content-Type: text/html;" \\" text/plain ";charset=GBK]
expected: NOTRUN expected: NOTRUN
[<iframe>: separate response Content-Type: text/html;" \\" text/plain]
expected: FAIL
[<iframe>: combined response Content-Type: */* text/html]
expected: FAIL
[<iframe>: combined response Content-Type: text/html;charset=gbk text/plain text/html]
expected: FAIL
[<iframe>: combined response Content-Type: text/html */*] [<iframe>: combined response Content-Type: text/html */*]
expected: FAIL expected: FAIL
@ -330,3 +321,9 @@
[<iframe>: combined response Content-Type: text/html */*;charset=gbk] [<iframe>: combined response Content-Type: text/html */*;charset=gbk]
expected: FAIL expected: FAIL
[<iframe>: combined response Content-Type: text/html;" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
expected: FAIL

View file

@ -56,6 +56,3 @@
[separate text/javascript x/x] [separate text/javascript x/x]
expected: FAIL expected: FAIL
[separate text/javascript ]
expected: FAIL

View file

@ -11,6 +11,9 @@
[X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!] [X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
expected: FAIL expected: FAIL
[Content-Type-Options%3A%20nosniff] [X-Content-Type-Options%3A%20%22nosniFF%22]
expected: FAIL
[X-Content-Type-Options%3A%20no%0D%0AX-Content-Type-Options%3A%20nosniff]
expected: FAIL expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_1.html]
[Multiple history traversals from the same task]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_4.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -0,0 +1,8 @@
[cross-origin-isolated.sub.https.html]
expected: TIMEOUT
[self: originAgentCluster must equal true]
expected: FAIL
[child: originAgentCluster must equal true]
expected: TIMEOUT

View file

@ -1,5 +1,5 @@
[iframe_sandbox_popups_escaping-1.html] [iframe_sandbox_popups_escaping-1.html]
expected: CRASH expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: FAIL expected: TIMEOUT

View file

@ -1,5 +1,4 @@
[iframe_sandbox_popups_escaping-3.html] [iframe_sandbox_popups_escaping-3.html]
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT expected: FAIL

View file

@ -1,5 +1,5 @@
[iframe_sandbox_popups_nonescaping-1.html] [iframe_sandbox_popups_nonescaping-1.html]
expected: TIMEOUT expected: CRASH
[Check that popups from a sandboxed iframe do not escape the sandbox] [Check that popups from a sandboxed iframe do not escape the sandbox]
expected: NOTRUN expected: NOTRUN

View file

@ -1,5 +1,5 @@
[iframe_sandbox_popups_nonescaping-2.html] [iframe_sandbox_popups_nonescaping-2.html]
expected: TIMEOUT expected: CRASH
[Check that popups from a sandboxed iframe do not escape the sandbox] [Check that popups from a sandboxed iframe do not escape the sandbox]
expected: NOTRUN expected: NOTRUN

View file

@ -0,0 +1,4 @@
[form-double-submit-2.html]
[preventDefault should allow onclick submit() to succeed]
expected: FAIL

View file

@ -0,0 +1,4 @@
[form-double-submit-3.html]
[<button> should have the same double-submit protection as <input type=submit>]
expected: FAIL

View file

@ -2,9 +2,6 @@
[input type search: setSelectionRange out of range a second time (must not fire select)] [input type search: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type text: selectionStart a second time (must not fire select)]
expected: FAIL
[textarea: selectionDirection a second time (must not fire select)] [textarea: selectionDirection a second time (must not fire select)]
expected: FAIL expected: FAIL
@ -17,30 +14,12 @@
[input type tel: selectionEnd out of range a second time (must not fire select)] [input type tel: selectionEnd out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type password: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type search: setRangeText() a second time (must not fire select)] [input type search: setRangeText() a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type url: selectionStart out of range a second time (must not fire select)] [input type url: selectionStart out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type password: selectionStart out of range a second time (must not fire select)]
expected: FAIL
[input type url: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL
[input type password: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL
[input type tel: setRangeText() a second time (must not fire select)]
expected: FAIL
[input type url: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type text: setSelectionRange out of range a second time (must not fire select)] [input type text: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
@ -50,30 +29,51 @@
[input type search: selectionStart out of range a second time (must not fire select)] [input type search: selectionStart out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type tel: selectionDirection a second time (must not fire select)]
expected: FAIL
[input type url: select() a second time (must not fire select)]
expected: FAIL
[input type url: selectionDirection a second time (must not fire select)]
expected: FAIL
[input type password: selectionEnd a second time (must not fire select)]
expected: FAIL
[input type search: selectionDirection a second time (must not fire select)]
expected: FAIL
[textarea: setSelectionRange out of range a second time (must not fire select)] [textarea: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type tel: select() a second time (must not fire select)] [input type password: select() a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type url: selectionStart a second time (must not fire select)] [input type text: selectionEnd a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type tel: selectionStart a second time (must not fire select)] [input type url: selectionEnd out of range a second time (must not fire select)]
expected: FAIL
[input type password: selectionEnd out of range a second time (must not fire select)]
expected: FAIL
[textarea: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type text: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type search: selectionEnd a second time (must not fire select)]
expected: FAIL
[input type password: setRangeText() a second time (must not fire select)]
expected: FAIL
[input type tel: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL
[input type text: selectionDirection a second time (must not fire select)]
expected: FAIL
[textarea: selectionStart out of range a second time (must not fire select)]
expected: FAIL
[input type url: setRangeText() a second time (must not fire select)]
expected: FAIL
[input type password: selectionDirection a second time (must not fire select)]
expected: FAIL
[textarea: selectionEnd out of range a second time (must not fire select)]
expected: FAIL
[input type search: selectionStart a second time (must not fire select)]
expected: FAIL expected: FAIL

View file

@ -1,4 +0,0 @@
[module-delayed.html]
[async document.write in a module]
expected: FAIL

View file

@ -0,0 +1,4 @@
[module-static-import-delayed.html]
[document.write in an imported module]
expected: FAIL

View file

@ -3,6 +3,3 @@
[The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document] [The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: TIMEOUT expected: TIMEOUT
[The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: FAIL

View file

@ -1,9 +1,10 @@
[promise-job-entry.html] [promise-job-entry.html]
expected: TIMEOUT
[Fulfillment handler on fulfilled promise] [Fulfillment handler on fulfilled promise]
expected: FAIL expected: FAIL
[Rejection handler on pending-then-rejected promise] [Rejection handler on pending-then-rejected promise]
expected: FAIL expected: TIMEOUT
[Sanity check: this all works as expected with no promises involved] [Sanity check: this all works as expected with no promises involved]
expected: FAIL expected: FAIL
@ -15,5 +16,5 @@
expected: FAIL expected: FAIL
[Fulfillment handler on pending-then-fulfilled promise] [Fulfillment handler on pending-then-fulfilled promise]
expected: FAIL expected: TIMEOUT

View file

@ -539,3 +539,9 @@
[X SNR (43.824040882292394 dB) is not greater than or equal to 65.737. Got 43.824040882292394.] [X SNR (43.824040882292394 dB) is not greater than or equal to 65.737. Got 43.824040882292394.]
expected: FAIL expected: FAIL
[X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[28696\]\t4.3013245373868222e+28\t9.3139332532882690e-1\t4.3013245373868222e+28\t4.6181612219179757e+28\t3.8985999999999999e-3\n\t[28697\]\t7.0477002859115601e-1\t9.0675884485244751e-1\t2.0198881626129150e-1\t2.2275913536212616e-1\t3.8985999999999999e-3\n\tMax AbsError of 4.3013245373868222e+28 at index of 28696.\n\tMax RelError of 4.6181612219179757e+28 at index of 28696.\n]
expected: FAIL
[X SNR (-529.2379582870841 dB) is not greater than or equal to 65.737. Got -529.2379582870841.]
expected: FAIL

View file

@ -1,5 +0,0 @@
[017.html]
expected: TIMEOUT
[origin of the script that invoked the method, about:blank]
expected: TIMEOUT

View file

@ -1,5 +0,0 @@
[017.html]
expected: TIMEOUT
[origin of the script that invoked the method, about:blank]
expected: TIMEOUT

View file

@ -0,0 +1,5 @@
[018.html]
expected: TIMEOUT
[origin of the script that invoked the method, javascript:]
expected: TIMEOUT

View file

@ -1,2 +0,0 @@
[Worker-constructor.html]
expected: ERROR

View file

@ -7,7 +7,7 @@
expected: FAIL expected: FAIL
[Opening a blob URL in a new window immediately before revoking it works.] [Opening a blob URL in a new window immediately before revoking it works.]
expected: TIMEOUT expected: FAIL
[Opening a blob URL in a noopener about:blank window immediately before revoking it works.] [Opening a blob URL in a noopener about:blank window immediately before revoking it works.]
expected: TIMEOUT expected: TIMEOUT

View file

@ -176521,6 +176521,45 @@
] ]
}, },
"css-scroll-snap": { "css-scroll-snap": {
"scroll-snap-root-001.html": [
"43028cb874d0e2b4899cb3248e20d43dd5930ee3",
[
null,
[
[
"/css/css-scroll-snap/scroll-snap-root-001-ref.html",
"=="
]
],
{}
]
],
"scroll-snap-root-002.html": [
"302c75634133de666d4262d1a94c9c9ae9bd7b7e",
[
null,
[
[
"/css/css-scroll-snap/scroll-snap-root-002-ref.html",
"=="
]
],
{}
]
],
"scroll-snap-root-003.html": [
"fc7b28fdf56f360f2ef963d54f4f205426f3ffc9",
[
null,
[
[
"/css/css-scroll-snap/no-red-ref.html",
"=="
]
],
{}
]
],
"scroll-target-align-001.html": [ "scroll-target-align-001.html": [
"eeda674e07c591cb1d17cce1c8f8bb460c45fbdf", "eeda674e07c591cb1d17cce1c8f8bb460c45fbdf",
[ [
@ -176717,6 +176756,19 @@
{} {}
] ]
], ],
"scroll-snap-writing-mode-000.html": [
"e5d3dd9358bdab8ed06e0d003a13863d2c42aa2d",
[
null,
[
[
"/css/css-scroll-snap/snap-after-initial-layout/scroll-snap-writing-mode-000-ref.html",
"=="
]
],
{}
]
],
"writing-mode-horizontal-tb.html": [ "writing-mode-horizontal-tb.html": [
"9a680d10d9ff61fc7173118bbfac4fae5b08ff97", "9a680d10d9ff61fc7173118bbfac4fae5b08ff97",
[ [
@ -259174,7 +259226,7 @@
}, },
"support": { "support": {
".azure-pipelines.yml": [ ".azure-pipelines.yml": [
"155143ef00592c713609a0e7d3e48ddd916b874e", "1c9883ccdec0532499b8df805527ea8e8c37c32f",
[] []
], ],
".codecov.yml": [ ".codecov.yml": [
@ -319163,6 +319215,18 @@
"df776353a31f1cef3abe9bc9d195da9be5da2510", "df776353a31f1cef3abe9bc9d195da9be5da2510",
[] []
], ],
"no-red-ref.html": [
"061454fb67a67b9b5b1d62b2603731da3988a3d9",
[]
],
"scroll-snap-root-001-ref.html": [
"88f028fb3952b394f6ff4a9548b0b41a26bad131",
[]
],
"scroll-snap-root-002-ref.html": [
"663b02b8c420593deda9634d47d44aa45a8596e2",
[]
],
"scroll-target-001-ref.html": [ "scroll-target-001-ref.html": [
"28b00184c2ef5f33f6a2e8927233f6f7f42b1973", "28b00184c2ef5f33f6a2e8927233f6f7f42b1973",
[] []
@ -319172,6 +319236,10 @@
"f3eaa06ac9b7c48479d439041ce51575ad8cc072", "f3eaa06ac9b7c48479d439041ce51575ad8cc072",
[] []
], ],
"scroll-snap-writing-mode-000-ref.html": [
"fe2d4074e26e0bb4417871be39fb37408aa2c498",
[]
],
"snap-after-initial-layout-ref.html": [ "snap-after-initial-layout-ref.html": [
"c8009b626cb63ebcef4a211f8b61249faf36c72f", "c8009b626cb63ebcef4a211f8b61249faf36c72f",
[] []
@ -335323,6 +335391,10 @@
] ]
}, },
"include": { "include": {
"editor-test-utils.js": [
"3ca014a472fa45991e38d565a10fe9dc9afa6ff8",
[]
],
"implementation.js": [ "implementation.js": [
"44a7afd82d25fdee8b023e3a84a650e4a134de0e", "44a7afd82d25fdee8b023e3a84a650e4a134de0e",
[] []
@ -340152,6 +340224,10 @@
[] []
], ],
"getter-special-cases": { "getter-special-cases": {
"cross-origin-isolated.sub.https.html.headers": [
"5f8621ef83660c66f0d037ea28fafefb558140f1",
[]
],
"csp-sandbox-no.https.html.headers": [ "csp-sandbox-no.https.html.headers": [
"4705ce9dedeeabf8208bf602176511c0cbe2cb76", "4705ce9dedeeabf8208bf602176511c0cbe2cb76",
[] []
@ -340264,6 +340340,14 @@
"9292fe3894e4a82f77a8877b76faa19fe6dfdb47", "9292fe3894e4a82f77a8877b76faa19fe6dfdb47",
[] []
], ],
"coep-frame.html": [
"7cbd89b943ffcae6b6038b1caf905783bbd4aab3",
[]
],
"coep-frame.html.headers": [
"4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
[]
],
"crashy-popup.sub.html": [ "crashy-popup.sub.html": [
"45c8d5074d22b3233d8bee4c6d1236e8899009e4", "45c8d5074d22b3233d8bee4c6d1236e8899009e4",
[] []
@ -340772,7 +340856,7 @@
[] []
], ],
"window-name.sub.html": [ "window-name.sub.html": [
"378f588e6b3ba0847a64795d1bca047ba6aa6b8a", "0fa874b2cfc2cfe6d1a0a79348857290215a3ef4",
[] []
], ],
"window-opener.html": [ "window-opener.html": [
@ -352321,7 +352405,7 @@
[] []
], ],
"input-events-get-target-ranges.js": [ "input-events-get-target-ranges.js": [
"f9404e07a5564c64a2863a1329728517fedd5ca7", "004416ec2a02e0abc254f9b2b7d0a0c41b751a7d",
[] []
] ]
}, },
@ -360752,6 +360836,10 @@
"8effa56c98c9f921201553121308591e7431a201", "8effa56c98c9f921201553121308591e7431a201",
[] []
], ],
"clients-matchall-blob-url-worker.html": [
"ee89a0d8b3ee7513ff3817632db32a9f5f2c162a",
[]
],
"clients-matchall-client-types-dedicated-worker.js": [ "clients-matchall-client-types-dedicated-worker.js": [
"5a3f04d33aaae64eb8abed16dc36e8181fed9de6", "5a3f04d33aaae64eb8abed16dc36e8181fed9de6",
[] []
@ -362449,7 +362537,7 @@
[] []
], ],
"README.md": [ "README.md": [
"fd964cf7e5b75db408e0b5813cb94172186f5c26", "9ab6e1284ad50d2982ea1f6fc78d7a519e796460",
[] []
], ],
"resources": { "resources": {
@ -363625,11 +363713,11 @@
[] []
], ],
"update_hosts.yml": [ "update_hosts.yml": [
"2036c419380b798eb5f11bfc1508e0ac44cf6249", "64aff7cf46a83e0ad0045065b5ec2e78deb6506d",
[] []
], ],
"update_manifest.yml": [ "update_manifest.yml": [
"b636b23a761ed033c160c8d64589e18506459c19", "e8f28217f1eaa9af3ec67e2b1a64365c8aa87a81",
[] []
] ]
}, },
@ -390553,7 +390641,7 @@
] ]
], ],
"DOMException-constructor-behavior.any.js": [ "DOMException-constructor-behavior.any.js": [
"d6e1cdd451ca8de2a6596d0fd31594c15f7318b3", "e9917af2287490c77543146a660d57c822cccf2e",
[ [
"WebIDL/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.html", "WebIDL/ecmascript-binding/es-exceptions/DOMException-constructor-behavior.any.html",
{} {}
@ -434735,6 +434823,124 @@
} }
] ]
], ],
"typing-around-link-element-at-collapsed-selection.tentative.html": [
"92fa2df233f327083f989c7a8abdf855f5005e61",
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&child=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&parent=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=ContentEditable&parent=b&child=i",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=DesignMode",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=DesignMode&child=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=DesignMode&parent=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-collapsed-selection.tentative.html?target=DesignMode&parent=b&child=i",
{
"testdriver": true,
"timeout": "long"
}
]
],
"typing-around-link-element-at-non-collapsed-selection.tentative.html": [
"a9e5790c356c4a910ce7d8565c3fe9a8408e2981",
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&child=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&parent=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=ContentEditable&parent=b&child=i",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=DesignMode",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=DesignMode&child=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=DesignMode&parent=b",
{
"testdriver": true,
"timeout": "long"
}
],
[
"editing/other/typing-around-link-element-at-non-collapsed-selection.tentative.html?target=DesignMode&parent=b&child=i",
{
"testdriver": true,
"timeout": "long"
}
]
],
"white-spaces-after-execCommand-delete.tentative.html": [ "white-spaces-after-execCommand-delete.tentative.html": [
"1490bf06f55a6c2b1e10afc044b19b108e5dd482", "1490bf06f55a6c2b1e10afc044b19b108e5dd482",
[ [
@ -461370,6 +461576,13 @@
] ]
], ],
"getter-special-cases": { "getter-special-cases": {
"cross-origin-isolated.sub.https.html": [
"e10d3452b91db3dc6a188bf4113833b699224ff1",
[
null,
{}
]
],
"csp-sandbox-no.https.html": [ "csp-sandbox-no.https.html": [
"e0b5f92376287417ab099c49e0b7396dc0d87256", "e0b5f92376287417ab099c49e0b7396dc0d87256",
[ [
@ -462518,7 +462731,7 @@
] ]
], ],
"clear-window-name.https.html": [ "clear-window-name.https.html": [
"39798f1e232f2ba109d64afdcf5031b06620207b", "27601eb7f6db164cde6ac1d0ecda0d69dfab1a75",
[ [
null, null,
{ {
@ -530306,6 +530519,13 @@
{} {}
] ]
], ],
"clients-matchall-blob-url-worker.https.html": [
"c29bac8b894a2c50828ae3469d3e181d227fe7ac",
[
null,
{}
]
],
"clients-matchall-client-types.https.html": [ "clients-matchall-client-types.https.html": [
"54f182b6202cd67cc63e159e7e2d4294fdf975ff", "54f182b6202cd67cc63e159e7e2d4294fdf975ff",
[ [
@ -549449,7 +549669,7 @@
"web-bundle": { "web-bundle": {
"subresource-loading": { "subresource-loading": {
"link-web-bundle.tentative.html": [ "link-web-bundle.tentative.html": [
"7f6de1014cf16be90a2f8ba0551c7c02c92219eb", "2f10cd6b3fdd4dd94dc5767df663583cd8d81071",
[ [
null, null,
{} {}
@ -549463,7 +549683,7 @@
] ]
], ],
"subresource-loading-from-web-bundle.tentative.html": [ "subresource-loading-from-web-bundle.tentative.html": [
"53a9c1b2cc707271c8d7b0e7fb7385fd59839744", "6f38f145bc8795c51961e08fdf09ea4e590d26eb",
[ [
null, null,
{} {}
@ -552794,7 +553014,7 @@
] ]
], ],
"video-decoder.any.js": [ "video-decoder.any.js": [
"33ea2dbe375d5446e68e90405f9867ce8f7fb272", "44e7375a78c013d0a9d0f60a33a04651473c48fb",
[ [
"webcodecs/video-decoder.any.html", "webcodecs/video-decoder.any.html",
{ {
@ -552827,7 +553047,7 @@
] ]
], ],
"video-encoder.any.js": [ "video-encoder.any.js": [
"77fe184bafa84786315c3375936dc61927ca304b", "d86e6b61f2039acdf882f7551c7027967eec3ce2",
[ [
"webcodecs/video-encoder.any.html", "webcodecs/video-encoder.any.html",
{ {
@ -552868,7 +553088,7 @@
] ]
], ],
"video-frame-serialization.any.js": [ "video-frame-serialization.any.js": [
"524f94374f417ae08798a2d1eeafbc012c028a41", "338f721da8f89e32b63a00dff5d1e62e7b60aa62",
[ [
"webcodecs/video-frame-serialization.any.html", "webcodecs/video-frame-serialization.any.html",
{ {
@ -552909,7 +553129,7 @@
] ]
], ],
"video-frame.any.js": [ "video-frame.any.js": [
"9eb6699c06ae541b7e2e3911a913b29a7f5d83cf", "14cce43baf144aedc57eab1a84dd8517dc2804e0",
[ [
"webcodecs/video-frame.any.html", "webcodecs/video-frame.any.html",
{ {
@ -552942,7 +553162,7 @@
] ]
], ],
"video-track-reader.html": [ "video-track-reader.html": [
"b5d610e9eacf72ee4339f79643e8fe54cb90fd24", "925e8374f58f4e1983d9ed7d05c0324a9028c22a",
[ [
null, null,
{} {}
@ -555141,6 +555361,13 @@
"timeout": "long" "timeout": "long"
} }
] ]
],
"RTCRtpTransceiver-headerExtensionControl.html": [
"e823bd830c6e34c159dc511c05721a40b69586b1",
[
null,
{}
]
] ]
}, },
"webrtc-identity": { "webrtc-identity": {

View file

@ -1,4 +0,0 @@
[hit-test-floats-005.html]
[Miss clipped float]
expected: FAIL

View file

@ -309,15 +309,6 @@
[fetch(): separate response Content-Type: text/plain ] [fetch(): separate response Content-Type: text/plain ]
expected: NOTRUN expected: NOTRUN
[<iframe>: separate response Content-Type: text/html;" \\" text/plain]
expected: FAIL
[<iframe>: combined response Content-Type: */* text/html]
expected: FAIL
[<iframe>: combined response Content-Type: text/html;charset=gbk text/plain text/html]
expected: FAIL
[<iframe>: combined response Content-Type: text/html */*] [<iframe>: combined response Content-Type: text/html */*]
expected: FAIL expected: FAIL
@ -330,3 +321,9 @@
[<iframe>: combined response Content-Type: text/html */*;charset=gbk] [<iframe>: combined response Content-Type: text/html */*;charset=gbk]
expected: FAIL expected: FAIL
[<iframe>: combined response Content-Type: text/html;" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
expected: FAIL

View file

@ -56,6 +56,3 @@
[separate text/javascript x/x] [separate text/javascript x/x]
expected: FAIL expected: FAIL
[separate text/javascript ]
expected: FAIL

View file

@ -11,6 +11,9 @@
[X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!] [X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
expected: FAIL expected: FAIL
[Content-Type-Options%3A%20nosniff] [X-Content-Type-Options%3A%20%22nosniFF%22]
expected: FAIL
[X-Content-Type-Options%3A%20no%0D%0AX-Content-Type-Options%3A%20nosniff]
expected: FAIL expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_1.html]
[Multiple history traversals from the same task]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_4.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -0,0 +1,8 @@
[cross-origin-isolated.sub.https.html]
expected: TIMEOUT
[self: originAgentCluster must equal true]
expected: FAIL
[child: originAgentCluster must equal true]
expected: TIMEOUT

View file

@ -1,6 +1,6 @@
[iframe_sandbox_popups_escaping-1.html] [iframe_sandbox_popups_escaping-1.html]
type: testharness type: testharness
expected: CRASH expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: FAIL expected: TIMEOUT

View file

@ -1,6 +1,5 @@
[iframe_sandbox_popups_escaping-3.html] [iframe_sandbox_popups_escaping-3.html]
type: testharness type: testharness
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used] [Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT expected: FAIL

View file

@ -1,6 +1,6 @@
[iframe_sandbox_popups_nonescaping-1.html] [iframe_sandbox_popups_nonescaping-1.html]
type: testharness type: testharness
expected: TIMEOUT expected: CRASH
[Check that popups from a sandboxed iframe do not escape the sandbox] [Check that popups from a sandboxed iframe do not escape the sandbox]
expected: NOTRUN expected: NOTRUN

View file

@ -1,6 +1,6 @@
[iframe_sandbox_popups_nonescaping-2.html] [iframe_sandbox_popups_nonescaping-2.html]
type: testharness type: testharness
expected: TIMEOUT expected: CRASH
[Check that popups from a sandboxed iframe do not escape the sandbox] [Check that popups from a sandboxed iframe do not escape the sandbox]
expected: NOTRUN expected: NOTRUN

View file

@ -0,0 +1,4 @@
[form-double-submit-2.html]
[preventDefault should allow onclick submit() to succeed]
expected: FAIL

View file

@ -0,0 +1,4 @@
[form-double-submit-3.html]
[<button> should have the same double-submit protection as <input type=submit>]
expected: FAIL

View file

@ -2,9 +2,6 @@
[input type search: setSelectionRange out of range a second time (must not fire select)] [input type search: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type text: selectionStart a second time (must not fire select)]
expected: FAIL
[textarea: selectionDirection a second time (must not fire select)] [textarea: selectionDirection a second time (must not fire select)]
expected: FAIL expected: FAIL
@ -17,30 +14,12 @@
[input type tel: selectionEnd out of range a second time (must not fire select)] [input type tel: selectionEnd out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type password: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type search: setRangeText() a second time (must not fire select)] [input type search: setRangeText() a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type url: selectionStart out of range a second time (must not fire select)] [input type url: selectionStart out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type password: selectionStart out of range a second time (must not fire select)]
expected: FAIL
[input type url: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL
[input type password: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL
[input type tel: setRangeText() a second time (must not fire select)]
expected: FAIL
[input type url: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type text: setSelectionRange out of range a second time (must not fire select)] [input type text: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
@ -50,30 +29,51 @@
[input type search: selectionStart out of range a second time (must not fire select)] [input type search: selectionStart out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type tel: selectionDirection a second time (must not fire select)]
expected: FAIL
[input type url: select() a second time (must not fire select)]
expected: FAIL
[input type url: selectionDirection a second time (must not fire select)]
expected: FAIL
[input type password: selectionEnd a second time (must not fire select)]
expected: FAIL
[input type search: selectionDirection a second time (must not fire select)]
expected: FAIL
[textarea: setSelectionRange out of range a second time (must not fire select)] [textarea: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type tel: select() a second time (must not fire select)] [input type password: select() a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type url: selectionStart a second time (must not fire select)] [input type text: selectionEnd a second time (must not fire select)]
expected: FAIL expected: FAIL
[input type tel: selectionStart a second time (must not fire select)] [input type url: selectionEnd out of range a second time (must not fire select)]
expected: FAIL
[input type password: selectionEnd out of range a second time (must not fire select)]
expected: FAIL
[textarea: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type text: setSelectionRange() a second time (must not fire select)]
expected: FAIL
[input type search: selectionEnd a second time (must not fire select)]
expected: FAIL
[input type password: setRangeText() a second time (must not fire select)]
expected: FAIL
[input type tel: setSelectionRange out of range a second time (must not fire select)]
expected: FAIL
[input type text: selectionDirection a second time (must not fire select)]
expected: FAIL
[textarea: selectionStart out of range a second time (must not fire select)]
expected: FAIL
[input type url: setRangeText() a second time (must not fire select)]
expected: FAIL
[input type password: selectionDirection a second time (must not fire select)]
expected: FAIL
[textarea: selectionEnd out of range a second time (must not fire select)]
expected: FAIL
[input type search: selectionStart a second time (must not fire select)]
expected: FAIL expected: FAIL

View file

@ -1,4 +0,0 @@
[module-delayed.html]
[async document.write in a module]
expected: FAIL

View file

@ -0,0 +1,4 @@
[module-static-import-delayed.html]
[document.write in an imported module]
expected: FAIL

View file

@ -4,6 +4,3 @@
[The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document] [The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: TIMEOUT expected: TIMEOUT
[The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
expected: FAIL

View file

@ -1,9 +1,10 @@
[promise-job-entry.html] [promise-job-entry.html]
expected: TIMEOUT
[Fulfillment handler on fulfilled promise] [Fulfillment handler on fulfilled promise]
expected: FAIL expected: FAIL
[Rejection handler on pending-then-rejected promise] [Rejection handler on pending-then-rejected promise]
expected: FAIL expected: TIMEOUT
[Sanity check: this all works as expected with no promises involved] [Sanity check: this all works as expected with no promises involved]
expected: FAIL expected: FAIL
@ -15,5 +16,5 @@
expected: FAIL expected: FAIL
[Fulfillment handler on pending-then-fulfilled promise] [Fulfillment handler on pending-then-fulfilled promise]
expected: FAIL expected: TIMEOUT

View file

@ -767,3 +767,9 @@
[X SNR (43.824040882292394 dB) is not greater than or equal to 65.737. Got 43.824040882292394.] [X SNR (43.824040882292394 dB) is not greater than or equal to 65.737. Got 43.824040882292394.]
expected: FAIL expected: FAIL
[X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[28696\]\t4.3013245373868222e+28\t9.3139332532882690e-1\t4.3013245373868222e+28\t4.6181612219179757e+28\t3.8985999999999999e-3\n\t[28697\]\t7.0477002859115601e-1\t9.0675884485244751e-1\t2.0198881626129150e-1\t2.2275913536212616e-1\t3.8985999999999999e-3\n\tMax AbsError of 4.3013245373868222e+28 at index of 28696.\n\tMax RelError of 4.6181612219179757e+28 at index of 28696.\n]
expected: FAIL
[X SNR (-529.2379582870841 dB) is not greater than or equal to 65.737. Got -529.2379582870841.]
expected: FAIL

View file

@ -1,5 +0,0 @@
[017.html]
expected: TIMEOUT
[origin of the script that invoked the method, about:blank]
expected: TIMEOUT

View file

@ -1,5 +0,0 @@
[017.html]
expected: TIMEOUT
[origin of the script that invoked the method, about:blank]
expected: TIMEOUT

View file

@ -0,0 +1,5 @@
[018.html]
expected: TIMEOUT
[origin of the script that invoked the method, javascript:]
expected: TIMEOUT

View file

@ -1,2 +0,0 @@
[Worker-constructor.html]
expected: ERROR

View file

@ -233,7 +233,11 @@ jobs:
- template: tools/ci/azure/install_chrome.yml - template: tools/ci/azure/install_chrome.yml
- template: tools/ci/azure/install_firefox.yml - template: tools/ci/azure/install_firefox.yml
- template: tools/ci/azure/update_hosts.yml - template: tools/ci/azure/update_hosts.yml
parameters:
pyflag: --py2
- template: tools/ci/azure/update_manifest.yml - template: tools/ci/azure/update_manifest.yml
parameters:
pyflag: --py2
- template: tools/ci/azure/tox_pytest.yml - template: tools/ci/azure/tox_pytest.yml
parameters: parameters:
directory: tools/wpt/ directory: tools/wpt/
@ -403,7 +407,11 @@ jobs:
# - template: tools/ci/azure/install_chrome.yml # - template: tools/ci/azure/install_chrome.yml
# - template: tools/ci/azure/install_firefox.yml # - template: tools/ci/azure/install_firefox.yml
- template: tools/ci/azure/update_hosts.yml - template: tools/ci/azure/update_hosts.yml
parameters:
pyflag: --py2
- template: tools/ci/azure/update_manifest.yml - template: tools/ci/azure/update_manifest.yml
parameters:
pyflag: --py2
- template: tools/ci/azure/tox_pytest.yml - template: tools/ci/azure/tox_pytest.yml
parameters: parameters:
directory: tools/wpt/ directory: tools/wpt/

View file

@ -100,6 +100,7 @@ test(function() {
{name: "InvalidModificationError", code: 13}, {name: "InvalidModificationError", code: 13},
{name: "NamespaceError", code: 14}, {name: "NamespaceError", code: 14},
{name: "InvalidAccessError", code: 15}, {name: "InvalidAccessError", code: 15},
{name: "TypeMismatchError", code: 17},
{name: "SecurityError", code: 18}, {name: "SecurityError", code: 18},
{name: "NetworkError", code: 19}, {name: "NetworkError", code: 19},
{name: "AbortError", code: 20}, {name: "AbortError", code: 20},
@ -107,7 +108,25 @@ test(function() {
{name: "QuotaExceededError", code: 22}, {name: "QuotaExceededError", code: 22},
{name: "TimeoutError", code: 23}, {name: "TimeoutError", code: 23},
{name: "InvalidNodeTypeError", code: 24}, {name: "InvalidNodeTypeError", code: 24},
{name: "DataCloneError", code: 25} {name: "DataCloneError", code: 25},
// These were removed from the error names table.
// See https://github.com/heycam/webidl/pull/946.
{name: "DOMStringSizeError", code: 0},
{name: "NoDataAllowedError", code: 0},
{name: "ValidationError", code: 0},
// The error names which don't have legacy code values.
{name: "EncodingError", code: 0},
{name: "NotReadableError", code: 0},
{name: "UnknownError", code: 0},
{name: "ConstraintError", code: 0},
{name: "DataError", code: 0},
{name: "TransactionInactiveError", code: 0},
{name: "ReadOnlyError", code: 0},
{name: "VersionError", code: 0},
{name: "OperationError", code: 0},
{name: "NotAllowedError", code: 0}
].forEach(function(test_case) { ].forEach(function(test_case) {
test(function() { test(function() {
var ex = new DOMException("msg", test_case.name); var ex = new DOMException("msg", test_case.name);

View file

@ -0,0 +1,5 @@
<!DOCTYPE html>
<html>
<title>CSS Reference: No Red</title>
<p>Test passes if there is no red.

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<title>CSS Scroll Snap Reference</title>
<style>
html, body { margin: 0; padding: 0; }
:root {
overflow: hidden; /* hide scrollbars for reftest analysis */
}
#target {
position: absolute;
top: 25%;
width: 100%;
margin: 25vh 0;
border-top: solid blue;
}
</style>
<div id="target">
<div>Test passes if the blue line above is centered in the viewport.</div>
</div>

View file

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<title>scroll-snap-type + scroll-padding propagates root to viewport</title>
<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'>
<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type'>
<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding'>
<link rel='match' href='scroll-snap-root-001-ref.html'>
<meta name='assert'
content="Test passes if scroll snap properties on root are applied to viewport.">
<style type='text/css'>
html, body { margin: 0; padding: 0; }
:root {
scroll-snap-type: block mandatory;
scroll-padding: 25%;
overflow: hidden; /* hide scrollbars for reftest analysis */
}
#fail {
font: bold 2em;
background: red;
height: 120vh;
margin-bottom: 60vh;
}
#target {
margin-bottom: 120vh;
scroll-margin: 25vh;
scroll-snap-align: start;
border-top: solid blue;
}
</style>
<div id="fail">FAIL</div>
<div id="target">
<div>Test passes if the blue line above is centered in the viewport.</div>
</div>
<script>
document.getElementById('target').scrollIntoView();
</script>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<title>CSS Scroll Snap Reference</title>
<style type='text/css'>
html, body { margin: 0; padding: 0; }
#target {
border-bottom: solid orange thick;
position: absolute;
bottom: 0;
width: 100%;
}
</style>
<div id="target">
<div>Test passes if the orange stripe below is exactly at the bottom of the viewport.</div>
</div>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<title>scroll-padding does not propagate body to viewport</title>
<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'>
<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type'>
<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding'>
<link rel='match' href='scroll-snap-root-002-ref.html'>
<meta name='assert'
content="Test passes if scroll-snap-padding on body is not applied to viewport.">
<style type='text/css'>
html, body { margin: 0; padding: 0; }
:root {
scroll-snap-type: block mandatory;
overflow: hidden; /* hide scrollbars for reftest analysis */
}
body {
scroll-padding: 25%;
}
#fail {
height: 120vh;
font: bold 2em;
background: red;
}
#target {
margin: 120vh 0;
scroll-snap-align: end;
border-bottom: solid orange thick;
}
</style>
<div id="fail">FAIL</div>
<div id="target">
<div>Test passes if the orange stripe below is exactly at the bottom of the viewport.</div>
</div>
<script>
document.getElementById('target').scrollIntoView();
</script>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<title>scroll-snap-type does not propagate body to viewport</title>
<link rel='author' title='Elika J. Etemad' href='http://fantasai.inkedblade.net/contact'>
<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-type'>
<link rel='help' href='https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding'>
<link rel='match' href='no-red-ref.html'>
<meta name='assert'
content="Test passes if scroll-snap-type on body is not applied to viewport.">
<style type='text/css'>
:root {
overflow: hidden; /* hide scrollbars for reftest analysis */
}
body {
scroll-snap-type: block mandatory;
}
#pass {
height: 120vh;
}
#target {
scroll-snap-align: start;
height: 100vh;
background: red;
}
</style>
<p id="pass">Test passes if there is no red.
<div id="target">
<div>FAIL</div>
</div>

View file

@ -0,0 +1,167 @@
<!DOCTYPE html>
<title>
CSS Scroll Snap Reference
</title>
<style>
.wrapper {
/* lay out in a nice grid */
display: grid;
gap: 0.25em;
grid-template-columns: repeat(6, max-content);
}
.scroller {
width: 50px;
height: 50px;
border: solid silver;
border-block-start-color: blue;
border-inline-start-color: blue;
position: relative;
}
.target {
width: 30px;
height: 30px;
background: orange;
top: 0; left: 0; right: 0; bottom: 0;
position: absolute;
}
.TB { writing-mode: horizontal-tb; }
.LR { writing-mode: vertical-lr; }
.RL { writing-mode: vertical-rl; }
.ltr { direction: ltr; }
.rtl { direction: rtl; }
.TB.invert .target { top: auto; }
.LR.invert .target { left: auto; }
.RL.invert .target { right: auto; }
.TB.ltr.invert .target { left: auto; }
.TB.rtl.invert .target { right: auto; }
.LR.ltr.invert .target { top: auto; }
.LR.rtl.invert .target { bottom: auto; }
.RL.ltr.invert .target { top: auto; }
.RL.rtl.invert .target { bottom: auto; }
/* not absolutizing the border colors, so that the test passes even if css-logical is not supported; */
.large.invert {
border: solid silver;
border-block-end-color: blue;
border-inline-end-color: blue;
}
</style>
<p>Test passes if there is an orange square tucked into each blue corner without gaps,
and there is no red.
<div class="wrapper">
<!-- Simple Small Cases -->
<div class="scroller TB ltr small">
<div class="target"></div>
</div>
<div class="scroller LR ltr small">
<div class="target"></div>
</div>
<div class="scroller RL ltr small">
<div class="target"></div>
</div>
<div class="scroller TB rtl small">
<div class="target"></div>
</div>
<div class="scroller LR rtl small">
<div class="target"></div>
</div>
<div class="scroller RL rtl small">
<div class="target"></div>
</div>
<!-- Target-inverted Small Cases
This row should be identical to the previous. -->
<div class="scroller TB ltr small invert">
<div class="target"></div>
</div>
<div class="scroller LR ltr small invert">
<div class="target"></div>
</div>
<div class="scroller RL ltr small invert">
<div class="target"></div>
</div>
<div class="scroller TB rtl small invert">
<div class="target"></div>
</div>
<div class="scroller LR rtl small invert">
<div class="target"></div>
</div>
<div class="scroller RL rtl small invert">
<div class="target"></div>
</div>
<!-- Simple Large Cases -->
<div class="scroller TB ltr large">
<div class="target"></div>
</div>
<div class="scroller LR ltr large">
<div class="target"></div>
</div>
<div class="scroller RL ltr large">
<div class="target"></div>
</div>
<div class="scroller TB rtl large">
<div class="target"></div>
</div>
<div class="scroller LR rtl large">
<div class="target"></div>
</div>
<div class="scroller RL rtl large">
<div class="target"></div>
</div>
<!-- Target-inverted Large Cases
This is the fun one. -->
<div class="scroller TB ltr large invert">
<div class="target"></div>
</div>
<div class="scroller LR ltr large invert">
<div class="target"></div>
</div>
<div class="scroller RL ltr large invert">
<div class="target"></div>
</div>
<div class="scroller TB rtl large invert">
<div class="target"></div>
</div>
<div class="scroller LR rtl large invert">
<div class="target"></div>
</div>
<div class="scroller RL rtl large invert">
<div class="target"></div>
</div>
</div> <!-- wrapper -->

View file

@ -0,0 +1,237 @@
<!DOCTYPE html>
<title>
scroll-snap-align vs writing-mode
</title>
<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-snap-align">
<link rel="help" href="https://drafts.csswg.org/css-scroll-snap/#re-snap">
<link rel="match" href="scroll-snap-writing-mode-000-ref.html">
<link rel="author" title="Elika J. Etemad" href="http://fantasai.inkedblade.net/contact">
<style>
.wrapper {
/* lay out in a nice grid */
display: grid;
gap: 0.25em;
grid-template-columns: repeat(6, max-content);
}
.scroller {
scroll-snap-type: both mandatory;
overflow: hidden;
scroll-padding: 0;
width: 50px;
height: 50px;
border: solid silver;
border-block-start-color: blue;
border-inline-start-color: blue;
}
.area {
width: 200px;
height: 200px;
}
.target {
margin: 5px;
scroll-snap-align: start;
}
.small .target {
width: 30px;
height: 30px;
background: orange;
}
.large .target {
width: 51px;
height: 51px;
border-block-end: 20px solid red;
border-inline-end: 20px solid red;
}
.large .target::before {
content: '';
display: block;
width: 30px;
height: 30px;
background: orange;
}
.TB { writing-mode: horizontal-tb; }
.LR { writing-mode: vertical-lr; }
.RL { writing-mode: vertical-rl; }
.ltr { direction: ltr; }
.rtl { direction: rtl; }
.TB.ltr.invert .target { writing-mode: vertical-rl; direction: rtl; }
.TB.rtl.invert .target { writing-mode: vertical-lr; direction: rtl; }
.LR.ltr.invert .target { writing-mode: vertical-rl; direction: rtl; }
.LR.rtl.invert .target { writing-mode: vertical-rl; direction: ltr; }
.RL.ltr.invert .target { writing-mode: vertical-lr; direction: rtl; }
.RL.rtl.invert .target { writing-mode: horizontal-tb; direction: ltr; }
.large.invert {
/* key off targets writing mode, which we just inverted */
border: solid silver;
border-block-end-color: blue;
border-inline-end-color: blue;
}
</style>
<p>Test passes if there is an orange square tucked into each blue corner without gaps,
and there is no red.
<div class="wrapper">
<!-- Simple Small Cases -->
<div class="scroller TB ltr small">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR ltr small">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL ltr small">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller TB rtl small">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR rtl small">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL rtl small">
<div class="area">
<div class="target"></div>
</div>
</div>
<!-- Target-inverted Small Cases
This row should be identical to the previous. -->
<div class="scroller TB ltr small invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR ltr small invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL ltr small invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller TB rtl small invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR rtl small invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL rtl small invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<!-- Simple Large Cases -->
<div class="scroller TB ltr large">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR ltr large">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL ltr large">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller TB rtl large">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR rtl large">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL rtl large">
<div class="area">
<div class="target"></div>
</div>
</div>
<!-- Target-inverted Large Cases
This is the fun one. -->
<div class="scroller TB ltr large invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR ltr large invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL ltr large invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller TB rtl large invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller LR rtl large invert">
<div class="area">
<div class="target"></div>
</div>
</div>
<div class="scroller RL rtl large invert">
<div class="area">
<div class="target"></div>
</div>
</div>
</div> <!-- wrapper -->

View file

@ -0,0 +1,344 @@
/**
* EditorTestUtils is a helper utilities to test HTML editor. This can be
* instantiated per an editing host. If you test `designMode`, the editing
* host should be the <body> element.
*/
class EditorTestUtils {
kShift = "\uE008";
kMeta = "\uE03d";
kControl = "\uE009";
kAlt = "\uE00A";
editingHost;
constructor(aEditingHost, aHarnessWindow = window) {
this.editingHost = aEditingHost;
if (aHarnessWindow != this.window) {
this.window.test_driver.set_test_context(aHarnessWindow);
}
}
get document() {
return this.editingHost.ownerDocument;
}
get window() {
return this.document.defaultView;
}
get selection() {
return this.window.getSelection();
}
sendKey(key, modifier) {
if (!modifier) {
return new this.window.test_driver.Actions()
.keyDown(key)
.keyUp(key)
.send();
}
return new this.window.test_driver.Actions()
.keyDown(modifier)
.keyDown(key)
.keyUp(key)
.keyUp(modifier)
.send();
}
sendDeleteKey(modifier) {
const kDeleteKey = "\uE017";
return this.sendKey(kDeleteKey, modifier);
}
sendBackspaceKey(modifier) {
const kBackspaceKey = "\uE003";
return this.sendKey(kBackspaceKey, modifier);
}
sendArrowLeftKey(modifier) {
const kArrowLeft = "\uE012";
return this.sendKey(kArrowLeft, modifier);
}
sendArrowRightKey(modifier) {
const kArrowRight = "\uE014";
return this.sendKey(kArrowRight, modifier);
}
sendHomeKey(modifier) {
const kHome = "\uE011";
return this.sendKey(kHome, modifier);
}
sendEndKey(modifier) {
const kEnd = "\uE010";
return this.sendKey(kEnd, modifier);
}
// Similar to `setupDiv` in editing/include/tests.js, this method sets
// innerHTML value of this.editingHost, and sets multiple selection ranges
// specified with the markers.
// - `[` specifies start boundary in a text node
// - `{` specifies start boundary before a node
// - `]` specifies end boundary in a text node
// - `}` specifies end boundary after a node
setupEditingHost(innerHTMLWithRangeMarkers) {
const startBoundaries = innerHTMLWithRangeMarkers.match(/\{|\[/g) || [];
const endBoundaries = innerHTMLWithRangeMarkers.match(/\}|\]/g) || [];
if (startBoundaries.length !== endBoundaries.length) {
throw "Should match number of open/close markers";
}
this.editingHost.innerHTML = innerHTMLWithRangeMarkers;
this.editingHost.focus();
if (startBoundaries.length === 0) {
// Don't remove the range for now since some tests may assume that
// setting innerHTML does not remove all selection ranges.
return;
}
let getNextRangeAndDeleteMarker = startNode => {
let getNextLeafNode = node => {
let inclusiveDeepestFirstChildNode = container => {
while (container.firstChild) {
container = container.firstChild;
}
return container;
};
if (node.hasChildNodes()) {
return inclusiveDeepestFirstChildNode(node);
}
if (node.nextSibling) {
return inclusiveDeepestFirstChildNode(node.nextSibling);
}
let nextSibling = (child => {
for (
let parent = child.parentElement;
parent && parent != this.editingHost;
parent = parent.parentElement
) {
if (parent.nextSibling) {
return parent.nextSibling;
}
}
return null;
})(node);
if (!nextSibling) {
return null;
}
return inclusiveDeepestFirstChildNode(nextSibling);
};
let scanMarkerInTextNode = (textNode, offset) => {
return /[\{\[\]\}]/.exec(textNode.data.substr(offset));
};
let startMarker = ((startContainer, startOffset) => {
let scanStartMakerInTextNode = (textNode, offset) => {
let scanResult = scanMarkerInTextNode(textNode, offset);
if (scanResult === null) {
return null;
}
if (scanResult[0] === "}" || scanResult[0] === "]") {
throw "An end marker is found before a start marker";
}
return {
marker: scanResult[0],
container: textNode,
offset: scanResult.index + offset
};
};
if (startContainer.nodeType === Node.TEXT_NODE) {
let scanResult = scanStartMakerInTextNode(
startContainer,
startOffset
);
if (scanResult !== null) {
return scanResult;
}
}
let nextNode = startContainer;
while ((nextNode = getNextLeafNode(nextNode))) {
if (nextNode.nodeType === Node.TEXT_NODE) {
let scanResult = scanStartMakerInTextNode(nextNode, 0);
if (scanResult !== null) {
return scanResult;
}
continue;
}
}
return null;
})(startNode, 0);
if (startMarker === null) {
return null;
}
let endMarker = ((startContainer, startOffset) => {
let scanEndMarkerInTextNode = (textNode, offset) => {
let scanResult = scanMarkerInTextNode(textNode, offset);
if (scanResult === null) {
return null;
}
if (scanResult[0] === "{" || scanResult[0] === "[") {
throw "A start marker is found before an end marker";
}
return {
marker: scanResult[0],
container: textNode,
offset: scanResult.index + offset
};
};
if (startContainer.nodeType === Node.TEXT_NODE) {
let scanResult = scanEndMarkerInTextNode(startContainer, startOffset);
if (scanResult !== null) {
return scanResult;
}
}
let nextNode = startContainer;
while ((nextNode = getNextLeafNode(nextNode))) {
if (nextNode.nodeType === Node.TEXT_NODE) {
let scanResult = scanEndMarkerInTextNode(nextNode, 0);
if (scanResult !== null) {
return scanResult;
}
continue;
}
}
return null;
})(startMarker.container, startMarker.offset + 1);
if (endMarker === null) {
throw "Found an open marker, but not found corresponding close marker";
}
let indexOfContainer = (container, child) => {
let offset = 0;
for (let node = container.firstChild; node; node = node.nextSibling) {
if (node == child) {
return offset;
}
offset++;
}
throw "child must be a child node of container";
};
let deleteFoundMarkers = () => {
let removeNode = node => {
let container = node.parentElement;
let offset = indexOfContainer(container, node);
node.remove();
return { container, offset };
};
if (startMarker.container == endMarker.container) {
// If the text node becomes empty, remove it and set collapsed range
// to the position where there is the text node.
if (startMarker.container.length === 2) {
if (!/[\[\{][\]\}]/.test(startMarker.container.data)) {
throw `Unexpected text node (data: "${startMarker.container.data}")`;
}
let { container, offset } = removeNode(startMarker.container);
startMarker.container = endMarker.container = container;
startMarker.offset = endMarker.offset = offset;
startMarker.marker = endMarker.marker = "";
return;
}
startMarker.container.data = `${startMarker.container.data.substring(
0,
startMarker.offset
)}${startMarker.container.data.substring(
startMarker.offset + 1,
endMarker.offset
)}${startMarker.container.data.substring(endMarker.offset + 1)}`;
if (startMarker.offset >= startMarker.container.length) {
startMarker.offset = endMarker.offset =
startMarker.container.length;
return;
}
endMarker.offset--; // remove the start marker's length
if (endMarker.offset > endMarker.container.length) {
endMarker.offset = endMarker.container.length;
}
return;
}
if (startMarker.container.length === 1) {
let { container, offset } = removeNode(startMarker.container);
startMarker.container = container;
startMarker.offset = offset;
startMarker.marker = "";
} else {
startMarker.container.data = `${startMarker.container.data.substring(
0,
startMarker.offset
)}${startMarker.container.data.substring(startMarker.offset + 1)}`;
}
if (endMarker.container.length === 1) {
let { container, offset } = removeNode(endMarker.container);
endMarker.container = container;
endMarker.offset = offset;
endMarker.marker = "";
} else {
endMarker.container.data = `${endMarker.container.data.substring(
0,
endMarker.offset
)}${endMarker.container.data.substring(endMarker.offset + 1)}`;
}
};
deleteFoundMarkers();
let handleNodeSelectMarker = () => {
if (startMarker.marker === "{") {
if (startMarker.offset === 0) {
// The range start with the text node.
let container = startMarker.container.parentElement;
startMarker.offset = indexOfContainer(
container,
startMarker.container
);
startMarker.container = container;
} else if (startMarker.offset === startMarker.container.data.length) {
// The range start after the text node.
let container = startMarker.container.parentElement;
startMarker.offset =
indexOfContainer(container, startMarker.container) + 1;
startMarker.container = container;
} else {
throw 'Start marker "{" is allowed start or end of a text node';
}
}
if (endMarker.marker === "}") {
if (endMarker.offset === 0) {
// The range ends before the text node.
let container = endMarker.container.parentElement;
endMarker.offset = indexOfContainer(container, endMarker.container);
endMarker.container = container;
} else if (endMarker.offset === endMarker.container.data.length) {
// The range ends with the text node.
let container = endMarker.container.parentElement;
endMarker.offset =
indexOfContainer(container, endMarker.container) + 1;
endMarker.container = container;
} else {
throw 'End marker "}" is allowed start or end of a text node';
}
}
};
handleNodeSelectMarker();
let range = document.createRange();
range.setStart(startMarker.container, startMarker.offset);
range.setEnd(endMarker.container, endMarker.offset);
return range;
};
let ranges = [];
for (
let range = getNextRangeAndDeleteMarker(this.editingHost.firstChild);
range;
range = getNextRangeAndDeleteMarker(range.endContainer)
) {
ranges.push(range);
}
this.selection.removeAllRanges();
for (let range of ranges) {
this.selection.addRange(range);
}
if (this.selection.rangeCount != ranges.length) {
throw `Failed to set selection to the given ranges whose length is ${ranges.length}, but only ${this.selection.rangeCount} ranges are added`;
}
}
}

View file

@ -0,0 +1,520 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<meta name="variant" content="?target=ContentEditable">
<meta name="variant" content="?target=ContentEditable&parent=b">
<meta name="variant" content="?target=ContentEditable&child=b">
<meta name="variant" content="?target=ContentEditable&parent=b&child=i">
<meta name="variant" content="?target=DesignMode">
<meta name="variant" content="?target=DesignMode&parent=b">
<meta name="variant" content="?target=DesignMode&child=b">
<meta name="variant" content="?target=DesignMode&parent=b&child=i">
<title>Testing inserting content around link element</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="../include/editor-test-utils.js"></script>
</head>
<body>
<div contenteditable></div>
<iframe srcdoc="
<!doctype html>
<html>
<script>document.designMode='on';</script>
<script src='/resources/testdriver.js'></script>
<script src='/resources/testdriver-vendor.js'></script>
<script src='/resources/testdriver-actions.js'></script>
<body></body>
</html>"></iframe>
<script>
"use strict";
const params = new URLSearchParams(location.search.substring(1));
const kTarget = params.get("target");
const kParentTag = params.get("parent") === null
? ["", ""]
: [`<${params.get("parent")}>`, `</${params.get("parent")}>`];
const kChildTag = params.get("child") === null
? ["", ""]
: [`<${params.get("child")}>`, `</${params.get("child")}>`];
const kLinkDesc = (() => {
let result = ""
if (kParentTag[0] !== "") {
result += `in ${kParentTag[0]} `;
if (kChildTag[0] !== "") {
result += "and ";
}
}
if (kChildTag[0] !== "") {
result += `containing ${kChildTag[0]} `;
}
return result;
})();
const kNewContainerOfLink = (() => {
if (kParentTag !== "" && kChildTag !== "") {
return [`${kParentTag[0]}${kChildTag[0]}`, `${kChildTag[1]}${kParentTag[1]}`];
}
if (kParentTag !== "") {
return kParentTag;
}
if (kChildTag !== "") {
return kChildTag;
}
return ["", ""];
})();
const kSelectorForTextNodeContainer = kChildTag[0] === ""
? "a"
: `a > ${kChildTag[0].substr(1, kChildTag[0].length - 2)}`;
function getEditingHost() {
return kTarget === "ContentEditable"
? document.querySelector("div[contenteditable]")
: document.querySelector("iframe").contentDocument.body;
}
function addPromiseTest(test) {
promise_test(async () => {
let editingHost = getEditingHost();
let utils = new EditorTestUtils(editingHost);
utils.setupEditingHost(test.innerHTML);
utils.window.focus();
utils.document.body.focus();
editingHost.focus();
await test.run(utils);
if (Array.isArray(test.expectedResult)) {
assert_in_array(editingHost.innerHTML, test.expectedResult);
} else {
assert_equals(editingHost.innerHTML, test.expectedResult);
}
}, `${test.description.trim()} in ${test.innerHTML}`);
}
promise_test(async () => {
await new Promise(resolve => {
addEventListener("load", resolve, { once: true });
});
}, "");
if (kChildTag[0] === "") {
// Immediately after creating a link with Document.execCommand.
addPromiseTest({
description: `Replacing text in a link ${kLinkDesc}with "XY"`,
innerHTML: `<p>${kParentTag[0]}[abc]${kParentTag[1]}</p>`,
run: async (utils) => {
utils.document.execCommand("createLink", false, "about:blank");
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}<a href="about:blank">XY${kParentTag[1]}</a></p>`,
`<p>${kParentTag[0]}<a href="about:blank">XY${kParentTag[1]}</a><br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after making a link ${kLinkDesc}(following Selection.collapseToEnd)`,
innerHTML: `<p>${kParentTag[0]}[abc]${kParentTag[1]}</p>`,
run: async (utils) => {
utils.document.execCommand("createLink", false, "about:blank");
utils.selection.collapseToEnd();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}<a href="about:blank">abc</a>XY${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">abc</a>XY${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after making a link ${kLinkDesc}(following ArrowRight key press)`,
innerHTML: `<p>${kParentTag[0]}[abc]${kParentTag[1]}</p>`,
run: async (utils) => {
utils.document.execCommand("createLink", false, "about:blank");
await utils.sendArrowRightKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}<a href="about:blank">abc</a>XY${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">abc</a>XY${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after making a link ${kLinkDesc}(following End key press)`,
innerHTML: `<p>${kParentTag[0]}[abc]${kParentTag[1]}</p>`,
run: async (utils) => {
utils.document.execCommand("createLink", false, "about:blank");
await utils.sendEndKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}<a href="about:blank">abc</a>XY${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">abc</a>XY${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after making a link ${kLinkDesc}(following Selection.collapseToStart)`,
innerHTML: `<p>${kParentTag[0]}[abc]${kParentTag[1]}</p>`,
run: async (utils) => {
utils.document.execCommand("createLink", false, "about:blank");
utils.selection.collapseToStart();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}XY<a href="about:blank">abc</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}XY<a href="about:blank">abc</a>${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after making a link ${kLinkDesc}(following ArrowLeft key press)`,
innerHTML: `<p>${kParentTag[0]}[abc]${kParentTag[1]}</p>`,
run: async (utils) => {
utils.document.execCommand("createLink", false, "about:blank");
await utils.sendArrowLeftKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}XY<a href="about:blank">abc</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}XY<a href="about:blank">abc</a>${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after making a link ${kLinkDesc}(following Home key press)`,
innerHTML: `<p>${kParentTag[0]}[abc]${kParentTag[1]}</p>`,
run: async (utils) => {
utils.document.execCommand("createLink", false, "about:blank");
await utils.sendHomeKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}XY<a href="about:blank">abc</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}XY<a href="about:blank">abc</a>${kParentTag[1]}<br></p>`,
],
});
}
addPromiseTest({
description: `Inserting "XY" after setting caret position to middle of a link ${kLinkDesc}(Selection.collapse)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}[]abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
utils.selection.collapse(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, 2);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abXYc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abXYc${kChildTag[1]}</a>${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after setting caret position to middle of a link ${kLinkDesc}(Selection.addRange)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}[]abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
utils.selection.removeAllRanges();
let range = utils.document.createRange();
range.setStart(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, 2);
utils.selection.addRange(range);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abXYc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abXYc${kChildTag[1]}</a>${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after setting caret position to start of a link ${kLinkDesc}(Selection.collapse)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}ab[]c${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
utils.selection.collapse(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, 0);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[2]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after setting caret position to start of a link ${kLinkDesc}(Selection.addRange)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}ab[]c${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
utils.selection.removeAllRanges();
let range = utils.document.createRange();
range.setStart(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, 0);
utils.selection.addRange(range);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[2]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after setting caret position to end of a link ${kLinkDesc}(Selection.collapse)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}ab[]c${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
utils.selection.collapse(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, "abc".length);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[2]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after setting caret position to end of a link ${kLinkDesc}(Selection.addRange)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}ab[]c${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
utils.selection.collapse(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, "abc".length);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[2]}<br></p>`,
],
});
// Type text after moving caret with Range API.
addPromiseTest({
description: `Inserting "XY" after modifying caret position to middle of a link ${kLinkDesc}`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}[]abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
let range = utils.selection.getRangeAt(0);
range.setStart(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, 2);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abXYc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abXYc${kChildTag[1]}</a>${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after modifying caret position to start of a link ${kLinkDesc}`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}ab[]c${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
let range = utils.selection.getRangeAt(0);
range.setStart(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, 0);
range.setEnd(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, 0);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after modifying caret position to end of a link ${kLinkDesc}`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}ab[]c${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
let range = utils.selection.getRangeAt(0);
range.setStart(utils.editingHost.querySelector(kSelectorForTextNodeContainer).firstChild, "abc".length);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
],
});
// Type text after deleting character immediately before/after a link.
addPromiseTest({
description: `Inserting "XY" after deleting following character of a link ${kLinkDesc}(Backspace)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abc${kChildTag[1]}</a>d[]${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.sendBackspaceKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting following character of a link ${kLinkDesc}(execCommand("delete"))`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abc${kChildTag[1]}</a>d[]${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.document.execCommand("delete", false);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting a previous character of a link ${kLinkDesc}(Delete)`,
innerHTML: `<p>${kParentTag[0]}[]z<a href="about:blank">${kChildTag[0]}abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.sendDeleteKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting a previous character of a link ${kLinkDesc}(execCommand("forwarddelete"))`,
innerHTML: `<p>${kParentTag[0]}[]z<a href="about:blank">${kChildTag[0]}abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.document.execCommand("forwarddelete", false);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
],
});
// Type text after deleting the last character in a link.
addPromiseTest({
description: `Inserting "XY" after deleting last character of a link ${kLinkDesc}(Backspace)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abcd[]${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.sendBackspaceKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[1]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting last character of a link ${kLinkDesc}(execCommand("delete"))`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abcd[]${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.document.execCommand("delete", false);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[1]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting last character of a link ${kLinkDesc}(Delete)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abc[]d${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.sendDeleteKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting last character of a link ${kLinkDesc}(execCommand("forwarddelete"))`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abc[]d${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.document.execCommand("forwarddelete", false);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
],
});
// Type text after deleting the first character in a link.
addPromiseTest({
description: `Inserting "XY" after deleting first character of a link ${kLinkDesc}(Backspace)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}z[]abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.sendBackspaceKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting first character of a link ${kLinkDesc}(execCommand("delete"))`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}z[]abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.document.execCommand("delete", false);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting first character of a link ${kLinkDesc}(Delete)`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}[]zabc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.sendDeleteKey();
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting first character of a link ${kLinkDesc}(execCommand("forwarddelete"))`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}[]zabc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await utils.document.execCommand("forwarddelete", false);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
],
});
</script>
</body>
</html>

View file

@ -0,0 +1,214 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="timeout" content="long">
<meta name="variant" content="?target=ContentEditable">
<meta name="variant" content="?target=ContentEditable&parent=b">
<meta name="variant" content="?target=ContentEditable&child=b">
<meta name="variant" content="?target=ContentEditable&parent=b&child=i">
<meta name="variant" content="?target=DesignMode">
<meta name="variant" content="?target=DesignMode&parent=b">
<meta name="variant" content="?target=DesignMode&child=b">
<meta name="variant" content="?target=DesignMode&parent=b&child=i">
<title>Testing inserting content at non-collapsed selection around link element</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="../include/editor-test-utils.js"></script>
</head>
<body>
<div contenteditable></div>
<iframe srcdoc="
<!doctype html>
<html>
<script>document.designMode='on';</script>
<script src='/resources/testdriver.js'></script>
<script src='/resources/testdriver-vendor.js'></script>
<script src='/resources/testdriver-actions.js'></script>
<body></body>
</html>"></iframe>
<script>
"use strict";
const params = new URLSearchParams(location.search.substring(1));
const kTarget = params.get("target");
const kParentTag = params.get("parent") === null
? ["", ""]
: [`<${params.get("parent")}>`, `</${params.get("parent")}>`];
const kChildTag = params.get("child") === null
? ["", ""]
: [`<${params.get("child")}>`, `</${params.get("child")}>`];
const kLinkDesc = (() => {
let result = ""
if (kParentTag[0] !== "") {
result += `in ${kParentTag[0]} `;
if (kChildTag[0] !== "") {
result += "and ";
}
}
if (kChildTag[0] !== "") {
result += `containing ${kChildTag[0]} `;
}
return result;
})();
const kNewContainerOfLink = (() => {
if (kParentTag !== "" && kChildTag !== "") {
return [`${kParentTag[0]}${kChildTag[0]}`, `${kChildTag[1]}${kParentTag[1]}`];
}
if (kParentTag !== "") {
return kParentTag;
}
if (kChildTag !== "") {
return kChildTag;
}
return ["", ""];
})();
function getEditingHost() {
return kTarget === "ContentEditable"
? document.querySelector("div[contenteditable]")
: document.querySelector("iframe").contentDocument.body;
}
function addPromiseTest(test) {
promise_test(async () => {
let editingHost = getEditingHost();
let utils = new EditorTestUtils(editingHost);
utils.setupEditingHost(test.innerHTML);
utils.window.focus();
utils.document.body.focus();
editingHost.focus();
await test.run(utils);
if (Array.isArray(test.expectedResult)) {
assert_in_array(editingHost.innerHTML, test.expectedResult);
} else {
assert_equals(editingHost.innerHTML, test.expectedResult);
}
}, `${test.description} in ${test.innerHTML}`);
}
promise_test(async () => {
await new Promise(resolve => {
addEventListener("load", resolve, { once: true });
});
}, "");
for (const test of [
["Direct typing", utils => {}],
["Backspace", utils => { return utils.sendBackspaceKey(); }],
["Delete", utils => { return utils.sendDeleteKey(); }],
["execCommand(\"delete\")", utils => { utils.document.execCommand("delete", false); }],
["execCommand(\"forwarddelete\")", utils => { utils.document.execCommand("forwarddelete", false); }],
]) {
addPromiseTest({
description: `Inserting "XY" after deleting first character of a link ${kLinkDesc}(${test[0]})`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}[z]abc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await test[1](utils);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: (() => {
if (test[0] === "Direct typing") {
return [
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}XYabc${kChildTag[1]}</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}XYabc${kChildTag[1]}</a>${kParentTag[1]}<br></p>`,
];
}
return [
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}XY<a href="about:blank">abc</a>${kNewContainerOfLink[1]}<br></p>`,
];
})(),
});
addPromiseTest({
description: `Inserting "XY" after deleting last character in a non-collapsed range of a link ${kLinkDesc}(${test[0]})`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abc[d]${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await test[1](utils);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: (() => {
if (test[0] === "Direct typing") {
return [
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abcXY${kChildTag[1]}</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}abcXY${kChildTag[1]}</a>${kParentTag[1]}<br></p>`,
];
}
return [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">abc</a>XY${kNewContainerOfLink[1]}<br></p>`,
];
})(),
});
addPromiseTest({
description: `Inserting "XY" after deleting text after middle of a link ${kLinkDesc}(${test[0]})`,
innerHTML: `<p>${kParentTag[0]}<a href="about:blank">${kChildTag[0]}ab[cd${kChildTag[1]}</a>de]f${kParentTag[1]}</p>`,
run: async (utils) => {
await test[1](utils);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kNewContainerOfLink[0]}<a href="about:blank">ab</a>XY${kChildTag[1]}f${kParentTag[1]}</p>`,
`<p>${kNewContainerOfLink[0]}<a href="about:blank">ab</a>XY${kChildTag[1]}f${kParentTag[1]}<br></p>`,
],
});
addPromiseTest({
description: `Inserting "XY" after deleting text before middle of a link ${kLinkDesc}(${test[0]})`,
innerHTML: `<p>${kParentTag[0]}a[bc<a href="about:blank">${kChildTag[0]}de]f${kChildTag[1]}</a>${kParentTag[1]}</p>`,
run: async (utils) => {
await test[1](utils);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
`<p>${kParentTag[0]}aXY<a href="about:blank">${kChildTag[0]}f${kChildTag[1]}</a>${kParentTag[1]}</p>`,
`<p>${kParentTag[0]}aXY<a href="about:blank">${kChildTag[0]}f${kChildTag[1]}</a>${kParentTag[1]}<br></p>`,
],
});
if (kParentTag[0] !== "" || kChildTag[0] !== "") {
continue;
}
addPromiseTest({
description: `Inserting "XY" after deleting text between 2 same links (${test[0]})`,
innerHTML: '<p><a href="about:blank">a[bc</a><a href="about:blank">de]f</a></p>',
run: async (utils) => {
await test[1](utils);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
'<p><a href="about:blank">a</a>XY<a href="about:blank">f</a></p>',
'<p><a href="about:blank">a</a>XY<a href="about:blank">f</a><br></p>',
],
});
addPromiseTest({
description: `Inserting "XY" after deleting text between 2 different links (${test[0]})`,
innerHTML: '<p><a href="about:blank">a[bc</a><a href="http://example.com/">de]f</a></p>',
run: async (utils) => {
await test[1](utils);
await utils.sendKey("X", utils.kShiftKey);
await utils.sendKey("Y", utils.kShiftKey);
},
expectedResult: [
'<p><a href="about:blank">a</a>XY<a href="http://example.com/">f</a></p>',
'<p><a href="about:blank">a</a>XY<a href="http://example.com/">f</a><br></p>',
],
});
}
</script>
</body>
</html>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>window.originAgentCluster must be implied by cross-origin isolation</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe src="//{{domains[www1]}}:{{location[port]}}/html/browsers/origin/origin-keyed-agent-clusters/resources/coep-frame.html"></iframe>
<div id="log"></div>
<script type="module">
import { testGetter } from "../resources/helpers.mjs";
setup({ explicit_done: true });
window.onload = () => {
// Cross-origin isolated pages are always origin-keyed.
testGetter(self, true, "self");
// Child frames of cross-origin isolated pages must also be cross-origin
// isolated, and thus also origin-keyed. Make sure the implementation doesn't
// treat them specially in some wierd way, for the purposes of this
// implication.
testGetter(0, true, "child");
done();
};
</script>

View file

@ -0,0 +1,2 @@
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

View file

@ -0,0 +1,5 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>A page with COEP set that will respond when asked</title>
<script type="module" src="send-header-page-script.mjs"></script>

View file

@ -0,0 +1,2 @@
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: cross-origin

View file

@ -100,6 +100,13 @@ promise_test(async t => {
await pollResultAndCheck(t, id, ""); await pollResultAndCheck(t, id, "");
}, "Window.name is reset at the first cross-origin navigation"); }, "Window.name is reset at the first cross-origin navigation");
promise_test(async t => {
const id = token();
window.open(`resources/window-name.sub.html?open|navOpener=about:blank|reportOpener=${id}|closeOpener|close`, id, "noopener");
await pollResultAndCheck(t, id, id);
}, "window.name is not reset after navigating to an about:blank page from a non-about:blank page");
</script> </script>
</body> </body>
</html> </html>

View file

@ -40,6 +40,42 @@ async function proceedTest() {
break; break;
} }
if (step === "closeOpener") {
if (window.opener) {
window.opener.close();
}
continue;
}
if (step.startsWith("navOpener=")) {
if (!window.opener) {
continue;
}
let url = step.split("=")[1];
window.opener.location.href = url;
continue;
}
if (step === "open") {
const url = new URL(window.location);
url.host = "{{host}}:{{ports[https][0]}}";
url.search = "?" + steps.join("|");
window.open(url);
break;
}
if (step.startsWith("reportOpener=")) {
const id = step.split("=")[1];
const stashURL = new URL("window-name-stash.py", location);
stashURL.searchParams.set('id', id);
stashURL.searchParams.set('value', window.opener.name);
await fetch(stashURL, { method: "POST" });
continue;
}
if (step.startsWith("set=")) { if (step.startsWith("set=")) {
window.name = step.split("=")[1]; window.name = step.split("=")[1];
continue; continue;

View file

@ -1,5 +1,7 @@
"use strict"; "use strict";
// TODO: extend `EditorTestUtils` in editing/include/edit-test-utils.mjs
const kBackspaceKey = "\uE003"; const kBackspaceKey = "\uE003";
const kDeleteKey = "\uE017"; const kDeleteKey = "\uE017";
const kArrowRight = "\uE014"; const kArrowRight = "\uE014";
@ -323,7 +325,7 @@ function setupEditor(innerHTMLWithRangeMarkers) {
return { return {
marker: scanResult[0], marker: scanResult[0],
container: textNode, container: textNode,
offset: scanResult.index + offset, offset: scanResult.index + offset
}; };
} }
if (startContainer.nodeType === Node.TEXT_NODE) { if (startContainer.nodeType === Node.TEXT_NODE) {
@ -359,7 +361,7 @@ function setupEditor(innerHTMLWithRangeMarkers) {
return { return {
marker: scanResult[0], marker: scanResult[0],
container: textNode, container: textNode,
offset: scanResult.index + offset, offset: scanResult.index + offset
}; };
} }
if (startContainer.nodeType === Node.TEXT_NODE) { if (startContainer.nodeType === Node.TEXT_NODE) {

View file

@ -0,0 +1,85 @@
<!DOCTYPE html>
<title>Service Worker: Clients.matchAll with a blob URL worker client</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
const SCRIPT = 'resources/clients-matchall-worker.js';
promise_test(async (t) => {
const scope = 'resources/clients-matchall-blob-url-worker.html';
const reg = await service_worker_unregister_and_register(t, SCRIPT, scope);
t.add_cleanup(_ => reg.unregister());
await wait_for_state(t, reg.installing, 'activated');
const frame = await with_iframe(scope);
t.add_cleanup(_ => frame.remove());
{
const message = await frame.contentWindow.waitForWorker();
assert_equals(message.data, 'Worker is ready.',
'Worker should reply to the message.');
}
const channel = new MessageChannel();
const message = await new Promise(resolve => {
channel.port1.onmessage = resolve;
frame.contentWindow.navigator.serviceWorker.controller.postMessage(
{port: channel.port2, options: {type: 'worker'}}, [channel.port2]);
});
checkMessageEvent(message);
}, 'Test Clients.matchAll() with a blob URL worker client.');
promise_test(async (t) => {
const scope = 'resources/blank.html';
const reg = await service_worker_unregister_and_register(t, SCRIPT, scope);
t.add_cleanup(_ => reg.unregister());
await wait_for_state(t, reg.installing, 'activated');
const workerScript = `
self.onmessage = (e) => {
self.postMessage("Worker is ready.");
};
`;
const blob = new Blob([workerScript], { type: 'text/javascript' });
const blobUrl = URL.createObjectURL(blob);
const worker = new Worker(blobUrl);
{
const message = await new Promise(resolve => {
worker.onmessage = resolve;
worker.postMessage("Ping to worker.");
});
assert_equals(message.data, 'Worker is ready.',
'Worker should reply to the message.');
}
const channel = new MessageChannel();
const message = await new Promise(resolve => {
channel.port1.onmessage = resolve;
reg.active.postMessage(
{port: channel.port2,
options: {includeUncontrolled: true, type: 'worker'}},
[channel.port2]
);
});
checkMessageEvent(message);
}, 'Test Clients.matchAll() with an uncontrolled blob URL worker client.');
function checkMessageEvent(e) {
assert_equals(e.data.length, 1);
const workerClient = e.data[0];
assert_equals(workerClient[0], undefined); // visibilityState
assert_equals(workerClient[1], undefined); // focused
assert_true(workerClient[2].includes('blob:')); // url
assert_equals(workerClient[3], 'worker'); // type
assert_equals(workerClient[4], 'none'); // frameType
}
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<script>
const workerScript = `
self.onmessage = (e) => {
self.postMessage("Worker is ready.");
};
`;
const blob = new Blob([workerScript], { type: 'text/javascript' });
const blobUrl = URL.createObjectURL(blob);
const worker = new Worker(blobUrl);
function waitForWorker() {
return new Promise(resolve => {
worker.onmessage = resolve;
worker.postMessage("Ping to worker.");
});
}
</script>
</html>

View file

@ -1,3 +1,3 @@
# Streams Tests # Streams Tests
The work on the streams tests is closely tracked by the specification authors, who maintain a reference implementation intended to match the spec line-by-line while passing all of these tests. See [the whatwg/streams repository for details](https://github.com/whatwg/streams/tree/master/reference-implementation). Some tests may be in that repository while the spec sections they test are still undergoing heavy churn. The work on the streams tests is closely tracked by the specification authors, who maintain a reference implementation intended to match the spec line-by-line while passing all of these tests. See [the whatwg/streams repository for details](https://github.com/whatwg/streams/tree/main/reference-implementation). Some tests may be in that repository while the spec sections they test are still undergoing heavy churn.

View file

@ -1,3 +1,6 @@
parameters:
pyflag: --py3
steps: steps:
- script: ./wpt make-hosts-file | sudo tee -a /etc/hosts - script: ./wpt make-hosts-file | sudo tee -a /etc/hosts
displayName: 'Update hosts (macOS)' displayName: 'Update hosts (macOS)'
@ -5,6 +8,6 @@ steps:
- powershell: | - powershell: |
$hostFile = "$env:systemroot\System32\drivers\etc\hosts" $hostFile = "$env:systemroot\System32\drivers\etc\hosts"
Copy-Item -Path $hostFile -Destination "$hostFile.back" -Force Copy-Item -Path $hostFile -Destination "$hostFile.back" -Force
python wpt --py2 make-hosts-file | Out-File $env:systemroot\System32\drivers\etc\hosts -Encoding ascii -Append python wpt ${{ parameters.pyflag }} make-hosts-file | Out-File $env:systemroot\System32\drivers\etc\hosts -Encoding ascii -Append
displayName: 'Update hosts (Windows)' displayName: 'Update hosts (Windows)'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))

View file

@ -1,4 +1,7 @@
parameters:
pyflag: --py3
steps: steps:
# `python wpt` instead of `./wpt` is to make this work on Windows: # `python wpt` instead of `./wpt` is to make this work on Windows:
- script: python wpt --py2 manifest - script: python wpt ${{ parameters.pyflag }} manifest
displayName: 'Update manifest' displayName: 'Update manifest'

View file

@ -10,6 +10,7 @@
<link id="link_empty" /> <link id="link_empty" />
<link id="link_web_bundle_1" rel="webbundle" /> <link id="link_web_bundle_1" rel="webbundle" />
<link id="link_web_bundle_2" rel="webbundle" resources="foo" /> <link id="link_web_bundle_2" rel="webbundle" resources="foo" />
<link id="link_web_bundle_3" rel="webbundle" scopes="bar" />
<script> <script>
test(() => { test(() => {
assert_false( assert_false(
@ -22,6 +23,17 @@
); );
}, "resources must be defined on HTMLLinkElement prototype"); }, "resources must be defined on HTMLLinkElement prototype");
test(() => {
assert_false(
"scopes" in Element.prototype,
"scopes must not be defined on Element prototype"
);
assert_true(
"scopes" in HTMLLinkElement.prototype,
"scopes must be defined on HTMLLinkElement prototype"
);
}, "scopes must be defined on HTMLLinkElement prototype");
test(() => { test(() => {
const link = document.createElement("link"); const link = document.createElement("link");
assert_true(link.relList.supports("webbundle")); assert_true(link.relList.supports("webbundle"));
@ -55,6 +67,16 @@
"foo", "foo",
"resources attribute must return the specified value" "resources attribute must return the specified value"
); );
assert_equals(
document.querySelector("#link_web_bundle_2").getAttribute("scopes"),
null,
"scopes attribute must return null when the attribute is not given"
);
assert_equals(
document.querySelector("#link_web_bundle_3").getAttribute("scopes"),
"bar",
"scopes attribute must return the specified value"
);
// TODO: Test more variant of resoruces attribute values. // TODO: Test more variant of resoruces attribute values.
}, "resoruces attribute must return null or specified value"); }, "resoruces attribute must return null or specified value");
@ -75,5 +97,23 @@
"https://test2.example.com" "https://test2.example.com"
]); ]);
}, "resources must be DOMTokenList"); }, "resources must be DOMTokenList");
test(() => {
const link = document.createElement("link");
assert_class_string(link.scopes, "DOMTokenList");
assert_equals(
String(link.scopes.value),
"",
"scopes.value should return the empty list for an undefined scopes attribute"
);
link.setAttribute(
"scopes",
"https://test1.example.com https://test2.example.com "
);
assert_array_equals(link.scopes, [
"https://test1.example.com",
"https://test2.example.com"
]);
}, "scopes must be DOMTokenList");
</script> </script>
</body> </body>

View file

@ -80,7 +80,42 @@
assert_equals( assert_equals(
await loadScriptAndWaitReport(classic_script_url), await loadScriptAndWaitReport(classic_script_url),
'classic script from network'); 'classic script from network');
}, 'Dynamically loading classic script from web bundle'); }, 'Dynamically loading classic script from web bundle with link.resources');
promise_test(async () => {
const classic_script_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/dynamic/classic_script.js';
const scope = 'http://web-platform.test:8001/web-bundle/resources/wbn/dynamic/';
const link = document.createElement("link");
link.rel = "webbundle";
link.href = "../resources/wbn/dynamic1.wbn";
link.scopes.add(scope);
document.body.appendChild(link);
assert_equals(
await loadScriptAndWaitReport(classic_script_url),
'classic script from dynamic1.wbn');
link.href = "../resources/wbn/dynamic2.wbn";
// Loading the classic script should not reuse the previously loaded
// script. So in this case, the script must be loaded from dynamic2.wbn.
assert_equals(
await loadScriptAndWaitReport(classic_script_url),
'classic script from dynamic2.wbn');
// Changes the scope not to hit the classic_script.js.
link.scopes = scope + 'dummy';
// And in this case, the script must be loaded from network.
assert_equals(
await loadScriptAndWaitReport(classic_script_url),
'classic script from network');
// Adds the scope to hit the classic_script.js.
link.scopes.add(scope + 'classic_');
assert_equals(
await loadScriptAndWaitReport(classic_script_url),
'classic script from dynamic2.wbn');
document.body.removeChild(link);
// And in this case, the script must be loaded from network.
assert_equals(
await loadScriptAndWaitReport(classic_script_url),
'classic script from network');
}, 'Dynamically loading classic script from web bundle with link.scopes');
promise_test(() => { promise_test(() => {
return addLinkAndWaitForLoad("../resources/wbn/dynamic1.wbn?test-event"); return addLinkAndWaitForLoad("../resources/wbn/dynamic1.wbn?test-event");
@ -108,7 +143,19 @@
link.resources = url; link.resources = url;
document.body.appendChild(link); document.body.appendChild(link);
assert_equals(await loadScriptAndWaitReport(url), 'OK'); assert_equals(await loadScriptAndWaitReport(url), 'OK');
}, 'Subresource loading with urn:uuid: URL'); document.body.removeChild(link);
}, 'Subresource loading with urn:uuid: URL with link.resources');
promise_test(async () => {
const url = 'urn:uuid:020111b3-437a-4c5c-ae07-adb6bbffb720';
const link = document.createElement('link');
link.rel = 'webbundle';
link.href = '../resources/wbn/urn-uuid.wbn';
link.scopes = 'urn:uuid:';
document.body.appendChild(link);
assert_equals(await loadScriptAndWaitReport(url), 'OK');
document.body.removeChild(link);
}, 'Subresource loading with urn:uuid: URL with link.scopes');
promise_test(async () => { promise_test(async () => {
const wbn_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/subresource.wbn?test-resources-update'; const wbn_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/subresource.wbn?test-resources-update';

View file

@ -199,7 +199,7 @@ promise_test(async t => {
assert_equals(frame.cropWidth, 320, "cropWidth"); assert_equals(frame.cropWidth, 320, "cropWidth");
assert_equals(frame.cropHeight, 240, "cropHeight"); assert_equals(frame.cropHeight, 240, "cropHeight");
assert_equals(frame.timestamp, 0, "timestamp"); assert_equals(frame.timestamp, 0, "timestamp");
frame.destroy(); frame.close();
}); });
}, },
error(e) { error(e) {

View file

@ -222,14 +222,14 @@ promise_test(async t => {
encoder.encode(frame); encoder.encode(frame);
// |frame| is not longer valid since it has been destroyed. // |frame| is not longer valid since it has been closed.
assert_not_equals(frame.timestamp, timestamp); assert_not_equals(frame.timestamp, timestamp);
assert_throws_dom("InvalidStateError", () => frame.clone()); assert_throws_dom("InvalidStateError", () => frame.clone());
encoder.close(); encoder.close();
return endAfterEventLoopTurn(); return endAfterEventLoopTurn();
}, 'Test encoder consumes (destroys) frames.'); }, 'Test encoder consumes (closes) frames.');
promise_test(async t => { promise_test(async t => {
let encoder = new VideoEncoder(getDefaultCodecInit(t)); let encoder = new VideoEncoder(getDefaultCodecInit(t));
@ -251,12 +251,11 @@ promise_test(async t => {
let encoder = new VideoEncoder(getDefaultCodecInit(t)); let encoder = new VideoEncoder(getDefaultCodecInit(t));
let frame = await createVideoFrame(640, 480, 0); let frame = await createVideoFrame(640, 480, 0);
frame.destroy(); frame.close();
encoder.configure(defaultConfig); encoder.configure(defaultConfig);
frame.destroy();
assert_throws_dom("OperationError", () => { assert_throws_dom("OperationError", () => {
encoder.encode(frame) encoder.encode(frame)
}); });
}, 'Verify encoding destroyed frames throws.'); }, 'Verify encoding closed frames throws.');

View file

@ -25,8 +25,8 @@ test(t => {
assert_equals(frame.cropWidth, clone.cropWidth); assert_equals(frame.cropWidth, clone.cropWidth);
assert_equals(frame.cropHeight, clone.cropHeight); assert_equals(frame.cropHeight, clone.cropHeight);
frame.destroy(); frame.close();
clone.destroy(); clone.close();
}, 'Test we can clone a VideoFrame.'); }, 'Test we can clone a VideoFrame.');
test(t => { test(t => {
@ -35,23 +35,23 @@ test(t => {
let copy = frame; let copy = frame;
let clone = frame.clone(); let clone = frame.clone();
frame.destroy(); frame.close();
assert_not_equals(copy.timestamp, defaultInit.timestamp); assert_not_equals(copy.timestamp, defaultInit.timestamp);
assert_equals(clone.timestamp, defaultInit.timestamp); assert_equals(clone.timestamp, defaultInit.timestamp);
clone.destroy(); clone.close();
}, 'Verify destroying a frame doesn\'t affect its clones.'); }, 'Verify closing a frame doesn\'t affect its clones.');
test(t => { test(t => {
let frame = createDefaultVideoFrame(); let frame = createDefaultVideoFrame();
frame.destroy(); frame.close();
assert_throws_dom("InvalidStateError", () => { assert_throws_dom("InvalidStateError", () => {
let clone = frame.clone(); let clone = frame.clone();
}); });
}, 'Verify cloning a destroyed frame throws.'); }, 'Verify cloning a closed frame throws.');
async_test(t => { async_test(t => {
let localFrame = createDefaultVideoFrame(); let localFrame = createDefaultVideoFrame();
@ -62,7 +62,7 @@ async_test(t => {
externalPort.onmessage = t.step_func((e) => { externalPort.onmessage = t.step_func((e) => {
let externalFrame = e.data; let externalFrame = e.data;
externalFrame.destroy(); externalFrame.close();
externalPort.postMessage("Done"); externalPort.postMessage("Done");
}) })
@ -72,7 +72,7 @@ async_test(t => {
localPort.postMessage(localFrame); localPort.postMessage(localFrame);
}, 'Verify destroying frames propagates accross contexts.'); }, 'Verify closing frames propagates accross contexts.');
async_test(t => { async_test(t => {
let localFrame = createDefaultVideoFrame(); let localFrame = createDefaultVideoFrame();
@ -83,18 +83,18 @@ async_test(t => {
externalPort.onmessage = t.step_func((e) => { externalPort.onmessage = t.step_func((e) => {
let externalFrame = e.data; let externalFrame = e.data;
externalFrame.destroy(); externalFrame.close();
externalPort.postMessage("Done"); externalPort.postMessage("Done");
}) })
localPort.onmessage = t.step_func_done((e) => { localPort.onmessage = t.step_func_done((e) => {
assert_equals(localFrame.timestamp, defaultInit.timestamp); assert_equals(localFrame.timestamp, defaultInit.timestamp);
localFrame.destroy(); localFrame.close();
}) })
localPort.postMessage(localFrame.clone()); localPort.postMessage(localFrame.clone());
}, 'Verify destroying cloned frames doesn\'t propagate accross contexts.'); }, 'Verify closing cloned frames doesn\'t propagate accross contexts.');
async_test(t => { async_test(t => {
let localFrame = createDefaultVideoFrame(); let localFrame = createDefaultVideoFrame();
@ -104,11 +104,11 @@ async_test(t => {
localPort.onmessage = t.unreached_func(); localPort.onmessage = t.unreached_func();
localFrame.destroy(); localFrame.close();
assert_throws_dom("DataCloneError", () => { assert_throws_dom("DataCloneError", () => {
localPort.postMessage(localFrame); localPort.postMessage(localFrame);
}); });
t.done(); t.done();
}, 'Verify posting destroyed frames throws.'); }, 'Verify posting closed frames throws.');

View file

@ -12,7 +12,7 @@ test(t => {
assert_equals(frame.cropWidth, 32, "displayWidth"); assert_equals(frame.cropWidth, 32, "displayWidth");
assert_equals(frame.cropHeight, 16, "displayHeight"); assert_equals(frame.cropHeight, 16, "displayHeight");
frame.destroy(); frame.close();
}, 'Test we can construct a VideoFrame.'); }, 'Test we can construct a VideoFrame.');
test(t => { test(t => {
@ -24,7 +24,7 @@ test(t => {
assert_equals(frame.cropWidth, 1, "displayWidth"); assert_equals(frame.cropWidth, 1, "displayWidth");
assert_equals(frame.cropHeight, 1, "displayHeight"); assert_equals(frame.cropHeight, 1, "displayHeight");
frame.destroy(); frame.close();
}, 'Test we can construct an odd-sized VideoFrame.'); }, 'Test we can construct an odd-sized VideoFrame.');
test(t => { test(t => {
@ -63,7 +63,7 @@ test(t => {
// guarantees about the color space. // guarantees about the color space.
assert_equals(view[0], 94, "Y value at (0, 0)"); assert_equals(view[0], 94, "Y value at (0, 0)");
frame.destroy(); frame.close();
}, 'Test we can read planar data from a VideoFrame.'); }, 'Test we can read planar data from a VideoFrame.');
test(t => { test(t => {
@ -78,15 +78,15 @@ test(t => {
assert_equals(frame.planes.length, 3, "number of planes"); assert_equals(frame.planes.length, 3, "number of planes");
// Attempt to read Y plane data, but destroy the frame first. // Attempt to read Y plane data, but close the frame first.
let yPlane = frame.planes[0]; let yPlane = frame.planes[0];
let yLength = yPlane.length; let yLength = yPlane.length;
frame.destroy(); frame.close();
let buffer = new ArrayBuffer(yLength); let buffer = new ArrayBuffer(yLength);
let view = new Uint8Array(buffer); let view = new Uint8Array(buffer);
assert_throws_dom("InvalidStateError", () => yPlane.readInto(view)); assert_throws_dom("InvalidStateError", () => yPlane.readInto(view));
}, 'Test we cannot read planar data from a destroyed VideoFrame.'); }, 'Test we cannot read planar data from a closed VideoFrame.');
test(t => { test(t => {
let image = makeImageBitmap(32, 16); let image = makeImageBitmap(32, 16);

View file

@ -38,7 +38,7 @@ promise_test(async function(t) {
assert_equals(frame.codedWidth, testVideo.width); assert_equals(frame.codedWidth, testVideo.width);
assert_equals(frame.codedHeight, testVideo.height); assert_equals(frame.codedHeight, testVideo.height);
assert_not_equals(frame.timestamp, null); assert_not_equals(frame.timestamp, null);
frame.destroy(); frame.close();
if (++numberFrames == 5) { if (++numberFrames == 5) {
vtr.stop(); vtr.stop();

View file

@ -0,0 +1,219 @@
<!doctype html>
<meta charset=utf-8>
<title>RTCRtpParameters encodings</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/webrtc/dictionary-helper.js"></script>
<script src="/webrtc/RTCRtpParameters-helper.js"></script>
<script>
'use strict';
async function negotiate(pc1, pc2) {
const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
await pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
await pc1.setRemoteDescription(answer);
}
test(function(t) {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('video');
const capabilities = transceiver.headerExtensionsToOffer;
let capability = capabilities.find((capability) => {
return capability.uri == "urn:ietf:params:rtp-hdrext:sdes:mid" &&
capability.direction != "stopped";
});
assert_not_equals(capability, undefined);
capability = capabilities.find((capability) => {
return capability.uri == "urn:3gpp:video-orientation" &&
capability.direction != "stopped";
});
assert_not_equals(capability, undefined);
}, `the video transceiver.headerExtensionsToOffer() includes mandatory extensions`);
test(function(t) {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('audio');
const capabilities = transceiver.headerExtensionsToOffer;
let capability = capabilities.find((capability) => {
return capability.uri == "urn:ietf:params:rtp-hdrext:sdes:mid" &&
capability.direction != "stopped";
});
assert_not_equals(capability, undefined);
}, `the audio transceiver.headerExtensionsToOffer() includes mandatory extensions`);
test(function(t) {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('audio');
const capabilities = transceiver.headerExtensionsToOffer;
capabilities[0].uri = "";
assert_throws_js(TypeError, () => {
transceiver.setOfferedRtpHeaderExtensions(capabilities);
}, 'transceiver should throw TypeError when setting an empty URI');
}, `setOfferedRtpHeaderExtensions throws TypeError on encountering missing URI`);
test(function(t) {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('audio');
const capabilities = transceiver.headerExtensionsToOffer;
capabilities[0].uri = "4711";
assert_throws_dom("NotSupportedError", () => {
transceiver.setOfferedRtpHeaderExtensions(capabilities);
}, 'transceiver should throw NotSupported when setting an unknown URI');
}, `setOfferedRtpHeaderExtensions throws NotSupported on encountering unknown URI`);
test(function(t) {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('audio');
const capabilities = transceiver.headerExtensionsToOffer;
let capability = capabilities.find((capability) => {
return capability.uri == "urn:ietf:params:rtp-hdrext:sdes:mid";
});
["sendonly", "recvonly", "inactive", "stopped"].map(direction => {
capability.direction = direction;
assert_throws_dom("InvalidModificationError", () => {
transceiver.setOfferedRtpHeaderExtensions(capabilities);
}, `transceiver should throw InvalidModificationError when setting a mandatory header extension\'s direction to ${direction}`);
});
}, `setOfferedRtpHeaderExtensions throws InvalidModificationError when setting a mandatory header extension\'s direction to something else than "sendrecv"`);
test(function(t) {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('audio');
let capabilities = transceiver.headerExtensionsToOffer;
let selected_capability = capabilities.find((capability) => {
return capability.direction == "sendrecv" &&
capability.uri != "urn:ietf:params:rtp-hdrext:sdes:mid";
});
selected_capability.direction = "stopped";
const offered_capabilities = transceiver.headerExtensionsToOffer;
let altered_capability = capabilities.find((capability) => {
return capability.uri == selected_capability.uri &&
capability.direction == "stopped";
});
assert_not_equals(altered_capability, undefined);
}, `modified direction set by setOfferedRtpHeaderExtensions is visible in headerExtensionsOffered`);
promise_test(async t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('video');
const capabilities = transceiver.headerExtensionsToOffer;
const offer = await pc.createOffer();
const extmaps = offer
.sdp
.split("\n")
.filter(line => { return line.includes("a=extmap"); })
.join("\n");
for (const capability of capabilities) {
if (capability.direction == "stopped") {
assert_false(extmaps.includes(capability.uri));
} else {
assert_true(extmaps.includes(capability.uri));
}
}
}, `unstopped extensions turn up in offer`);
promise_test(async t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const transceiver = pc.addTransceiver('video');
const capabilities = transceiver.headerExtensionsToOffer;
const selected_capability = capabilities.find((capability) => {
return capability.direction == "sendrecv" &&
capability.uri != "urn:ietf:params:rtp-hdrext:sdes:mid" &&
capability.uri != "urn:3gpp:video-orientation";
});
selected_capability.direction = "stopped";
transceiver.setOfferedRtpHeaderExtensions(capabilities);
const offer = await pc.createOffer();
const extmaps = offer
.sdp
.split("\n")
.filter(line => { return line.includes("a=extmap"); })
.join("\n");
for (const capability of capabilities) {
if (capability.direction == "stopped") {
assert_false(extmaps.includes(capability.uri));
} else {
assert_true(extmaps.includes(capability.uri));
}
}
}, `stopped extensions do not turn up in offers`);
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
// Disable a non-mandatory extension before first negotiation.
const transceiver = pc1.addTransceiver('video');
const capabilities = transceiver.headerExtensionsToOffer;
const selected_capability = capabilities.find((capability) => {
return capability.direction == "sendrecv" &&
capability.uri != "urn:ietf:params:rtp-hdrext:sdes:mid" &&
capability.uri != "urn:3gpp:video-orientation";
});
selected_capability.direction = "stopped";
transceiver.setOfferedRtpHeaderExtensions(capabilities);
await negotiate(pc1, pc2);
const negotiated_capabilites = transceiver.headerExtensionsNegotiated;
// Attempt enabling the extension.
selected_capability.direction = "sendrecv";
// The enabled extension should not be part of the negotiated set.
transceiver.setOfferedRtpHeaderExtensions(capabilities);
await negotiate(pc1, pc2);
assert_not_equals(
transceiver.headerExtensionsNegotiated.find(capability => {
return capability.uri == selected_capability.uri &&
capability.direction == "sendrecv";
}), undefined);
}, `the set of negotiated extensions grows with subsequent offers`);
promise_test(async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
// Disable a non-mandatory extension before first negotiation.
const transceiver = pc1.addTransceiver('video');
const capabilities = transceiver.headerExtensionsToOffer;
const selected_capability = capabilities.find((capability) => {
return capability.direction == "sendrecv" &&
capability.uri != "urn:ietf:params:rtp-hdrext:sdes:mid" &&
capability.uri != "urn:3gpp:video-orientation";
});
selected_capability.direction = "stopped";
transceiver.setOfferedRtpHeaderExtensions(capabilities);
await negotiate(pc1, pc2);
const negotiated_capabilites = transceiver.headerExtensionsNegotiated;
for (const capability of negotiated_capabilites) {
assert_not_equals(capabilities.find((cap) => {
return cap.uri == capability.uri && cap.direction != "stopped";
}), undefined);
}
for (const capability of capabilities) {
if (capability.direction != "stopped") {
assert_not_equals(negotiated_capabilites.find((cap) => {
return cap.uri == capability.uri;
}), undefined);
}
}
}, `the set of negotiated extensions is the set of unstopped extensions`);
</script>