test_contenteditable_text_input_handling.html (11462B)
1 <html> 2 <head> 3 <title>Test for text input event handling on contenteditable editor</title> 4 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> 5 <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> 6 <link rel="stylesheet" type="text/css" 7 href="chrome://mochikit/content/tests/SimpleTest/test.css" /> 8 </head> 9 <body> 10 <div id="display"> 11 <p id="static">static content<input id="inputInStatic"><textarea id="textareaInStatic"></textarea></p> 12 <p id="editor"contenteditable="true">content editable<input id="inputInEditor"><textarea id="textareaInEditor"></textarea></p> 13 </div> 14 <div id="content" style="display: none"> 15 16 </div> 17 <pre id="test"> 18 </pre> 19 20 <script class="testbody" type="application/javascript"> 21 22 SimpleTest.waitForExplicitFinish(); 23 SimpleTest.waitForFocus(runTests); 24 const { AppConstants } = ChromeUtils.importESModule( 25 "resource://gre/modules/AppConstants.sys.mjs" 26 ); 27 const kLF = !navigator.platform.indexOf("Win") && false ? "\r\n" : "\n"; 28 29 function runTests() { 30 var fm = Services.focus; 31 32 var listener = { 33 handleEvent: function _hv(aEvent) { 34 aEvent.preventDefault(); // prevent the browser default behavior 35 }, 36 }; 37 SpecialPowers.wrap(window).addEventListener("keypress", listener, { mozSystemGroup: true }); 38 39 var staticContent = document.getElementById("static"); 40 staticContent._defaultValue = getTextValue(staticContent); 41 staticContent._isFocusable = false; 42 staticContent._isEditable = false; 43 staticContent._isContentEditable = false; 44 staticContent._description = "non-editable p element"; 45 var inputInStatic = document.getElementById("inputInStatic"); 46 inputInStatic._defaultValue = getTextValue(inputInStatic); 47 inputInStatic._isFocusable = true; 48 inputInStatic._isEditable = true; 49 inputInStatic._isContentEditable = false; 50 inputInStatic._description = "input element in static content"; 51 var textareaInStatic = document.getElementById("textareaInStatic"); 52 textareaInStatic._defaultValue = getTextValue(textareaInStatic); 53 textareaInStatic._isFocusable = true; 54 textareaInStatic._isEditable = true; 55 textareaInStatic._isContentEditable = false; 56 textareaInStatic._description = "textarea element in static content"; 57 var editor = document.getElementById("editor"); 58 editor._defaultValue = getTextValue(editor); 59 editor._isFocusable = true; 60 editor._isEditable = true; 61 editor._isContentEditable = true; 62 editor._description = "contenteditable editor"; 63 var inputInEditor = document.getElementById("inputInEditor"); 64 inputInEditor._defaultValue = getTextValue(inputInEditor); 65 inputInEditor._isFocusable = true; 66 inputInEditor._isEditable = true; 67 inputInEditor._isContentEditable = false; 68 inputInEditor._description = "input element in contenteditable editor"; 69 var textareaInEditor = document.getElementById("textareaInEditor"); 70 textareaInEditor._defaultValue = getTextValue(textareaInEditor); 71 textareaInEditor._isFocusable = true; 72 textareaInEditor._isEditable = true; 73 textareaInEditor._isContentEditable = false; 74 textareaInEditor._description = "textarea element in contenteditable editor"; 75 76 function getTextValue(aElement) { 77 if (aElement == editor) { 78 var value = ""; 79 for (var node = aElement.firstChild; node; node = node.nextSibling) { 80 if (node.nodeType == Node.TEXT_NODE) { 81 value += node.data; 82 } else if (node.nodeType == Node.ELEMENT_NODE) { 83 var tagName = node.tagName.toLowerCase(); 84 switch (tagName) { 85 case "input": 86 case "textarea": 87 value += kLF; 88 break; 89 default: 90 ok(false, "Undefined tag is used in the editor: " + tagName); 91 break; 92 } 93 } 94 } 95 return value; 96 } 97 return aElement.value; 98 } 99 100 function testTextInput(aFocus) { 101 var when = " when " + 102 ((aFocus && aFocus._isFocusable) ? aFocus._description + " has focus" : 103 "nobody has focus"); 104 105 function checkValue(aElement, aInsertedText) { 106 if (aElement == aFocus && aElement._isEditable) { 107 is(getTextValue(aElement), aInsertedText + aElement._defaultValue, 108 aElement._description + 109 " wasn't edited by synthesized key events" + when); 110 return; 111 } 112 is(getTextValue(aElement), aElement._defaultValue, 113 aElement._description + 114 " was edited by synthesized key events" + when); 115 } 116 117 if (aFocus && aFocus._isFocusable) { 118 aFocus.focus(); 119 is(fm.focusedElement, aFocus, 120 aFocus._description + " didn't get focus at preparing tests" + when); 121 } else { 122 var focusedElement = fm.focusedElement; 123 if (focusedElement) { 124 focusedElement.blur(); 125 } 126 ok(!fm.focusedElement, 127 "Failed to blur at preparing tests" + when); 128 } 129 130 if (aFocus && aFocus._isFocusable) { 131 sendString("ABC"); 132 checkValue(staticContent, "ABC"); 133 checkValue(inputInStatic, "ABC"); 134 checkValue(textareaInStatic, "ABC"); 135 checkValue(editor, "ABC"); 136 checkValue(inputInEditor, "ABC"); 137 checkValue(textareaInEditor, "ABC"); 138 139 if (aFocus._isEditable) { 140 synthesizeKey("KEY_Backspace", {repeat: 3}); 141 checkValue(staticContent, ""); 142 checkValue(inputInStatic, ""); 143 checkValue(textareaInStatic, ""); 144 checkValue(editor, ""); 145 checkValue(inputInEditor, ""); 146 checkValue(textareaInEditor, ""); 147 } 148 } 149 150 // When key events are fired on unfocused editor. 151 function testDispatchedKeyEvent(aTarget) { 152 var targetDescription = " (dispatched to " + aTarget._description + ")"; 153 function dispatchKeyEvent(aKeyCode, aChar, aDispatchTarget) { 154 var keyEvent = new KeyboardEvent("keypress", { 155 bubbles: true, 156 cancelable: true, 157 view: null, 158 keyCode: aKeyCode, 159 charCode: aChar ? aChar.charCodeAt(0) : 0 160 }); 161 aDispatchTarget.dispatchEvent(keyEvent); 162 } 163 164 function checkValueForDispatchedKeyEvent(aElement, aInsertedText) { 165 if (aElement == aTarget && aElement._isEditable && 166 (!aElement._isContentEditable || aElement == aFocus)) { 167 is(getTextValue(aElement), aInsertedText + aElement._defaultValue, 168 aElement._description + 169 " wasn't edited by dispatched key events" + 170 when + targetDescription); 171 return; 172 } 173 if (aElement == aTarget) { 174 is(getTextValue(aElement), aElement._defaultValue, 175 aElement._description + 176 " was edited by dispatched key events" + 177 when + targetDescription); 178 return; 179 } 180 is(getTextValue(aElement), aElement._defaultValue, 181 aElement._description + 182 " was edited by key events unexpectedly" + 183 when + targetDescription); 184 } 185 186 dispatchKeyEvent(0, "A", aTarget); 187 dispatchKeyEvent(0, "B", aTarget); 188 dispatchKeyEvent(0, "C", aTarget); 189 190 checkValueForDispatchedKeyEvent(staticContent, "ABC"); 191 checkValueForDispatchedKeyEvent(inputInStatic, "ABC"); 192 checkValueForDispatchedKeyEvent(textareaInStatic, "ABC"); 193 checkValueForDispatchedKeyEvent(editor, "ABC"); 194 checkValueForDispatchedKeyEvent(inputInEditor, "ABC"); 195 checkValueForDispatchedKeyEvent(textareaInEditor, "ABC"); 196 197 dispatchKeyEvent(KeyboardEvent.DOM_VK_BACK_SPACE, 0, aTarget); 198 dispatchKeyEvent(KeyboardEvent.DOM_VK_BACK_SPACE, 0, aTarget); 199 dispatchKeyEvent(KeyboardEvent.DOM_VK_BACK_SPACE, 0, aTarget); 200 201 checkValueForDispatchedKeyEvent(staticContent, ""); 202 checkValueForDispatchedKeyEvent(inputInStatic, ""); 203 checkValueForDispatchedKeyEvent(textareaInStatic, ""); 204 checkValueForDispatchedKeyEvent(editor, ""); 205 checkValueForDispatchedKeyEvent(inputInEditor, ""); 206 checkValueForDispatchedKeyEvent(textareaInEditor, ""); 207 } 208 209 testDispatchedKeyEvent(staticContent); 210 testDispatchedKeyEvent(inputInStatic); 211 testDispatchedKeyEvent(textareaInStatic); 212 testDispatchedKeyEvent(editor); 213 testDispatchedKeyEvent(inputInEditor); 214 testDispatchedKeyEvent(textareaInEditor); 215 216 if (!aFocus._isEditable) { 217 return; 218 } 219 220 // IME 221 // input first character 222 synthesizeCompositionChange( 223 { "composition": 224 { "string": "\u3089", 225 "clauses": 226 [ 227 { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }, 228 ], 229 }, 230 "caret": { "start": 1, "length": 0 }, 231 }); 232 var queryText = synthesizeQueryTextContent(0, 100); 233 ok(queryText, "query text event result is null" + when); 234 if (!queryText) { 235 return; 236 } 237 ok(queryText.succeeded, "query text event failed" + when); 238 if (!queryText.succeeded) { 239 return; 240 } 241 is(queryText.text, "\u3089" + aFocus._defaultValue, 242 "composing text is incorrect" + when); 243 var querySelectedText = synthesizeQuerySelectedText(); 244 ok(querySelectedText, "query selected text event result is null" + when); 245 if (!querySelectedText) { 246 return; 247 } 248 ok(querySelectedText.succeeded, "query selected text event failed" + when); 249 if (!querySelectedText.succeeded) { 250 return; 251 } 252 is(querySelectedText.offset, 1, 253 "query selected text event returns wrong offset" + when); 254 is(querySelectedText.text, "", 255 "query selected text event returns wrong selected text" + when); 256 // commit composition 257 synthesizeComposition({ type: "compositioncommitasis" }); 258 queryText = synthesizeQueryTextContent(0, 100); 259 ok(queryText, "query text event result is null after commit" + when); 260 if (!queryText) { 261 return; 262 } 263 ok(queryText.succeeded, "query text event failed after commit" + when); 264 if (!queryText.succeeded) { 265 return; 266 } 267 is(queryText.text, "\u3089" + aFocus._defaultValue, 268 "composing text is incorrect after commit" + when); 269 querySelectedText = synthesizeQuerySelectedText(); 270 ok(querySelectedText, 271 "query selected text event result is null after commit" + when); 272 if (!querySelectedText) { 273 return; 274 } 275 ok(querySelectedText.succeeded, 276 "query selected text event failed after commit" + when); 277 if (!querySelectedText.succeeded) { 278 return; 279 } 280 is(querySelectedText.offset, 1, 281 "query selected text event returns wrong offset after commit" + when); 282 is(querySelectedText.text, "", 283 "query selected text event returns wrong selected text after commit" + 284 when); 285 286 checkValue(staticContent, "\u3089"); 287 checkValue(inputInStatic, "\u3089"); 288 checkValue(textareaInStatic, "\u3089"); 289 checkValue(editor, "\u3089"); 290 checkValue(inputInEditor, "\u3089"); 291 checkValue(textareaInEditor, "\u3089"); 292 293 synthesizeKey("KEY_Backspace"); 294 checkValue(staticContent, ""); 295 checkValue(inputInStatic, ""); 296 checkValue(textareaInStatic, ""); 297 checkValue(editor, ""); 298 checkValue(inputInEditor, ""); 299 checkValue(textareaInEditor, ""); 300 } 301 302 testTextInput(inputInStatic); 303 testTextInput(textareaInStatic); 304 testTextInput(editor); 305 testTextInput(inputInEditor); 306 testTextInput(textareaInEditor); 307 308 SpecialPowers.wrap(window).removeEventListener("keypress", listener, { mozSystemGroup: true }); 309 310 SimpleTest.finish(); 311 } 312 313 </script> 314 </body> 315 316 </html>