diff options
Diffstat (limited to 'editor/libeditor/tests/test_contenteditable_focus.html')
-rw-r--r-- | editor/libeditor/tests/test_contenteditable_focus.html | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/editor/libeditor/tests/test_contenteditable_focus.html b/editor/libeditor/tests/test_contenteditable_focus.html new file mode 100644 index 0000000000..741a5b48bf --- /dev/null +++ b/editor/libeditor/tests/test_contenteditable_focus.html @@ -0,0 +1,335 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Test for contenteditable focus</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<div id="display"> + First text in this document.<br> + <input id="inputText" type="text"><br> + <input id="inputTextReadonly" type="text" readonly><br> + <input id="inputButton" type="button" value="input[type=button]"><br> + <button id="button">button</button><br> + <div id="primaryEditor" contenteditable="true"> + editable contents.<br> + <input id="inputTextInEditor" type="text"><br> + <input id="inputTextReadonlyInEditor" type="text" readonly><br> + <input id="inputButtonInEditor" type="button" value="input[type=button]"><br> + <button id="buttonInEditor">button</button><br> + <div id="noeditableInEditor" contenteditable="false"> + <span id="spanInNoneditableInEditor">span element in noneditable in editor</span><br> + <input id="inputTextInNoneditableInEditor" type="text"><br> + <input id="inputTextReadonlyInNoneditableInEditor" type="text" readonly><br> + <input id="inputButtonInNoneditableInEditor" type="button" value="input[type=button]"><br> + <button id="buttonInNoneditableInEditor">button</button><br> + </div> + <span id="spanInEditor">span element in editor</span><br> + </div> + <div id="otherEditor" contenteditable="true"> + other editor. + </div> +</div> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> + +<script> +"use strict"; + +SimpleTest.waitForExplicitFinish(); +SimpleTest.waitForFocus(() => { + function getNodeDescription(aNode) { + if (aNode === undefined) { + return "undefined"; + } + if (aNode === null) { + return "null"; + } + switch (aNode.nodeType) { + case Node.TEXT_NODE: + return `${aNode.nodeName}, "${aNode.data.replace(/\n/g, "\\n")}"`; + case Node.ELEMENT_NODE: + return `<${aNode.tagName.toLowerCase()}${ + aNode.getAttribute("id") !== null + ? ` id="${aNode.getAttribute("id")}"` + : "" + }${ + aNode.getAttribute("readonly") !== null + ? " readonly" + : "" + }${ + aNode.getAttribute("type") !== null + ? ` type="${aNode.getAttribute("type")}"` + : "" + }>`; + } + return aNode.nodeName; + } + + const fm = SpecialPowers.Services.focus; + // XXX using selCon for checking the visibility of the caret, however, + // selCon is shared in document, cannot get the element of owner of the + // caret from javascript? + const selCon = SpecialPowers.wrap(window).docShell. + QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor). + getInterface(SpecialPowers.Ci.nsISelectionDisplay). + QueryInterface(SpecialPowers.Ci.nsISelectionController); + + const primaryEditor = document.getElementById("primaryEditor"); + const spanInEditor = document.getElementById("spanInEditor"); + const otherEditor = document.getElementById("otherEditor"); + + (function test_initial_state_on_load() { + is( + getSelection().rangeCount, + 0, + "There should be no selection range at start" + ); + ok(!selCon.caretVisible, "The caret should not be visible in the document"); + // Move focus to <input type="text"> in the primary editor + primaryEditor.querySelector("input[type=text]").focus(); + is( + SpecialPowers.unwrap(fm.focusedElement), + primaryEditor.querySelector("input[type=text]"), + '<input type="text"> in the primary editor should get focus' + ); + todo_is( + getSelection().rangeCount, + 0, + 'There should be no selection range after calling focus() of <input type="text"> in the primary editor' + ); + ok( + selCon.caretVisible, + 'The caret should not be visible in the <input type="text"> in the primary editor' + ); + })(); + // Move focus to the editor + (function test_move_focus_from_child_input_to_parent_editor() { + primaryEditor.focus(); + is( + SpecialPowers.unwrap(fm.focusedElement), + primaryEditor, + `The editor should steal focus from <input type="text"> in the primary editor with calling its focus() (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + }` + ); + is( + getSelection().rangeCount, + 1, + "There should be one range after focus() of the editor is called" + ); + const range = getSelection().getRangeAt(0); + ok( + range.collapsed, + "The selection range should be collapsed (immediately after calling focus() of the editor)" + ); + is( + range.startContainer, + primaryEditor.firstChild, + `The selection range should be in the first text node of the editor (immediately after calling focus() of the editor, got ${ + getNodeDescription(range.startContainer) + })` + ); + ok( + selCon.caretVisible, + "The caret should be visible in the primary editor (immediately after calling focus() of the editor)" + ); + })(); + // Move focus to other editor + (function test_move_focus_from_editor_to_the_other_editor() { + otherEditor.focus(); + is( + SpecialPowers.unwrap(fm.focusedElement), + otherEditor, + `The other editor should steal focus from the editor (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + }` + ); + is( + getSelection().rangeCount, + 1, + "There should be one range after focus() of the other editor is called" + ); + const range = getSelection().getRangeAt(0); + ok( + range.collapsed, + "The selection range should be collapsed (immediately after calling focus() of the other editor)" + ); + is( + range.startContainer, + otherEditor.firstChild, + `The selection range should be in the first text node of the editor (immediately after calling focus() of the other editor, got ${ + getNodeDescription(range.startContainer) + })` + ); + ok( + selCon.caretVisible, + "The caret should be visible in the primary editor (immediately after calling focus() of the other editor)" + ); + })(); + // Move focus to <input type="text"> in the primary editor + (function test_move_focus_from_the_other_editor_to_input_in_the_editor() { + primaryEditor.querySelector("input[type=text]").focus(); + is( + SpecialPowers.unwrap(fm.focusedElement), + primaryEditor.querySelector("input[type=text]"), + `<input type="text"> in the primary editor should steal focus from the other editor (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + }`); + is( + getSelection().rangeCount, + 1, + 'There should be one range after focus() of the <input type="text"> in the primary editor is called' + ); + const range = getSelection().getRangeAt(0); + ok( + range.collapsed, + 'The selection range should be collapsed (immediately after calling focus() of the <input type="text"> in the primary editor)' + ); + // XXX maybe, the caret can stay on the other editor if it's better. + is( + range.startContainer, + primaryEditor.firstChild, + `The selection range should be in the first text node of the editor (immediately after calling focus() of the <input type="text"> in the primary editor, got ${ + getNodeDescription(range.startContainer) + })` + ); + ok( + selCon.caretVisible, + 'The caret should be visible in the <input type="text"> (immediately after calling focus() of the <input type="text"> in the primary editor)' + ); + })(); + // Move focus to the other editor again + (function test_move_focus_from_the_other_editor_to_the_editor_with_Selection_API() { + otherEditor.focus(); + is( + SpecialPowers.unwrap(fm.focusedElement), + otherEditor, + `The other editor should steal focus from the <input type="text"> in the primary editor with its focus() (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + })` + ); + // Set selection to the span element in the primary editor. + getSelection().collapse(spanInEditor.firstChild, 5); + is( + getSelection().rangeCount, + 1, + "There should be one selection range after collapsing selection into the <span> in the primary editor when the other editor has focus" + ); + is( + SpecialPowers.unwrap(fm.focusedElement), + primaryEditor, + `The editor should steal focus from the other editor with Selection API (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + }` + ); + ok( + selCon.caretVisible, + "The caret should be visible in the primary editor (immediately after moving focus with Selection API)" + ); + })(); + // Move focus to the editor + (function test_move_focus() { + primaryEditor.focus(); + is( + SpecialPowers.unwrap(fm.focusedElement), + primaryEditor, + "The editor should keep having focus (immediately after calling focus() of the editor when it has focus)" + ); + is( + getSelection().rangeCount, + 1, + "There should be one selection range in the primary editor (immediately after calling focus() of the editor when it has focus)" + ); + const range = getSelection().getRangeAt(0); + ok( + range.collapsed, + "The selection range should be collapsed in the primary editor (immediately after calling focus() of the editor when it has focus)" + ); + is( + range.startOffset, + 5, + "The startOffset of the selection range shouldn't be changed (immediately after calling focus() of the editor when it has focus)" + ); + is( + range.startContainer.parentNode, + spanInEditor, + `The startContainer of the selection range shouldn't be changed (immediately after calling focus() of the editor when it has focus, got ${ + getNodeDescription(range.startContainer) + })`); + ok( + selCon.caretVisible, + "The caret should be visible in the primary editor (immediately after calling focus() of the editor when it has focus)" + ); + })(); + + // Move focus to each focusable element in the primary editor. + function test_move_focus_from_the_editor(aTargetElement, aFocusable, aCaretVisible) { + primaryEditor.focus(); + is( + SpecialPowers.unwrap(fm.focusedElement), + primaryEditor, + `The editor should have focus at preparing to move focus to ${ + getNodeDescription(aTargetElement) + } (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + }`); + aTargetElement.focus(); + if (aFocusable) { + is( + SpecialPowers.unwrap(fm.focusedElement), + aTargetElement, + `${ + getNodeDescription(aTargetElement) + } should get focus with calling its focus() (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + }` + ); + } else { + is( + SpecialPowers.unwrap(fm.focusedElement), + primaryEditor, + `${ + getNodeDescription(aTargetElement) + } should not take focus with calling its focus() (got ${ + getNodeDescription(SpecialPowers.unwrap(fm.focusedElement)) + }` + ); + } + is( + selCon.caretVisible, + aCaretVisible, + `The caret ${ + aCaretVisible ? "should" : "should not" + } visible after calling focus() of ${ + getNodeDescription(aTargetElement) + }` + ); + } + test_move_focus_from_the_editor(primaryEditor.querySelector("input[type=text]"), true, true); + test_move_focus_from_the_editor(primaryEditor.querySelector("input[type=text][readonly]"), true, true); + // XXX shouldn't the caret become invisible? + test_move_focus_from_the_editor(primaryEditor.querySelector("input[type=button]"), true, true); + test_move_focus_from_the_editor(primaryEditor.querySelector("button"), true, true); + test_move_focus_from_the_editor(primaryEditor.querySelector("div[contenteditable=false]"), false, true); + test_move_focus_from_the_editor(primaryEditor.querySelector("div[contenteditable=false] > span"), false, true); + test_move_focus_from_the_editor(primaryEditor.querySelector("div[contenteditable=false] > input[type=text]"), true, true); + test_move_focus_from_the_editor(primaryEditor.querySelector("div[contenteditable=false] > input[type=text][readonly]"), true, true); + test_move_focus_from_the_editor(primaryEditor.querySelector("div[contenteditable=false] > input[type=button]"), true, false); + test_move_focus_from_the_editor(primaryEditor.querySelector("div[contenteditable=false] > button"), true, false); + test_move_focus_from_the_editor(spanInEditor, false, true); + test_move_focus_from_the_editor(document.querySelector("input[type=text]"), true, true); + test_move_focus_from_the_editor(document.querySelector("input[type=text][readonly]"), true, true); + test_move_focus_from_the_editor(document.querySelector("input[type=button]"), true, false); + test_move_focus_from_the_editor(document.querySelector("button"), true, false); + + SimpleTest.finish(); +}); +</script> +</body> +</html> |