5 <title>Test for contenteditable focus
</title>
6 <script src=
"/tests/SimpleTest/SimpleTest.js"></script>
7 <link rel=
"stylesheet" type=
"text/css" href=
"/tests/SimpleTest/test.css" />
11 First text in this document.
<br>
12 <input id=
"inputText" type=
"text"><br>
13 <input id=
"inputTextReadonly" type=
"text" readonly
><br>
14 <input id=
"inputButton" type=
"button" value=
"input[type=button]"><br>
15 <button id=
"button">button
</button><br>
16 <div id=
"primaryEditor" contenteditable=
"true">
17 editable contents.
<br>
18 <input id=
"inputTextInEditor" type=
"text"><br>
19 <input id=
"inputTextReadonlyInEditor" type=
"text" readonly
><br>
20 <input id=
"inputButtonInEditor" type=
"button" value=
"input[type=button]"><br>
21 <button id=
"buttonInEditor">button
</button><br>
22 <div id=
"noeditableInEditor" contenteditable=
"false">
23 <span id=
"spanInNoneditableInEditor">span element in noneditable in editor
</span><br>
24 <input id=
"inputTextInNoneditableInEditor" type=
"text"><br>
25 <input id=
"inputTextReadonlyInNoneditableInEditor" type=
"text" readonly
><br>
26 <input id=
"inputButtonInNoneditableInEditor" type=
"button" value=
"input[type=button]"><br>
27 <button id=
"buttonInNoneditableInEditor">button
</button><br>
29 <span id=
"spanInEditor">span element in editor
</span><br>
31 <div id=
"otherEditor" contenteditable=
"true">
35 <div id=
"content" style=
"display: none">
44 SimpleTest
.waitForExplicitFinish();
45 SimpleTest
.waitForFocus(() => {
46 function getNodeDescription(aNode
) {
47 if (aNode
=== undefined) {
53 switch (aNode
.nodeType
) {
55 return `${aNode.nodeName}, "${aNode.data.replace(/\n/g, "\\n")}"`;
56 case Node
.ELEMENT_NODE
:
57 return `<${aNode.tagName.toLowerCase()}${
58 aNode.getAttribute("id") !== null
59 ? ` id
="${aNode.getAttribute("id")}"`
62 aNode.getAttribute("readonly") !== null
66 aNode.getAttribute("type") !== null
67 ? ` type
="${aNode.getAttribute("type")}"`
71 return aNode
.nodeName
;
74 const fm
= SpecialPowers
.Services
.focus
;
75 // XXX using selCon for checking the visibility of the caret, however,
76 // selCon is shared in document, cannot get the element of owner of the
77 // caret from javascript?
78 const selCon
= SpecialPowers
.wrap(window
).docShell
.
79 QueryInterface(SpecialPowers
.Ci
.nsIInterfaceRequestor
).
80 getInterface(SpecialPowers
.Ci
.nsISelectionDisplay
).
81 QueryInterface(SpecialPowers
.Ci
.nsISelectionController
);
83 const primaryEditor
= document
.getElementById("primaryEditor");
84 const spanInEditor
= document
.getElementById("spanInEditor");
85 const otherEditor
= document
.getElementById("otherEditor");
87 (function test_initial_state_on_load() {
89 getSelection().rangeCount
,
91 "There should be no selection range at start"
93 ok(!selCon
.caretVisible
, "The caret should not be visible in the document");
94 // Move focus to <input type="text"> in the primary editor
95 primaryEditor
.querySelector("input[type=text]").focus();
97 SpecialPowers
.unwrap(fm
.focusedElement
),
98 primaryEditor
.querySelector("input[type=text]"),
99 '<input type="text"> in the primary editor should get focus'
102 getSelection().rangeCount
,
104 'There should be no selection range after calling focus() of <input type="text"> in the primary editor'
108 'The caret should not be visible in the <input type="text"> in the primary editor'
111 // Move focus to the editor
112 (function test_move_focus_from_child_input_to_parent_editor() {
113 primaryEditor
.focus();
115 SpecialPowers
.unwrap(fm
.focusedElement
),
117 `The editor should steal focus from <input type="text"> in the primary editor with calling its focus() (got ${
118 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
122 getSelection().rangeCount
,
124 "There should be one range after focus() of the editor is called"
126 const range
= getSelection().getRangeAt(0);
129 "The selection range should be collapsed (immediately after calling focus() of the editor)"
132 range
.startContainer
,
133 primaryEditor
.firstChild
,
134 `The selection range should be in the first text node of the editor (immediately after calling focus() of the editor, got ${
135 getNodeDescription(range.startContainer)
140 "The caret should be visible in the primary editor (immediately after calling focus() of the editor)"
143 // Move focus to other editor
144 (function test_move_focus_from_editor_to_the_other_editor() {
147 SpecialPowers
.unwrap(fm
.focusedElement
),
149 `The other editor should steal focus from the editor (got ${
150 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
154 getSelection().rangeCount
,
156 "There should be one range after focus() of the other editor is called"
158 const range
= getSelection().getRangeAt(0);
161 "The selection range should be collapsed (immediately after calling focus() of the other editor)"
164 range
.startContainer
,
165 otherEditor
.firstChild
,
166 `The selection range should be in the first text node of the editor (immediately after calling focus() of the other editor, got ${
167 getNodeDescription(range.startContainer)
172 "The caret should be visible in the primary editor (immediately after calling focus() of the other editor)"
175 // Move focus to <input type="text"> in the primary editor
176 (function test_move_focus_from_the_other_editor_to_input_in_the_editor() {
177 primaryEditor
.querySelector("input[type=text]").focus();
179 SpecialPowers
.unwrap(fm
.focusedElement
),
180 primaryEditor
.querySelector("input[type=text]"),
181 `<input type="text"> in the primary editor should steal focus from the other editor (got ${
182 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
185 getSelection().rangeCount
,
187 'There should be one range after focus() of the <input type="text"> in the primary editor is called'
189 const range
= getSelection().getRangeAt(0);
192 'The selection range should be collapsed (immediately after calling focus() of the <input type="text"> in the primary editor)'
194 // XXX maybe, the caret can stay on the other editor if it's better.
196 range
.startContainer
,
197 primaryEditor
.firstChild
,
198 `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 ${
199 getNodeDescription(range.startContainer)
204 'The caret should be visible in the <input type="text"> (immediately after calling focus() of the <input type="text"> in the primary editor)'
207 // Move focus to the other editor again
208 (function test_move_focus_from_the_other_editor_to_the_editor_with_Selection_API() {
211 SpecialPowers
.unwrap(fm
.focusedElement
),
213 `The other editor should steal focus from the <input type="text"> in the primary editor with its focus() (got ${
214 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
217 // Set selection to the span element in the primary editor.
218 getSelection().collapse(spanInEditor
.firstChild
, 5);
220 getSelection().rangeCount
,
222 "There should be one selection range after collapsing selection into the <span> in the primary editor when the other editor has focus"
225 SpecialPowers
.unwrap(fm
.focusedElement
),
227 `The editor should steal focus from the other editor with Selection API (got ${
228 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
233 "The caret should be visible in the primary editor (immediately after moving focus with Selection API)"
236 // Move focus to the editor
237 (function test_move_focus() {
238 primaryEditor
.focus();
240 SpecialPowers
.unwrap(fm
.focusedElement
),
242 "The editor should keep having focus (immediately after calling focus() of the editor when it has focus)"
245 getSelection().rangeCount
,
247 "There should be one selection range in the primary editor (immediately after calling focus() of the editor when it has focus)"
249 const range
= getSelection().getRangeAt(0);
252 "The selection range should be collapsed in the primary editor (immediately after calling focus() of the editor when it has focus)"
257 "The startOffset of the selection range shouldn't be changed (immediately after calling focus() of the editor when it has focus)"
260 range
.startContainer
.parentNode
,
262 `The startContainer of the selection range shouldn't be changed (immediately after calling focus() of the editor when it has focus, got ${
263 getNodeDescription(range.startContainer)
267 "The caret should be visible in the primary editor (immediately after calling focus() of the editor when it has focus)"
271 // Move focus to each focusable element in the primary editor.
272 function test_move_focus_from_the_editor(aTargetElement
, aFocusable
, aCaretVisible
) {
273 primaryEditor
.focus();
275 SpecialPowers
.unwrap(fm
.focusedElement
),
277 `The editor should have focus at preparing to move focus to ${
278 getNodeDescription(aTargetElement)
280 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
282 aTargetElement
.focus();
285 SpecialPowers
.unwrap(fm
.focusedElement
),
288 getNodeDescription(aTargetElement)
289 } should get focus with calling its focus() (got ${
290 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
295 SpecialPowers
.unwrap(fm
.focusedElement
),
298 getNodeDescription(aTargetElement)
299 } should not take focus with calling its focus() (got ${
300 getNodeDescription(SpecialPowers.unwrap(fm.focusedElement))
308 aCaretVisible ? "should" : "should not"
309 } visible after calling focus() of ${
310 getNodeDescription(aTargetElement)
314 test_move_focus_from_the_editor(primaryEditor
.querySelector("input[type=text]"), true, true);
315 test_move_focus_from_the_editor(primaryEditor
.querySelector("input[type=text][readonly]"), true, true);
316 // XXX shouldn't the caret become invisible?
317 test_move_focus_from_the_editor(primaryEditor
.querySelector("input[type=button]"), true, true);
318 test_move_focus_from_the_editor(primaryEditor
.querySelector("button"), true, true);
319 test_move_focus_from_the_editor(primaryEditor
.querySelector("div[contenteditable=false]"), false, true);
320 test_move_focus_from_the_editor(primaryEditor
.querySelector("div[contenteditable=false] > span"), false, true);
321 test_move_focus_from_the_editor(primaryEditor
.querySelector("div[contenteditable=false] > input[type=text]"), true, true);
322 test_move_focus_from_the_editor(primaryEditor
.querySelector("div[contenteditable=false] > input[type=text][readonly]"), true, true);
323 test_move_focus_from_the_editor(primaryEditor
.querySelector("div[contenteditable=false] > input[type=button]"), true, false);
324 test_move_focus_from_the_editor(primaryEditor
.querySelector("div[contenteditable=false] > button"), true, false);
325 test_move_focus_from_the_editor(spanInEditor
, false, true);
326 test_move_focus_from_the_editor(document
.querySelector("input[type=text]"), true, true);
327 test_move_focus_from_the_editor(document
.querySelector("input[type=text][readonly]"), true, true);
328 test_move_focus_from_the_editor(document
.querySelector("input[type=button]"), true, false);
329 test_move_focus_from_the_editor(document
.querySelector("button"), true, false);