mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Update web-platform-tests to revision a184aa4fd5cd8f92eb87ce0035f257f2a4c7c0b2
This commit is contained in:
parent
e1065fa22a
commit
c7e8937c37
84 changed files with 2653 additions and 218 deletions
|
@ -4,7 +4,7 @@
|
|||
expected: TIMEOUT
|
||||
|
||||
[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.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[hit-test-floats-005.html]
|
||||
[Miss clipped float]
|
||||
expected: FAIL
|
||||
|
|
@ -309,15 +309,6 @@
|
|||
[Response: combined response Content-Type: text/html;" \\" text/plain ";charset=GBK]
|
||||
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 */*]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -330,3 +321,9 @@
|
|||
[<iframe>: combined response Content-Type: text/html */*;charset=gbk]
|
||||
expected: FAIL
|
||||
|
||||
[<iframe>: combined response Content-Type: text/html;" text/plain]
|
||||
expected: FAIL
|
||||
|
||||
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -56,6 +56,3 @@
|
|||
[separate text/javascript x/x]
|
||||
expected: FAIL
|
||||
|
||||
[separate text/javascript ]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
[X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
|
||||
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
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[traverse_the_history_1.html]
|
||||
[Multiple history traversals from the same task]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[traverse_the_history_4.html]
|
||||
[Multiple history traversals, last would be aborted]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[traverse_the_history_5.html]
|
||||
[Multiple history traversals, last would be aborted]
|
||||
expected: FAIL
|
||||
|
|
@ -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
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
[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]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[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]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[iframe_sandbox_popups_nonescaping-1.html]
|
||||
expected: TIMEOUT
|
||||
expected: CRASH
|
||||
[Check that popups from a sandboxed iframe do not escape the sandbox]
|
||||
expected: NOTRUN
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[iframe_sandbox_popups_nonescaping-2.html]
|
||||
expected: TIMEOUT
|
||||
expected: CRASH
|
||||
[Check that popups from a sandboxed iframe do not escape the sandbox]
|
||||
expected: NOTRUN
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[form-double-submit-2.html]
|
||||
[preventDefault should allow onclick submit() to succeed]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[form-double-submit-3.html]
|
||||
[<button> should have the same double-submit protection as <input type=submit>]
|
||||
expected: FAIL
|
||||
|
|
@ -2,9 +2,6 @@
|
|||
[input type search: setSelectionRange out of range a second time (must not fire select)]
|
||||
expected: FAIL
|
||||
|
||||
[input type text: selectionStart a second time (must not fire select)]
|
||||
expected: FAIL
|
||||
|
||||
[textarea: selectionDirection a second time (must not fire select)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -17,30 +14,12 @@
|
|||
[input type tel: selectionEnd out of range a second time (must not fire select)]
|
||||
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)]
|
||||
expected: FAIL
|
||||
|
||||
[input type url: selectionStart out of range a second time (must not fire select)]
|
||||
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)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -50,30 +29,51 @@
|
|||
[input type search: selectionStart out of range a second time (must not fire select)]
|
||||
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)]
|
||||
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
|
||||
|
||||
[input type url: selectionStart a second time (must not fire select)]
|
||||
[input type text: selectionEnd a second time (must not fire select)]
|
||||
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
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[module-delayed.html]
|
||||
[async document.write in a module]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[module-static-import-delayed.html]
|
||||
[document.write in an imported module]
|
||||
expected: FAIL
|
||||
|
|
@ -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]
|
||||
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
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
[promise-job-entry.html]
|
||||
expected: TIMEOUT
|
||||
[Fulfillment handler on fulfilled promise]
|
||||
expected: FAIL
|
||||
|
||||
[Rejection handler on pending-then-rejected promise]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Sanity check: this all works as expected with no promises involved]
|
||||
expected: FAIL
|
||||
|
@ -15,5 +16,5 @@
|
|||
expected: FAIL
|
||||
|
||||
[Fulfillment handler on pending-then-fulfilled promise]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -539,3 +539,9 @@
|
|||
[X SNR (43.824040882292394 dB) is not greater than or equal to 65.737. Got 43.824040882292394.]
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[017.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, about:blank]
|
||||
expected: TIMEOUT
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[017.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, about:blank]
|
||||
expected: TIMEOUT
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
[018.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, javascript:]
|
||||
expected: TIMEOUT
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
[Worker-constructor.html]
|
||||
expected: ERROR
|
|
@ -7,7 +7,7 @@
|
|||
expected: FAIL
|
||||
|
||||
[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.]
|
||||
expected: TIMEOUT
|
||||
|
|
|
@ -176521,6 +176521,45 @@
|
|||
]
|
||||
},
|
||||
"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": [
|
||||
"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": [
|
||||
"9a680d10d9ff61fc7173118bbfac4fae5b08ff97",
|
||||
[
|
||||
|
@ -259174,7 +259226,7 @@
|
|||
},
|
||||
"support": {
|
||||
".azure-pipelines.yml": [
|
||||
"155143ef00592c713609a0e7d3e48ddd916b874e",
|
||||
"1c9883ccdec0532499b8df805527ea8e8c37c32f",
|
||||
[]
|
||||
],
|
||||
".codecov.yml": [
|
||||
|
@ -319163,6 +319215,18 @@
|
|||
"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": [
|
||||
"28b00184c2ef5f33f6a2e8927233f6f7f42b1973",
|
||||
[]
|
||||
|
@ -319172,6 +319236,10 @@
|
|||
"f3eaa06ac9b7c48479d439041ce51575ad8cc072",
|
||||
[]
|
||||
],
|
||||
"scroll-snap-writing-mode-000-ref.html": [
|
||||
"fe2d4074e26e0bb4417871be39fb37408aa2c498",
|
||||
[]
|
||||
],
|
||||
"snap-after-initial-layout-ref.html": [
|
||||
"c8009b626cb63ebcef4a211f8b61249faf36c72f",
|
||||
[]
|
||||
|
@ -335323,6 +335391,10 @@
|
|||
]
|
||||
},
|
||||
"include": {
|
||||
"editor-test-utils.js": [
|
||||
"3ca014a472fa45991e38d565a10fe9dc9afa6ff8",
|
||||
[]
|
||||
],
|
||||
"implementation.js": [
|
||||
"44a7afd82d25fdee8b023e3a84a650e4a134de0e",
|
||||
[]
|
||||
|
@ -340152,6 +340224,10 @@
|
|||
[]
|
||||
],
|
||||
"getter-special-cases": {
|
||||
"cross-origin-isolated.sub.https.html.headers": [
|
||||
"5f8621ef83660c66f0d037ea28fafefb558140f1",
|
||||
[]
|
||||
],
|
||||
"csp-sandbox-no.https.html.headers": [
|
||||
"4705ce9dedeeabf8208bf602176511c0cbe2cb76",
|
||||
[]
|
||||
|
@ -340264,6 +340340,14 @@
|
|||
"9292fe3894e4a82f77a8877b76faa19fe6dfdb47",
|
||||
[]
|
||||
],
|
||||
"coep-frame.html": [
|
||||
"7cbd89b943ffcae6b6038b1caf905783bbd4aab3",
|
||||
[]
|
||||
],
|
||||
"coep-frame.html.headers": [
|
||||
"4e798cd9f5d3f756df077a43ce9a1a6f9b41fd28",
|
||||
[]
|
||||
],
|
||||
"crashy-popup.sub.html": [
|
||||
"45c8d5074d22b3233d8bee4c6d1236e8899009e4",
|
||||
[]
|
||||
|
@ -340772,7 +340856,7 @@
|
|||
[]
|
||||
],
|
||||
"window-name.sub.html": [
|
||||
"378f588e6b3ba0847a64795d1bca047ba6aa6b8a",
|
||||
"0fa874b2cfc2cfe6d1a0a79348857290215a3ef4",
|
||||
[]
|
||||
],
|
||||
"window-opener.html": [
|
||||
|
@ -352321,7 +352405,7 @@
|
|||
[]
|
||||
],
|
||||
"input-events-get-target-ranges.js": [
|
||||
"f9404e07a5564c64a2863a1329728517fedd5ca7",
|
||||
"004416ec2a02e0abc254f9b2b7d0a0c41b751a7d",
|
||||
[]
|
||||
]
|
||||
},
|
||||
|
@ -360752,6 +360836,10 @@
|
|||
"8effa56c98c9f921201553121308591e7431a201",
|
||||
[]
|
||||
],
|
||||
"clients-matchall-blob-url-worker.html": [
|
||||
"ee89a0d8b3ee7513ff3817632db32a9f5f2c162a",
|
||||
[]
|
||||
],
|
||||
"clients-matchall-client-types-dedicated-worker.js": [
|
||||
"5a3f04d33aaae64eb8abed16dc36e8181fed9de6",
|
||||
[]
|
||||
|
@ -362449,7 +362537,7 @@
|
|||
[]
|
||||
],
|
||||
"README.md": [
|
||||
"fd964cf7e5b75db408e0b5813cb94172186f5c26",
|
||||
"9ab6e1284ad50d2982ea1f6fc78d7a519e796460",
|
||||
[]
|
||||
],
|
||||
"resources": {
|
||||
|
@ -363625,11 +363713,11 @@
|
|||
[]
|
||||
],
|
||||
"update_hosts.yml": [
|
||||
"2036c419380b798eb5f11bfc1508e0ac44cf6249",
|
||||
"64aff7cf46a83e0ad0045065b5ec2e78deb6506d",
|
||||
[]
|
||||
],
|
||||
"update_manifest.yml": [
|
||||
"b636b23a761ed033c160c8d64589e18506459c19",
|
||||
"e8f28217f1eaa9af3ec67e2b1a64365c8aa87a81",
|
||||
[]
|
||||
]
|
||||
},
|
||||
|
@ -390553,7 +390641,7 @@
|
|||
]
|
||||
],
|
||||
"DOMException-constructor-behavior.any.js": [
|
||||
"d6e1cdd451ca8de2a6596d0fd31594c15f7318b3",
|
||||
"e9917af2287490c77543146a660d57c822cccf2e",
|
||||
[
|
||||
"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": [
|
||||
"1490bf06f55a6c2b1e10afc044b19b108e5dd482",
|
||||
[
|
||||
|
@ -461370,6 +461576,13 @@
|
|||
]
|
||||
],
|
||||
"getter-special-cases": {
|
||||
"cross-origin-isolated.sub.https.html": [
|
||||
"e10d3452b91db3dc6a188bf4113833b699224ff1",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"csp-sandbox-no.https.html": [
|
||||
"e0b5f92376287417ab099c49e0b7396dc0d87256",
|
||||
[
|
||||
|
@ -462518,7 +462731,7 @@
|
|||
]
|
||||
],
|
||||
"clear-window-name.https.html": [
|
||||
"39798f1e232f2ba109d64afdcf5031b06620207b",
|
||||
"27601eb7f6db164cde6ac1d0ecda0d69dfab1a75",
|
||||
[
|
||||
null,
|
||||
{
|
||||
|
@ -530306,6 +530519,13 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"clients-matchall-blob-url-worker.https.html": [
|
||||
"c29bac8b894a2c50828ae3469d3e181d227fe7ac",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"clients-matchall-client-types.https.html": [
|
||||
"54f182b6202cd67cc63e159e7e2d4294fdf975ff",
|
||||
[
|
||||
|
@ -549449,7 +549669,7 @@
|
|||
"web-bundle": {
|
||||
"subresource-loading": {
|
||||
"link-web-bundle.tentative.html": [
|
||||
"7f6de1014cf16be90a2f8ba0551c7c02c92219eb",
|
||||
"2f10cd6b3fdd4dd94dc5767df663583cd8d81071",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
@ -549463,7 +549683,7 @@
|
|||
]
|
||||
],
|
||||
"subresource-loading-from-web-bundle.tentative.html": [
|
||||
"53a9c1b2cc707271c8d7b0e7fb7385fd59839744",
|
||||
"6f38f145bc8795c51961e08fdf09ea4e590d26eb",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
@ -552794,7 +553014,7 @@
|
|||
]
|
||||
],
|
||||
"video-decoder.any.js": [
|
||||
"33ea2dbe375d5446e68e90405f9867ce8f7fb272",
|
||||
"44e7375a78c013d0a9d0f60a33a04651473c48fb",
|
||||
[
|
||||
"webcodecs/video-decoder.any.html",
|
||||
{
|
||||
|
@ -552827,7 +553047,7 @@
|
|||
]
|
||||
],
|
||||
"video-encoder.any.js": [
|
||||
"77fe184bafa84786315c3375936dc61927ca304b",
|
||||
"d86e6b61f2039acdf882f7551c7027967eec3ce2",
|
||||
[
|
||||
"webcodecs/video-encoder.any.html",
|
||||
{
|
||||
|
@ -552868,7 +553088,7 @@
|
|||
]
|
||||
],
|
||||
"video-frame-serialization.any.js": [
|
||||
"524f94374f417ae08798a2d1eeafbc012c028a41",
|
||||
"338f721da8f89e32b63a00dff5d1e62e7b60aa62",
|
||||
[
|
||||
"webcodecs/video-frame-serialization.any.html",
|
||||
{
|
||||
|
@ -552909,7 +553129,7 @@
|
|||
]
|
||||
],
|
||||
"video-frame.any.js": [
|
||||
"9eb6699c06ae541b7e2e3911a913b29a7f5d83cf",
|
||||
"14cce43baf144aedc57eab1a84dd8517dc2804e0",
|
||||
[
|
||||
"webcodecs/video-frame.any.html",
|
||||
{
|
||||
|
@ -552942,7 +553162,7 @@
|
|||
]
|
||||
],
|
||||
"video-track-reader.html": [
|
||||
"b5d610e9eacf72ee4339f79643e8fe54cb90fd24",
|
||||
"925e8374f58f4e1983d9ed7d05c0324a9028c22a",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
@ -555141,6 +555361,13 @@
|
|||
"timeout": "long"
|
||||
}
|
||||
]
|
||||
],
|
||||
"RTCRtpTransceiver-headerExtensionControl.html": [
|
||||
"e823bd830c6e34c159dc511c05721a40b69586b1",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
]
|
||||
},
|
||||
"webrtc-identity": {
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[hit-test-floats-005.html]
|
||||
[Miss clipped float]
|
||||
expected: FAIL
|
||||
|
|
@ -309,15 +309,6 @@
|
|||
[fetch(): separate response Content-Type: text/plain ]
|
||||
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 */*]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -330,3 +321,9 @@
|
|||
[<iframe>: combined response Content-Type: text/html */*;charset=gbk]
|
||||
expected: FAIL
|
||||
|
||||
[<iframe>: combined response Content-Type: text/html;" text/plain]
|
||||
expected: FAIL
|
||||
|
||||
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -56,6 +56,3 @@
|
|||
[separate text/javascript x/x]
|
||||
expected: FAIL
|
||||
|
||||
[separate text/javascript ]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
[X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
|
||||
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
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[traverse_the_history_1.html]
|
||||
[Multiple history traversals from the same task]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[traverse_the_history_4.html]
|
||||
[Multiple history traversals, last would be aborted]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[traverse_the_history_5.html]
|
||||
[Multiple history traversals, last would be aborted]
|
||||
expected: FAIL
|
||||
|
|
@ -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
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[iframe_sandbox_popups_escaping-1.html]
|
||||
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]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
[iframe_sandbox_popups_escaping-3.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[iframe_sandbox_popups_nonescaping-1.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
expected: CRASH
|
||||
[Check that popups from a sandboxed iframe do not escape the sandbox]
|
||||
expected: NOTRUN
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[iframe_sandbox_popups_nonescaping-2.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
expected: CRASH
|
||||
[Check that popups from a sandboxed iframe do not escape the sandbox]
|
||||
expected: NOTRUN
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
[form-double-submit-2.html]
|
||||
[preventDefault should allow onclick submit() to succeed]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[form-double-submit-3.html]
|
||||
[<button> should have the same double-submit protection as <input type=submit>]
|
||||
expected: FAIL
|
||||
|
|
@ -2,9 +2,6 @@
|
|||
[input type search: setSelectionRange out of range a second time (must not fire select)]
|
||||
expected: FAIL
|
||||
|
||||
[input type text: selectionStart a second time (must not fire select)]
|
||||
expected: FAIL
|
||||
|
||||
[textarea: selectionDirection a second time (must not fire select)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -17,30 +14,12 @@
|
|||
[input type tel: selectionEnd out of range a second time (must not fire select)]
|
||||
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)]
|
||||
expected: FAIL
|
||||
|
||||
[input type url: selectionStart out of range a second time (must not fire select)]
|
||||
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)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -50,30 +29,51 @@
|
|||
[input type search: selectionStart out of range a second time (must not fire select)]
|
||||
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)]
|
||||
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
|
||||
|
||||
[input type url: selectionStart a second time (must not fire select)]
|
||||
[input type text: selectionEnd a second time (must not fire select)]
|
||||
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
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[module-delayed.html]
|
||||
[async document.write in a module]
|
||||
expected: FAIL
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
[module-static-import-delayed.html]
|
||||
[document.write in an imported module]
|
||||
expected: FAIL
|
||||
|
|
@ -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]
|
||||
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
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
[promise-job-entry.html]
|
||||
expected: TIMEOUT
|
||||
[Fulfillment handler on fulfilled promise]
|
||||
expected: FAIL
|
||||
|
||||
[Rejection handler on pending-then-rejected promise]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Sanity check: this all works as expected with no promises involved]
|
||||
expected: FAIL
|
||||
|
@ -15,5 +16,5 @@
|
|||
expected: FAIL
|
||||
|
||||
[Fulfillment handler on pending-then-fulfilled promise]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
|
|
|
@ -767,3 +767,9 @@
|
|||
[X SNR (43.824040882292394 dB) is not greater than or equal to 65.737. Got 43.824040882292394.]
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
[017.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, about:blank]
|
||||
expected: TIMEOUT
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
[017.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, about:blank]
|
||||
expected: TIMEOUT
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
[018.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, javascript:]
|
||||
expected: TIMEOUT
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
[Worker-constructor.html]
|
||||
expected: ERROR
|
|
@ -233,7 +233,11 @@ jobs:
|
|||
- template: tools/ci/azure/install_chrome.yml
|
||||
- template: tools/ci/azure/install_firefox.yml
|
||||
- template: tools/ci/azure/update_hosts.yml
|
||||
parameters:
|
||||
pyflag: --py2
|
||||
- template: tools/ci/azure/update_manifest.yml
|
||||
parameters:
|
||||
pyflag: --py2
|
||||
- template: tools/ci/azure/tox_pytest.yml
|
||||
parameters:
|
||||
directory: tools/wpt/
|
||||
|
@ -403,7 +407,11 @@ jobs:
|
|||
# - template: tools/ci/azure/install_chrome.yml
|
||||
# - template: tools/ci/azure/install_firefox.yml
|
||||
- template: tools/ci/azure/update_hosts.yml
|
||||
parameters:
|
||||
pyflag: --py2
|
||||
- template: tools/ci/azure/update_manifest.yml
|
||||
parameters:
|
||||
pyflag: --py2
|
||||
- template: tools/ci/azure/tox_pytest.yml
|
||||
parameters:
|
||||
directory: tools/wpt/
|
||||
|
|
|
@ -100,6 +100,7 @@ test(function() {
|
|||
{name: "InvalidModificationError", code: 13},
|
||||
{name: "NamespaceError", code: 14},
|
||||
{name: "InvalidAccessError", code: 15},
|
||||
{name: "TypeMismatchError", code: 17},
|
||||
{name: "SecurityError", code: 18},
|
||||
{name: "NetworkError", code: 19},
|
||||
{name: "AbortError", code: 20},
|
||||
|
@ -107,7 +108,25 @@ test(function() {
|
|||
{name: "QuotaExceededError", code: 22},
|
||||
{name: "TimeoutError", code: 23},
|
||||
{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) {
|
||||
test(function() {
|
||||
var ex = new DOMException("msg", test_case.name);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>CSS Reference: No Red</title>
|
||||
|
||||
<p>Test passes if there is no red.
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 -->
|
|
@ -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 target‘s 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 -->
|
|
@ -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`;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,2 @@
|
|||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Opener-Policy: same-origin
|
|
@ -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>
|
|
@ -0,0 +1,2 @@
|
|||
Cross-Origin-Embedder-Policy: require-corp
|
||||
Cross-Origin-Resource-Policy: cross-origin
|
|
@ -100,6 +100,13 @@ promise_test(async t => {
|
|||
await pollResultAndCheck(t, id, "");
|
||||
}, "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>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -40,6 +40,42 @@ async function proceedTest() {
|
|||
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=")) {
|
||||
window.name = step.split("=")[1];
|
||||
continue;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
// TODO: extend `EditorTestUtils` in editing/include/edit-test-utils.mjs
|
||||
|
||||
const kBackspaceKey = "\uE003";
|
||||
const kDeleteKey = "\uE017";
|
||||
const kArrowRight = "\uE014";
|
||||
|
@ -323,7 +325,7 @@ function setupEditor(innerHTMLWithRangeMarkers) {
|
|||
return {
|
||||
marker: scanResult[0],
|
||||
container: textNode,
|
||||
offset: scanResult.index + offset,
|
||||
offset: scanResult.index + offset
|
||||
};
|
||||
}
|
||||
if (startContainer.nodeType === Node.TEXT_NODE) {
|
||||
|
@ -359,7 +361,7 @@ function setupEditor(innerHTMLWithRangeMarkers) {
|
|||
return {
|
||||
marker: scanResult[0],
|
||||
container: textNode,
|
||||
offset: scanResult.index + offset,
|
||||
offset: scanResult.index + offset
|
||||
};
|
||||
}
|
||||
if (startContainer.nodeType === Node.TEXT_NODE) {
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,3 +1,3 @@
|
|||
# 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.
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
parameters:
|
||||
pyflag: --py3
|
||||
|
||||
steps:
|
||||
- script: ./wpt make-hosts-file | sudo tee -a /etc/hosts
|
||||
displayName: 'Update hosts (macOS)'
|
||||
|
@ -5,6 +8,6 @@ steps:
|
|||
- powershell: |
|
||||
$hostFile = "$env:systemroot\System32\drivers\etc\hosts"
|
||||
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)'
|
||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
parameters:
|
||||
pyflag: --py3
|
||||
|
||||
steps:
|
||||
# `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'
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<link id="link_empty" />
|
||||
<link id="link_web_bundle_1" rel="webbundle" />
|
||||
<link id="link_web_bundle_2" rel="webbundle" resources="foo" />
|
||||
<link id="link_web_bundle_3" rel="webbundle" scopes="bar" />
|
||||
<script>
|
||||
test(() => {
|
||||
assert_false(
|
||||
|
@ -22,6 +23,17 @@
|
|||
);
|
||||
}, "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(() => {
|
||||
const link = document.createElement("link");
|
||||
assert_true(link.relList.supports("webbundle"));
|
||||
|
@ -55,6 +67,16 @@
|
|||
"foo",
|
||||
"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.
|
||||
}, "resoruces attribute must return null or specified value");
|
||||
|
||||
|
@ -75,5 +97,23 @@
|
|||
"https://test2.example.com"
|
||||
]);
|
||||
}, "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>
|
||||
</body>
|
||||
|
|
|
@ -80,7 +80,42 @@
|
|||
assert_equals(
|
||||
await loadScriptAndWaitReport(classic_script_url),
|
||||
'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(() => {
|
||||
return addLinkAndWaitForLoad("../resources/wbn/dynamic1.wbn?test-event");
|
||||
|
@ -108,7 +143,19 @@
|
|||
link.resources = url;
|
||||
document.body.appendChild(link);
|
||||
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 () => {
|
||||
const wbn_url = 'http://web-platform.test:8001/web-bundle/resources/wbn/subresource.wbn?test-resources-update';
|
||||
|
|
|
@ -199,7 +199,7 @@ promise_test(async t => {
|
|||
assert_equals(frame.cropWidth, 320, "cropWidth");
|
||||
assert_equals(frame.cropHeight, 240, "cropHeight");
|
||||
assert_equals(frame.timestamp, 0, "timestamp");
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
});
|
||||
},
|
||||
error(e) {
|
||||
|
|
|
@ -222,14 +222,14 @@ promise_test(async t => {
|
|||
|
||||
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_throws_dom("InvalidStateError", () => frame.clone());
|
||||
|
||||
encoder.close();
|
||||
|
||||
return endAfterEventLoopTurn();
|
||||
}, 'Test encoder consumes (destroys) frames.');
|
||||
}, 'Test encoder consumes (closes) frames.');
|
||||
|
||||
promise_test(async t => {
|
||||
let encoder = new VideoEncoder(getDefaultCodecInit(t));
|
||||
|
@ -251,12 +251,11 @@ promise_test(async t => {
|
|||
let encoder = new VideoEncoder(getDefaultCodecInit(t));
|
||||
|
||||
let frame = await createVideoFrame(640, 480, 0);
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
|
||||
encoder.configure(defaultConfig);
|
||||
|
||||
frame.destroy();
|
||||
assert_throws_dom("OperationError", () => {
|
||||
encoder.encode(frame)
|
||||
});
|
||||
}, 'Verify encoding destroyed frames throws.');
|
||||
}, 'Verify encoding closed frames throws.');
|
||||
|
|
|
@ -25,8 +25,8 @@ test(t => {
|
|||
assert_equals(frame.cropWidth, clone.cropWidth);
|
||||
assert_equals(frame.cropHeight, clone.cropHeight);
|
||||
|
||||
frame.destroy();
|
||||
clone.destroy();
|
||||
frame.close();
|
||||
clone.close();
|
||||
}, 'Test we can clone a VideoFrame.');
|
||||
|
||||
test(t => {
|
||||
|
@ -35,23 +35,23 @@ test(t => {
|
|||
let copy = frame;
|
||||
let clone = frame.clone();
|
||||
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
|
||||
assert_not_equals(copy.timestamp, defaultInit.timestamp);
|
||||
assert_equals(clone.timestamp, defaultInit.timestamp);
|
||||
|
||||
clone.destroy();
|
||||
}, 'Verify destroying a frame doesn\'t affect its clones.');
|
||||
clone.close();
|
||||
}, 'Verify closing a frame doesn\'t affect its clones.');
|
||||
|
||||
test(t => {
|
||||
let frame = createDefaultVideoFrame();
|
||||
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
|
||||
assert_throws_dom("InvalidStateError", () => {
|
||||
let clone = frame.clone();
|
||||
});
|
||||
}, 'Verify cloning a destroyed frame throws.');
|
||||
}, 'Verify cloning a closed frame throws.');
|
||||
|
||||
async_test(t => {
|
||||
let localFrame = createDefaultVideoFrame();
|
||||
|
@ -62,7 +62,7 @@ async_test(t => {
|
|||
|
||||
externalPort.onmessage = t.step_func((e) => {
|
||||
let externalFrame = e.data;
|
||||
externalFrame.destroy();
|
||||
externalFrame.close();
|
||||
externalPort.postMessage("Done");
|
||||
})
|
||||
|
||||
|
@ -72,7 +72,7 @@ async_test(t => {
|
|||
|
||||
localPort.postMessage(localFrame);
|
||||
|
||||
}, 'Verify destroying frames propagates accross contexts.');
|
||||
}, 'Verify closing frames propagates accross contexts.');
|
||||
|
||||
async_test(t => {
|
||||
let localFrame = createDefaultVideoFrame();
|
||||
|
@ -83,18 +83,18 @@ async_test(t => {
|
|||
|
||||
externalPort.onmessage = t.step_func((e) => {
|
||||
let externalFrame = e.data;
|
||||
externalFrame.destroy();
|
||||
externalFrame.close();
|
||||
externalPort.postMessage("Done");
|
||||
})
|
||||
|
||||
localPort.onmessage = t.step_func_done((e) => {
|
||||
assert_equals(localFrame.timestamp, defaultInit.timestamp);
|
||||
localFrame.destroy();
|
||||
localFrame.close();
|
||||
})
|
||||
|
||||
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 => {
|
||||
let localFrame = createDefaultVideoFrame();
|
||||
|
@ -104,11 +104,11 @@ async_test(t => {
|
|||
|
||||
localPort.onmessage = t.unreached_func();
|
||||
|
||||
localFrame.destroy();
|
||||
localFrame.close();
|
||||
|
||||
assert_throws_dom("DataCloneError", () => {
|
||||
localPort.postMessage(localFrame);
|
||||
});
|
||||
|
||||
t.done();
|
||||
}, 'Verify posting destroyed frames throws.');
|
||||
}, 'Verify posting closed frames throws.');
|
||||
|
|
|
@ -12,7 +12,7 @@ test(t => {
|
|||
assert_equals(frame.cropWidth, 32, "displayWidth");
|
||||
assert_equals(frame.cropHeight, 16, "displayHeight");
|
||||
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
}, 'Test we can construct a VideoFrame.');
|
||||
|
||||
test(t => {
|
||||
|
@ -24,7 +24,7 @@ test(t => {
|
|||
assert_equals(frame.cropWidth, 1, "displayWidth");
|
||||
assert_equals(frame.cropHeight, 1, "displayHeight");
|
||||
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
}, 'Test we can construct an odd-sized VideoFrame.');
|
||||
|
||||
test(t => {
|
||||
|
@ -63,7 +63,7 @@ test(t => {
|
|||
// guarantees about the color space.
|
||||
assert_equals(view[0], 94, "Y value at (0, 0)");
|
||||
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
}, 'Test we can read planar data from a VideoFrame.');
|
||||
|
||||
test(t => {
|
||||
|
@ -78,15 +78,15 @@ test(t => {
|
|||
|
||||
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 yLength = yPlane.length;
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
|
||||
let buffer = new ArrayBuffer(yLength);
|
||||
let view = new Uint8Array(buffer);
|
||||
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 => {
|
||||
let image = makeImageBitmap(32, 16);
|
||||
|
|
|
@ -38,7 +38,7 @@ promise_test(async function(t) {
|
|||
assert_equals(frame.codedWidth, testVideo.width);
|
||||
assert_equals(frame.codedHeight, testVideo.height);
|
||||
assert_not_equals(frame.timestamp, null);
|
||||
frame.destroy();
|
||||
frame.close();
|
||||
|
||||
if (++numberFrames == 5) {
|
||||
vtr.stop();
|
||||
|
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue