diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/selection/contenteditable | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/selection/contenteditable')
6 files changed, 1073 insertions, 0 deletions
diff --git a/testing/web-platform/tests/selection/contenteditable/cefalse-on-boundaries.html b/testing/web-platform/tests/selection/contenteditable/cefalse-on-boundaries.html new file mode 100644 index 0000000000..945058dfc3 --- /dev/null +++ b/testing/web-platform/tests/selection/contenteditable/cefalse-on-boundaries.html @@ -0,0 +1,72 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>Selection of contenteditable=false at the beginning and end of contenteditable element</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div contenteditable id="host"> + <div contenteditable="false" id="beginning"> </div> + <p id="paragraph">Lorem ipsum dolor sit amet.</p> + <div contenteditable="false" id="end"> </div> +</div> +<script> +test( () => { + const range = document.createRange(); + + range.setStartBefore( beginning ); + range.setEndAfter( paragraph ); + getSelection().removeAllRanges(); + getSelection().addRange( range ); + + const selectedRange = getSelection().getRangeAt( 0 ); + + assert_equals( selectedRange.startContainer, host ); + assert_equals( selectedRange.startOffset, 1 ); + assert_equals( selectedRange.endContainer, host ); + assert_equals( selectedRange.endOffset, 4 ); +}, 'Selection can start on cE=false element at the beginning of the cE=true element' ); + +test( () => { + const range = document.createRange(); + + range.setStartBefore( paragraph ); + range.setEndAfter( end ); + getSelection().removeAllRanges(); + getSelection().addRange( range ); + + const selectedRange = getSelection().getRangeAt( 0 ); + + assert_equals( selectedRange.startContainer, host ); + assert_equals( selectedRange.startOffset, 3 ); + assert_equals( selectedRange.endContainer, host ); + assert_equals( selectedRange.endOffset, 6 ); +}, 'Selection can end on cE=false element at the end of the cE=true element' ); + +test( () => { + const range = document.createRange(); + + range.setStartBefore( beginning ); + range.setEndAfter( end ); + getSelection().removeAllRanges(); + getSelection().addRange( range ); + + const selectedRange = getSelection().getRangeAt( 0 ); + + assert_equals( selectedRange.startContainer, host ); + assert_equals( selectedRange.startOffset, 1 ); + assert_equals( selectedRange.endContainer, host ); + assert_equals( selectedRange.endOffset, 6 ); +}, 'Selection can start and end on cE=false elements at the boundaries of cE=true element' ); + +test( () => { + const range = document.createRange(); + + range.selectNodeContents( host ); + + const selectedRange = getSelection().getRangeAt( 0 ); + + assert_equals( selectedRange.startContainer, host ); + assert_equals( selectedRange.startOffset, 1 ); + assert_equals( selectedRange.endContainer, host ); + assert_equals( selectedRange.endOffset, 6 ); +}, 'Range#selectNodeContents() correctly select contents of cE=true element with cE=false elements on boundaries' ); +</script> diff --git a/testing/web-platform/tests/selection/contenteditable/collapse.html b/testing/web-platform/tests/selection/contenteditable/collapse.html new file mode 100644 index 0000000000..6081d05516 --- /dev/null +++ b/testing/web-platform/tests/selection/contenteditable/collapse.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<meta charset=utf-8> +<title>Selection across multiple contenteditable</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<button id="button"></button> +<div contenteditable id="host1"></div> +<div contenteditable id="host2"></div> +<div contenteditable id="host3"> + <div contenteditable="false"> + <div contenteditable id="host4"></div> + </div> +</div> +<script> +function clearFocus() { + // Move focus from editable host to a button to remove selection limiter + button.focus(); +} +test(() => { + clearFocus(); + getSelection().collapse(host1); + assert_equals(document.activeElement, host1); + getSelection().collapse(host2); + assert_equals(document.activeElement, host2); +}, "Selection.collapse() must succeed across siblings"); + +test(() => { + clearFocus(); + getSelection().collapse(host4); + assert_equals(document.activeElement, host4); + getSelection().collapse(host3); + assert_equals(document.activeElement, host3); +}, "Selection.collapse() must succeed for the ancestor"); + + +test(() => { + clearFocus(); + getSelection().collapse(host3); + assert_equals(document.activeElement, host3); + getSelection().collapse(host4); + assert_equals(document.activeElement, host4); +}, "Selection.collapse() must succeed for the descendant"); +</script> diff --git a/testing/web-platform/tests/selection/contenteditable/initial-selection-on-focus.tentative.html b/testing/web-platform/tests/selection/contenteditable/initial-selection-on-focus.tentative.html new file mode 100644 index 0000000000..fcaf70d877 --- /dev/null +++ b/testing/web-platform/tests/selection/contenteditable/initial-selection-on-focus.tentative.html @@ -0,0 +1,473 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="variant" content="?div"> +<meta name="variant" content="?span"> +<title>initial selection on focus of contenteditable</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script>var testsJsLibraryOnly = true</script> +<script src="../../editing/include/tests.js"></script> +<body> +<script> +"use strict"; + +(function() { + const editingHostTagName = document.location.search.substr(1); + const editor = document.createElement(editingHostTagName); + editor.style.minHeight = "1em"; + editor.setAttribute("contenteditable", ""); + document.body.insertBefore(editor, document.body.firstChild); + editor.focus(); + editor.getBoundingClientRect(); + + const tests = [ + { description: "empty editor should set focus to start of it", + content: "{}", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to start of the text node", + content: "[]abc", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <br> node", + content: "{}<br>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the first <br> node", + content: "{}<br><br>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should set selection to start of the text node in the <p> node", + content: "<p>[]abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the first visible character in the text node in the <p> node", + content: "<p> []abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the <p> node because of preformatted white-space", + content: "<p style=\"white-space: pre\">[] abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the <p> node because of preformatted line break", + content: "<p style=\"white-space: pre\">[]\nabc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the <p> node", + content: "<p>{}<br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the first <br> node in the <p> node", + content: "<p>{}<br><br></p>", + canTestInInlineEditingHost: false, + }, + + { description: "editor should set selection to start of the text node in the <span> node", + content: "<span>[]abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <br> node in the <span> node", + content: "<span>{}<br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the first <br> node in the <span> node", + content: "<span>{}<br><br></span>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should set selection to before the empty <span> node", + content: "{}<span></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the empty <b> node", + content: "{}<b></b>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the empty <i> node", + content: "{}<i></i>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the empty <u> node", + content: "{}<u></u>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the empty <s> node", + content: "{}<s></s>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the empty <code> node", + content: "{}<code></code>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the empty <a> node", + content: "{}<a href=\"foo.html\"></a>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the empty <foobar> node", + content: "{}<foobar></foobar>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <input> node", + content: "{}<input>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <img> node", + content: "{}<img alt=\"foo\">", + canTestInInlineEditingHost: true, + }, + + { description: "editor should set selection to start of the text node in the second <span> node", + content: "<span></span><span>[]abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <br> node in the second <span> node", + content: "<span></span><span>{}<br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to start of the text node in the first <span> node #1", + content: "<span>[]abc</span><span>abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to start of the text node in the first <span> node #2", + content: "<span>[]abc</span><span><br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <br> node in the first <span> node #1", + content: "<span>{}<br></span><span><br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <br> node in the first <span> node #2", + content: "<span>{}<br></span><span>abc</span>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should set selection to start of the text node in the second <span> node since the text node in the first <span> node is only whitespaces", + content: "<span> </span><span>[]abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <br> node in the second <span> node since the text node in the first <span> node is only whitespaces", + content: "<span> </span><span>{}<br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to start of the text node in the second <span> node even if there is a whitespace only text node before the first <span> node", + content: " <span></span><span>[]abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should set selection to before the <br> node in the second <span> node even if there is a whitespace only text node before the first <span> node", + content: " <span></span><span>{}<br></span>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should set selection to start of the text node in the second <p> node following the empty <p> node", + content: "<p></p><p>[]abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the second <p> node following another <p> node containing only a comment node", + content: "<p><!-- comment --></p><p>[]abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the second <p> node", + content: "<p></p><p>{}<br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the first <p> node #1", + content: "<p>[]abc</p><p>abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the first <p> node #2", + content: "<p>[]abc</p><p><br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the first <p> node #1", + content: "<p>{}<br></p><p><br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the first <p> node #2", + content: "<p>{}<br></p><p>abc</p>", + canTestInInlineEditingHost: false, + }, + + { description: "editor should set selection to start of the text node in the second <p> node since the text node in the first <p> node is only whitespaces", + content: "<p> </p><p>[]abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the first <p> node whose white-spaces are preformatted", + content: "<p style=\"white-space: pre\">[] </p><p>abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the first <p> node whose line breaks are preformatted", + content: "<p style=\"white-space: pre\">[]\n</p><p>abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the second <p> node since the text node in the first <p> node is only whitespaces", + content: "<p> </p><p>{}<br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the second <p> node even if there is a whitespace only text node before the first <p> node", + content: " <p></p><p>[]abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the second <p> node even if there is a whitespace only text node before the first <p> node", + content: " <p></p><p>{}<br></p>", + canTestInInlineEditingHost: false, + }, + + { description: "editor should set selection to start of the text node in the <span> node in the second <p> node", + content: "<p><span></span></p><p><span>[]abc</span></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the <span> node in the second <p> node", + content: "<p><span></span></p><p><span>{}<br></span></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the <span> node in the first <p> node #1", + content: "<p><span>[]abc</span></p><p><span>abc</span></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to start of the text node in the <span> node in the first <p> node #2", + content: "<p><span>[]abc</span></p><p><span><br></span></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the <span> node in the first <p> node #1", + content: "<p><span>{}<br></span></p><p><span><br></span></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should set selection to before the <br> node in the <span> node in the first <p> node #2", + content: "<p><span>{}<br></span></p><p><span>abc</span></p>", + canTestInInlineEditingHost: false, + }, + + { description: "editor should collapse selection before the non-editable <span> node", + content: "{}<span contenteditable=\"false\"></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection before the non-editable <span> node even if it has a text node", + content: "{}<span contenteditable=\"false\">abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection before the non-editable <span> node even if it has a <br> node", + content: "{}<span contenteditable=\"false\"><br></span>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should collapse selection before the non-editable empty <span> node followed by a text node", + content: "{}<span contenteditable=\"false\"></span><span>abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection before the non-editable <span> node having a text node and followed by another text node", + content: "{}<span contenteditable=\"false\">abc</span><span>def</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection before the non-editable <span> node having a <br> node and followed by a text node", + content: "{}<span contenteditable=\"false\"><br></span><span>abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection before the non-editable empty <span> node followed by a <br> node", + content: "{}<span contenteditable=\"false\"></span><span><br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection before the non-editable <span> node having text node and followed by a <br> node", + content: "{}<span contenteditable=\"false\">abc</span><span><br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection before the non-editable <span> node having a <br> node and followed by another <br> node", + content: "{}<span contenteditable=\"false\"><br></span><span><br></span>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should collapse selection before the non-editable empty <p> node followed by a text node", + content: "{}<p contenteditable=\"false\"></p><p>abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection before the non-editable <p> node having a text node and followed by another text node", + content: "{}<p contenteditable=\"false\">abc</p><p>def</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection before the non-editable <p> node having a <br> node and followed by a text node", + content: "{}<p contenteditable=\"false\"><br></p><p>abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection before the non-editable empty <p> node followed by a <br> node", + content: "{}<p contenteditable=\"false\"></p><p><br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection before the non-editable <p> node having text node and followed by a <br> node", + content: "{}<p contenteditable=\"false\">abc</p><p><br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection before the non-editable <p> node having a <br> node and followed by another <br> node", + content: "{}<p contenteditable=\"false\"><br></p><p><br></p>", + canTestInInlineEditingHost: false, + }, + + { description: "editor should collapse selection to start of itself when there is only empty inline elements before the non-editable node before first editable text node", + content: "{}<span></span><span contenteditable=\"false\"></span><span>abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to start of itself when there is only empty inline elements before the non-editable node having a text node before first editable text node", + content: "{}<span></span><span contenteditable=\"false\">abc</span><span>def</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to start of itself when there is only empty inline elements before the non-editable node having a <br> node before first editable text node", + content: "{}<span></span><span contenteditable=\"false\"><br></span><span>abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to start of itself when there is only empty inline elements before the non-editable node before first editable <br> node", + content: "{}<span></span><span contenteditable=\"false\"></span><span><br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to start of itself when there is only empty inline elements before the non-editable node having a text node before first editable <br> node", + content: "{}<span></span><span contenteditable=\"false\">abc</span><span><br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to start of itself when there is only empty inline elements before the non-editable node having a <br> node before first editable <br> node", + content: "{}<span></span><span contenteditable=\"false\"><br></span><span><br></span>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should collapse selection to start of the first dive element when there is only empty inline elements before the non-editable node before first editable text node", + content: "<div>{}<span></span><span contenteditable=\"false\"></span><span>abc</span></div>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to start of the first dive element when there is only empty inline elements before the non-editable node having a text node before first editable text node", + content: "<div>{}<span></span><span contenteditable=\"false\">abc</span><span>def</span></div>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to start of the first dive element when there is only empty inline elements before the non-editable node having a <br> node before first editable text node", + content: "<div>{}<span></span><span contenteditable=\"false\"><br></span><span>abc</span></div>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to start of the first dive element when there is only empty inline elements before the non-editable node before first editable <br> node", + content: "<div>{}<span></span><span contenteditable=\"false\"></span><span><br></span></div>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to start of the first dive element when there is only empty inline elements before the non-editable node having a text node before first editable <br> node", + content: "<div>{}<span></span><span contenteditable=\"false\">abc</span><span><br></span></div>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to start of the first dive element when there is only empty inline elements before the non-editable node having a <br> node before first editable <br> node", + content: "<div>{}<span></span><span contenteditable=\"false\"><br></span><span><br></span></div>", + canTestInInlineEditingHost: false, + }, + + { description: "editor should collapse selection to the first editable text node in the first <span> node even if followed by a non-editable node", + content: "<span>[]abc</span><span contenteditable=\"false\"></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to the first editable text node in the first <span> node even if followed by a non-editable node having another text node", + content: "<span>[]abc</span><span contenteditable=\"false\">def</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to the first editable text node in the first <span> node even if followed by a non-editable node having a <br> node", + content: "<span>[]abc</span><span contenteditable=\"false\"><br></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to the first editable <br> node in the first <span> node even if followed by a non-editable node", + content: "<span>{}<br></span><span contenteditable=\"false\"></span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to the first editable <br> node in the first <span> node even if followed by a non-editable node having a text node", + content: "<span>{}<br></span><span contenteditable=\"false\">abc</span>", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to the first editable <br> node in the first <span> node even if followed by a non-editable node having a <br> node", + content: "<span>{}<br></span><span contenteditable=\"false\"><br></span>", + canTestInInlineEditingHost: true, + }, + + { description: "editor should collapse selection to the first editable text node in the first <p> node even if followed by a non-editable node", + content: "<p>[]abc</p><p contenteditable=\"false\"></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to the first editable text node in the first <p> node even if followed by a non-editable node having another text node", + content: "<p>[]abc</p><p contenteditable=\"false\">def</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to the first editable text node in the first <p> node even if followed by a non-editable node having a <br> node", + content: "<p>[]abc</p><p contenteditable=\"false\"><br></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to the first editable <br> node in the first <p> node even if followed by a non-editable node", + content: "<p>{}<br></p><p contenteditable=\"false\"></p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to the first editable <br> node in the first <p> node even if followed by a non-editable node having a text node", + content: "<p>{}<br></p><p contenteditable=\"false\">abc</p>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to the first editable <br> node in the first <p> node even if followed by a non-editable node having a <br> node", + content: "<p>{}<br></p><p contenteditable=\"false\"><br></p>", + canTestInInlineEditingHost: false, + }, + + { description: "editor should collapse selection to start of itself if first content is an input element", + content: "{}<input>abc", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to start of itself if first content is an hr element", + content: "{}<hr>abc", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to start of itself if first content is an textarea element", + content: "{}<textarea>abc</textarea>def", + canTestInInlineEditingHost: true, + }, + { description: "editor should collapse selection to the input element", + content: "<div>{}<input>abc</div>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to the hr element", + content: "<div>{}<hr>abc</div>", + canTestInInlineEditingHost: false, + }, + { description: "editor should collapse selection to the textarea element", + content: "<div>{}<textarea>abc</textarea>def</div>", + canTestInInlineEditingHost: false, + }, + ]; + + const isInlineEditingHost = editingHostTagName == "span"; + + const selection = document.getSelection(); + for (const testData of tests) { + if (isInlineEditingHost && !testData.canTestInInlineEditingHost) { + continue; + } + test(function() { + editor.blur(); + selection.removeAllRanges(); + editor.innerHTML = testData.content.replace(/[{}\[\]]/g, ""); + editor.focus(); + editor.getBoundingClientRect(); + + assert_equals(selection.rangeCount, 1, "Only one caret should be in the editor"); + if (selection.rangeCount) { + addBrackets(selection.getRangeAt(0)); + assert_equals(editor.innerHTML, testData.content); + } + }, testData.description); + } + + test(function() { + // Check if selection is initialized after temporarily blurred. + editor.innerHTML = + `<${editingHostTagName}>abc</${editingHostTagName}><${editingHostTagName}>def</${editingHostTagName}>`; + editor.focus(); + // Move selection to the second paragraph. + selection.collapse(editor.firstChild.nextSibling.firstChild); + // Reset focus. + editor.blur(); + editor.focus(); + // Then, selection should still be in the second paragraph. + assert_equals(selection.rangeCount, 1, "Only one caret should be in the editor"); + if (selection.rangeCount) { + addBrackets(selection.getRangeAt(0)); + assert_equals( + editor.innerHTML, + `<${editingHostTagName}>abc</${editingHostTagName}><${editingHostTagName}>[]def</${editingHostTagName}>` + ); + } + }, "editor shouldn't reset selection when it gets focus again"); +})(); +</script> diff --git a/testing/web-platform/tests/selection/contenteditable/modify.tentative.html b/testing/web-platform/tests/selection/contenteditable/modify.tentative.html new file mode 100644 index 0000000000..a3afd52ea2 --- /dev/null +++ b/testing/web-platform/tests/selection/contenteditable/modify.tentative.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Selection.modify() inside contenteditable</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div contenteditable id="host">Editable</div> +<div>Non-editable</div> +<div id="inlinehosts"> + Prefix: <span contenteditable title="inline">Editable</span>: Suffix<br> + Prefix: <span contenteditable style="display:inline-block;" title="inline-block">Editable</span>: Suffix<br> + <span contenteditable title="suffix only">Editable</span>: Suffix<br> + Prefix: <span contenteditable title="prefix only">Editable</span><br> + <span contenteditable title="standalone">Editable</span><br> + Prefix: <span contenteditable title="inline linebreak">Edit<br>able</span>: Suffix<br> + Prefix: <span contenteditable style="display:inline-block;" title="inline-block linebreak">Edit<br>able</span>: + Suffix<br> +</div> +<script> + /** @param {Node} parent */ + function* textNodeEntries(parent) { + for (const [i, node] of parent.childNodes.entries()) { + if (node.nodeType === Node.TEXT_NODE) { + yield [i, node]; + } + } + } + + const selection = getSelection(); + test(() => { + selection.collapse(host); + selection.modify('extend', 'forward', 'word'); + selection.modify('extend', 'forward', 'word'); + assert_equals(selection.focusNode.parentElement, host); + }, "Selection.modify() must not select outside of the host"); + + /** @type {NodeListOf<HTMLElement>} */ + const hosts = inlinehosts.querySelectorAll("span[contenteditable]"); + for (const host of hosts) { + test(() => { + for (const [i, text] of textNodeEntries(host)) { + selection.collapse(text, 1); + selection.modify("move", "forward", "lineboundary"); + if (selection.focusNode === host) { + assert_equals(selection.focusOffset, i + 1, "focusOffset should be after the text node"); + } else { + assert_equals(selection.focusNode, text, "focusNode should be the text node"); + assert_equals(selection.focusOffset, text.textContent.length, "focusOffset should be the length of the text node"); + } + } + }, `Selection.modify('move', 'forward', 'lineboundary') must be within the inline editing host: ${host.title}`); + test(() => { + for (const [i, text] of textNodeEntries(host)) { + selection.collapse(text, 1); + selection.modify("move", "backward", "lineboundary"); + assert_equals(selection.focusNode, text, "focusNode should be the text node"); + assert_equals(selection.focusOffset, 0, "focusOffset should be 0"); + } + }, `Selection.modify('move', 'backward', 'lineboundary') must be within the inline editing host: ${host.title}`); + } +</script> diff --git a/testing/web-platform/tests/selection/contenteditable/modifying-selection-with-middle-mouse-button.tentative.html b/testing/web-platform/tests/selection/contenteditable/modifying-selection-with-middle-mouse-button.tentative.html new file mode 100644 index 0000000000..a8287d9002 --- /dev/null +++ b/testing/web-platform/tests/selection/contenteditable/modifying-selection-with-middle-mouse-button.tentative.html @@ -0,0 +1,213 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Testing default action of `mousedown` of middle button and `mouseup` of middle button</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> +<style> +span { + white-space: nowrap; +} +</style> +</head> +<body> +<div contenteditable></div> +<script> +"use strict"; + +var editor = document.querySelector("div[contenteditable]"); +var span1, span2, link; +var selection = getSelection(); + +function preventDefault(event) { + event.preventDefault(); +} +editor.addEventListener("paste", preventDefault, {capture: true}); + +function resetEditor() { + editor.innerHTML = + '<span id="span1">first span.</span><br><span id="span2">second span.</span><br><a id="link" href="#top">link.</a>'; + span1 = document.getElementById("span1"); + span2 = document.getElementById("span2"); + link = document.getElementById("link"); +} + +promise_test(async () => { + resetEditor(); + editor.blur(); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span1}) + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .send(); + + assert_equals(document.activeElement, editor, + "The clicked editor should get focus"); + assert_true(selection.isCollapsed, + "Selection should be collapsed after middle button click"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should be collapsed in the first <span> element which was clicked by middle button"); +}, "Middle click should set focus to clicked editable element and collapse selection around the clicked point"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .send(); + + assert_equals(document.activeElement, editor, + "The clicked editor should keep having focus"); + assert_true(selection.isCollapsed, + "Selection should be collapsed after middle button click"); + assert_equals(selection.focusNode, span2.firstChild, + "Selection should be collapsed in the second <span> element which was clicked by middle button"); +}, "Middle click should move caret in an editable element"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + addEventListener("mousedown", preventDefault); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .send(); + removeEventListener("mousedown", preventDefault); + + assert_equals(selection.focusNode, span1.firstChild, + "Selection should keep collapsed selection in the first <span> element"); + assert_equals(selection.focusOffset, 2, + "Selection should keep collapsed selection at 2 of the first <span> element"); +}, "Middle click shouldn't move caret in an editable element if the default of mousedown event is prevented"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + addEventListener("pointerdown", preventDefault); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .send(); + removeEventListener("pointerdown", preventDefault); + + assert_equals(selection.focusNode, span1.firstChild, + "Selection should keep collapsed selection in the first <span> element"); + assert_equals(selection.focusOffset, 2, + "Selection should keep collapsed selection at 2 of the first <span> element"); +}, "Middle click shouldn't move caret in an editable element if the default of pointerdown event is prevented"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .keyDown("\uE008") + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .keyUp("\uE008") + .send(); + + assert_equals(selection.anchorNode, span1.firstChild, + "Selection#anchorNode should keep in the first <span> element"); + assert_equals(selection.anchorOffset, 2, + "Selection#anchorNode should keep at 2 of the first <span> element"); + assert_equals(selection.focusNode, span2.firstChild, + "Selection#focusNode should be in the second <span> element which was clicked by middle button"); +}, "Shift + Middle click should extend the selection"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: link}) + .keyDown("\uE008") + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .keyUp("\uE008") + .send(); + + assert_equals(selection.focusNode, link.firstChild, + "Selection#focusNode should be in the <a href> element which was clicked by middle button"); + assert_true(selection.isCollapsed, + "Selection#isCollapsed should be true"); +}, "Shift + Middle click in a link shouldn't extend the selection"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + editor.addEventListener("pointerdown", () => { + assert_true(selection.isCollapsed, + "Selection shouldn't be modified before pointerdown event"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should stay in the first <span> element when pointerdown event is fired (focusNode)"); + assert_equals(selection.focusOffset, 2, + "Selection should stay in the first <span> element when pointerdown event is fired (focusOffset)"); + }, {once: true}); + editor.addEventListener("mousedown", () => { + assert_true(selection.isCollapsed, + "Selection shouldn't be modified before mousedown event"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should stay in the first <span> element when mousedown event is fired (focusNode)"); + assert_equals(selection.focusOffset, 2, + "Selection should stay in the first <span> element when mousedown event is fired (focusOffset)"); + }, {once: true}); + editor.addEventListener("pointerup", () => { + assert_true(selection.isCollapsed, + "Selection should be collapsed before pointerup event"); + assert_equals(selection.focusNode, span2.firstChild, + "Selection should be collapsed in the second <span> element which was clicked by middle button before pointerup event "); + }, {once: true}); + let focusOffsetAtMouseUp; + editor.addEventListener("mouseup", () => { + assert_true(selection.isCollapsed, + "Selection should be collapsed before mouseup event"); + assert_equals(selection.focusNode, span2.firstChild, + "Selection should be collapsed in the second <span> element which was clicked by middle button before mouseup event "); + focusOffsetAtMouseUp = selection.focusOffset; + }, {once: true}); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerMove(0, 0, {origin: span1}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .send(); + + assert_true(selection.isCollapsed, + "Selection shouldn't be extended by pointer moves during pressing middle button"); + assert_equals(selection.focusNode, span2.firstChild, + "Selection#focusNode should stay in the second <span> element"); + assert_equals(selection.focusOffset, focusOffsetAtMouseUp, + "Selection#focusOffset should stay in the second <span> element"); +}, "Middle mouse button down should move caret, but middle mouse button up shouldn't move caret"); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/selection/contenteditable/modifying-selection-with-primary-mouse-button.tentative.html b/testing/web-platform/tests/selection/contenteditable/modifying-selection-with-primary-mouse-button.tentative.html new file mode 100644 index 0000000000..57586a6060 --- /dev/null +++ b/testing/web-platform/tests/selection/contenteditable/modifying-selection-with-primary-mouse-button.tentative.html @@ -0,0 +1,212 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<title>Testing default action of `mousedown` of primary button and `mouseup` of primary button</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> +<style> +span { + white-space: nowrap; +} +</style> +</head> +<body> +<div contenteditable></div> +<script> +"use strict"; + +var editor = document.querySelector("div[contenteditable]"); +var span1, span2, link; +var selection = getSelection(); + +function preventDefault(event) { + event.preventDefault(); +} +editor.addEventListener("paste", preventDefault, {capture: true}); + +function resetEditor() { + editor.innerHTML = + '<span id="span1">first span.</span><br><span id="span2">second span.</span><br><a id="link" href="#top">link.</a>'; + span1 = document.getElementById("span1"); + span2 = document.getElementById("span2"); + link = document.getElementById("link"); +} + +promise_test(async () => { + resetEditor(); + editor.blur(); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span1}) + .pointerDown({button: actions.ButtonType.LEFT}) + .pointerUp({button: actions.ButtonType.LEFT}) + .send(); + + assert_equals(document.activeElement, editor, + "The clicked editor should get focus"); + assert_true(selection.isCollapsed, + "Selection should be collapsed after primary button click"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should be collapsed in the first <span> element which was clicked by primary button"); +}, "Primary click should set focus to clicked editable element and collapse selection around the clicked point"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.LEFT}) + .pointerUp({button: actions.ButtonType.LEFT}) + .send(); + + assert_equals(document.activeElement, editor, + "The clicked editor should keep having focus"); + assert_true(selection.isCollapsed, + "Selection should be collapsed after primary button click"); + assert_equals(selection.focusNode, span2.firstChild, + "Selection should be collapsed in the second <span> element which was clicked by primary button"); +}, "Primary click should move caret in an editable element"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + addEventListener("mousedown", preventDefault); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.LEFT}) + .pointerUp({button: actions.ButtonType.LEFT}) + .send(); + removeEventListener("mousedown", preventDefault); + + assert_equals(selection.focusNode, span1.firstChild, + "Selection should keep collapsed selection in the first <span> element"); + assert_equals(selection.focusOffset, 2, + "Selection should keep collapsed selection at 2 of the first <span> element"); +}, "Primary click shouldn't move caret in an editable element if the default of mousedown event is prevented"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + addEventListener("pointerdown", preventDefault); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.LEFT}) + .pointerUp({button: actions.ButtonType.LEFT}) + .send(); + removeEventListener("pointerdown", preventDefault); + + assert_equals(selection.focusNode, span1.firstChild, + "Selection should keep collapsed selection in the first <span> element"); + assert_equals(selection.focusOffset, 2, + "Selection should keep collapsed selection at 2 of the first <span> element"); +}, "Primary click shouldn't move caret in an editable element if the default of pointerdown event is prevented"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .keyDown("\uE008") + .pointerDown({button: actions.ButtonType.LEFT}) + .pointerUp({button: actions.ButtonType.LEFT}) + .keyUp("\uE008") + .send(); + + assert_equals(selection.anchorNode, span1.firstChild, + "Selection#anchorNode should keep in the first <span> element"); + assert_equals(selection.anchorOffset, 2, + "Selection#anchorNode should keep at 2 of the first <span> element"); + assert_equals(selection.focusNode, span2.firstChild, + "Selection#focusNode should be in the second <span> element which was clicked by primary button"); +}, "Shift + Primary click should extend the selection"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: link}) + .keyDown("\uE008") + .pointerDown({button: actions.ButtonType.MIDDLE}) + .pointerUp({button: actions.ButtonType.MIDDLE}) + .keyUp("\uE008") + .send(); + + assert_equals(selection.focusNode, link.firstChild, + "Selection#focusNode should be in the <a href> element which was clicked by primary button"); + assert_true(selection.isCollapsed, + "Selection#isCollapsed should be true"); +}, "Shift + Primary click in a link shouldn't extend the selection"); + +promise_test(async () => { + resetEditor(); + editor.focus(); + selection.collapse(span1.firstChild, 2); + editor.addEventListener("pointerdown", () => { + assert_true(selection.isCollapsed, + "Selection shouldn't be modified before pointerdown event"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should stay in the first <span> element when pointerdown event is fired (focusNode)"); + assert_equals(selection.focusOffset, 2, + "Selection should stay in the first <span> element when pointerdown event is fired (focusOffset)"); + }, {once: true}); + editor.addEventListener("mousedown", () => { + assert_true(selection.isCollapsed, + "Selection shouldn't be modified before mousedown event"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should stay in the first <span> element when mousedown event is fired (focusNode)"); + assert_equals(selection.focusOffset, 2, + "Selection should stay in the first <span> element when mousedown event is fired (focusOffset)"); + }, {once: true}); + editor.addEventListener("pointerup", () => { + assert_true(!selection.isCollapsed, + "Selection should be modified before pointerup event"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should be modified to extend the range before pointerup event "); + }, {once: true}); + let anchorOffsetAtMouseUp; + editor.addEventListener("mouseup", () => { + assert_true(!selection.isCollapsed, + "Selection should be modified before mouseup event"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection should be modified to extend the range before mouseup event "); + anchorOffsetAtMouseUp = selection.anchorOffset; + }, {once: true}); + let actions = new test_driver.Actions(); + await actions + .pointerMove(0, 0) + .pointerMove(0, 0, {origin: span2}) + .pointerDown({button: actions.ButtonType.LEFT}) + .pointerMove(0, 0, {origin: span1}) + .pointerUp({button: actions.ButtonType.LEFT}) + .send(); + + assert_equals(selection.anchorNode, span2.firstChild, + "Selection#anchorNode should stay in the second <span> element which mousedown occurred on"); + assert_equals(selection.anchorOffset, anchorOffsetAtMouseUp, + "Selection#anchorOffset should stay in the second <span> element which mousedown occurred on"); + assert_equals(selection.focusNode, span1.firstChild, + "Selection#focusNode should be in the first <span> element which mouseup occurred on"); +}, "Primary mouse button down should move caret, and primary mouse button up should extend the selection"); +</script> +</body> +</html> |