summaryrefslogtreecommitdiffstats
path: root/editor/libeditor/tests/test_contenteditable_focus.html
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor/tests/test_contenteditable_focus.html')
-rw-r--r--editor/libeditor/tests/test_contenteditable_focus.html335
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>