test_nsIEditorMailSupport_insertAsCitedQuotation.html (13389B)
1 <!DOCTYPE> 2 <html> 3 <head> 4 <title>Test for nsIEditorMailSupport.insertAsCitedQuotation()</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <link rel="stylesheet" href="/tests/SimpleTest/test.css"> 7 </head> 8 <body> 9 <div contenteditable></div> 10 <script> 11 "use strict"; 12 13 SimpleTest.waitForExplicitFinish(); 14 SimpleTest.waitForFocus(() => { 15 function testInputEvents() { 16 const inReadonlyMode = getEditor().flags & SpecialPowers.Ci.nsIEditor.eEditorReadonlyMask; 17 const editorDescription = `(readonly=${!!inReadonlyMode})`; 18 19 const editor = document.querySelector("div[contenteditable]"); 20 const selection = getSelection(); 21 22 let beforeInputEvents = []; 23 let inputEvents = []; 24 let selectionRanges = []; 25 function onBeforeInput(aEvent) { 26 beforeInputEvents.push(aEvent); 27 selectionRanges = []; 28 for (let i = 0; i < selection.rangeCount; i++) { 29 let range = selection.getRangeAt(i); 30 selectionRanges.push({startContainer: range.startContainer, startOffset: range.startOffset, 31 endContainer: range.endContainer, endOffset: range.endOffset}); 32 } 33 } 34 function onInput(aEvent) { 35 inputEvents.push(aEvent); 36 } 37 editor.addEventListener("beforeinput", onBeforeInput); 38 editor.addEventListener("input", onInput); 39 40 editor.innerHTML = ""; 41 editor.focus(); 42 selection.collapse(editor, 0); 43 44 function checkInputEvent(aEvent, aInputType, aData, aDescription) { 45 ok(aEvent instanceof InputEvent, 46 `"${aEvent.type}" event should be dispatched with InputEvent interface ${aDescription}`); 47 // If it were cancelable whose inputType is empty string, web apps would 48 // block any Firefox specific modification whose inputType are not declared 49 // by the spec. 50 let expectedCancelable = aEvent.type === "beforeinput" && aInputType !== ""; 51 is(aEvent.cancelable, expectedCancelable, 52 `"${aEvent.type}" event should ${expectedCancelable ? "be" : "be never"} cancelable ${aDescription}`); 53 is(aEvent.bubbles, true, 54 `"${aEvent.type}" event should always bubble ${aDescription}`); 55 is(aEvent.inputType, aInputType, 56 `inputType of "${aEvent.type}" event should be "${aInputType}" ${aDescription}`); 57 is(aEvent.data, aData, 58 `data of "${aEvent.type}" event should be ${aData} ${aDescription}`); 59 is(aEvent.dataTransfer, null, 60 `dataTransfer of "${aEvent.type}" event should be null ${aDescription}`); 61 let targetRanges = aEvent.getTargetRanges(); 62 if (aEvent.type === "beforeinput") { 63 is(targetRanges.length, selectionRanges.length, 64 `getTargetRanges() of "beforeinput" event should return selection ranges ${aDescription}`); 65 if (targetRanges.length === selectionRanges.length) { 66 for (let i = 0; i < selectionRanges.length; i++) { 67 is(targetRanges[i].startContainer, selectionRanges[i].startContainer, 68 `startContainer of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 69 is(targetRanges[i].startOffset, selectionRanges[i].startOffset, 70 `startOffset of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 71 is(targetRanges[i].endContainer, selectionRanges[i].endContainer, 72 `endContainer of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 73 is(targetRanges[i].endOffset, selectionRanges[i].endOffset, 74 `endOffset of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 75 } 76 } 77 } else { 78 is(targetRanges.length, 0, 79 `getTargetRanges() of "${aEvent.type}" event should return empty array ${aDescription}`); 80 } 81 } 82 83 // Tests when the editor is in plaintext mode. 84 85 getEditor().flags |= SpecialPowers.Ci.nsIEditor.eEditorPlaintextMask; 86 87 beforeInputEvents = []; 88 inputEvents = []; 89 getEditorMailSupport().insertAsCitedQuotation("this is quoted text\nAnd here is second line.", "this is cited text", false); 90 91 ok( 92 selection.isCollapsed, 93 `Selection should be collapsed after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor ${editorDescription}` 94 ); 95 is( 96 selection.focusNode, 97 editor, 98 `focus node of Selection should be a child of the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor ${editorDescription}` 99 ); 100 is( 101 selection.focusOffset, 102 1, 103 `focus offset of Selection should be next to inserted <span> element after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor ${editorDescription}` 104 ); 105 is( 106 editor.innerHTML, 107 '<span style="white-space: pre-wrap;">> this is quoted text<br>> And here is second line.<br><br></span>', 108 `The quoted text should be inserted as plaintext into the plaintext editor ${editorDescription}` 109 ); 110 is( 111 beforeInputEvents.length, 112 1, 113 `One "beforeinput" event should be fired on the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor ${editorDescription}` 114 ); 115 checkInputEvent( 116 beforeInputEvents[0], 117 "insertText", "this is quoted text\nAnd here is second line.", 118 `after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor ${editorDescription}` 119 ); 120 is( 121 inputEvents.length, 122 1, 123 `One "input" event should be fired on the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor ${editorDescription}` 124 ); 125 checkInputEvent( 126 inputEvents[0], 127 "insertText", "this is quoted text\nAnd here is second line.", 128 `after calling nsIEditorMailSupport.insertAsCitedQuotation() of plaintext editor ${editorDescription}` 129 ); 130 131 // Tests when the editor is in HTML editor mode. 132 getEditor().flags &= ~SpecialPowers.Ci.nsIEditor.eEditorPlaintextMask; 133 134 editor.innerHTML = ""; 135 136 beforeInputEvents = []; 137 inputEvents = []; 138 getEditorMailSupport().insertAsCitedQuotation("this is quoted text<br>", "this is cited text", false); 139 140 ok( 141 selection.isCollapsed, 142 `Selection should be collapsed after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext) ${editorDescription}` 143 ); 144 is( 145 selection.focusNode, 146 editor, 147 `focus node of Selection should be a child of the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext) ${editorDescription}` 148 ); 149 is( 150 selection.focusOffset, 151 1, 152 `focus offset of Selection should be next to inserted <span> element after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext) ${editorDescription}` 153 ); 154 is( 155 editor.innerHTML, 156 '<blockquote type="cite" cite="this is cited text">this is quoted text<br></blockquote>', 157 `The quoted text should be inserted as plaintext into the HTML editor ${editorDescription}` 158 ); 159 is( 160 beforeInputEvents.length, 161 1, 162 `One "beforeinput" event should be fired on the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext) ${editorDescription}` 163 ); 164 checkInputEvent( 165 beforeInputEvents[0], 166 "", 167 null, 168 `after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext) ${editorDescription}` 169 ); 170 is( 171 inputEvents.length, 172 1, 173 `One "input" event should be fired on the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext) ${editorDescription}` 174 ); 175 checkInputEvent( 176 inputEvents[0], 177 "", 178 null, 179 `after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as plaintext) ${editorDescription}` 180 ); 181 182 editor.innerHTML = ""; 183 184 beforeInputEvents = []; 185 inputEvents = []; 186 getEditorMailSupport().insertAsCitedQuotation("this is quoted text<br>And here is second line.", "this is cited text", true); 187 188 ok( 189 selection.isCollapsed, 190 `Selection should be collapsed after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source) ${editorDescription}` 191 ); 192 is( 193 selection.focusNode, 194 editor, 195 `focus node of Selection should be a child of the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source) ${editorDescription}` 196 ); 197 is( 198 selection.focusOffset, 199 1, 200 `focus offset of Selection should be next to inserted <span> element after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source) ${editorDescription}` 201 ); 202 is( 203 editor.innerHTML, 204 '<blockquote type="cite" cite="this is cited text">this is quoted text<br>And here is second line.</blockquote>', 205 `The quoted text should be inserted as HTML source into the HTML editor ${editorDescription}` 206 ); 207 is( 208 beforeInputEvents.length, 209 1, 210 `One "beforeinput" event should be fired on the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source) ${editorDescription}` 211 ); 212 checkInputEvent( 213 beforeInputEvents[0], 214 "", 215 null, 216 `after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source) ${editorDescription}` 217 ); 218 is( 219 inputEvents.length, 220 1, 221 `One "input" event should be fired on the editing host after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source) ${editorDescription}` 222 ); 223 checkInputEvent( 224 inputEvents[0], 225 "", 226 null, 227 `after calling nsIEditorMailSupport.insertAsCitedQuotation() of HTMLEditor editor (inserting as HTML source) ${editorDescription}` 228 ); 229 230 editor.removeEventListener("beforeinput", onBeforeInput); 231 editor.removeEventListener("input", onInput); 232 } 233 234 function testStyleOfPlaintextMode() { 235 const inReadonlyMode = getEditor().flags & SpecialPowers.Ci.nsIEditor.eEditorReadonlyMask; 236 const editorDescription = `(readonly=${!!inReadonlyMode})`; 237 238 getEditor().flags |= SpecialPowers.Ci.nsIEditor.eEditorPlaintextMask; 239 240 (function testInDiv() { 241 const editor = document.querySelector("div[contenteditable]"); 242 editor.innerHTML = ""; 243 editor.focus(); 244 getEditorMailSupport().insertAsCitedQuotation( 245 "this is quoted text.", 246 "this is cited text", 247 false 248 ); 249 is( 250 editor.firstChild.tagName, 251 "SPAN", 252 `testStyleOfPlaintextMode: testInDiv: insertAsCitedQuotation should insert a <span> element ${editorDescription}` 253 ); 254 const computedSpanStyle = getComputedStyle(editor.firstChild); 255 is( 256 computedSpanStyle.display, 257 "inline", 258 `testStyleOfPlaintextMode: testInDiv: The inserted <span> element should be "display: inline;" ${editorDescription}` 259 ); 260 is( 261 computedSpanStyle.whiteSpace, 262 "pre-wrap", 263 `testStyleOfPlaintextMode: testInDiv: The inserted <span> element should be "white-space: pre-wrap;" ${editorDescription}` 264 ); 265 })(); 266 267 try { 268 document.body.contentEditable = true; 269 (function testInBody() { 270 getSelection().collapse(document.body, 0); 271 getEditorMailSupport().insertAsCitedQuotation( 272 "this is quoted text.", 273 "this is cited text", 274 false 275 ); 276 is( 277 document.body.firstChild.tagName, 278 "SPAN", 279 `testStyleOfPlaintextMode: testInBody: insertAsCitedQuotation should insert a <span> element in plaintext mode and in a <body> element ${editorDescription}` 280 ); 281 const computedSpanStyle = getComputedStyle(document.body.firstChild); 282 is( 283 computedSpanStyle.display, 284 "block", 285 `testStyleOfPlaintextMode: testInBody: The inserted <span> element should be "display: block;" in plaintext mode and in a <body> element ${editorDescription}` 286 ); 287 is( 288 computedSpanStyle.whiteSpace, 289 "pre-wrap", 290 `testStyleOfPlaintextMode: testInBody: The inserted <span> element should be "white-space: pre-wrap;" in plaintext mode and in a <body> element ${editorDescription}` 291 ); 292 document.body.firstChild.remove(); 293 })(); 294 } finally { 295 document.body.contentEditable = false; 296 } 297 } 298 299 testInputEvents(); 300 testStyleOfPlaintextMode(); 301 302 // Even if the HTMLEditor is readonly, XPCOM API should keep working. 303 getEditor().flags |= SpecialPowers.Ci.nsIEditor.eEditorReadonlyMask; 304 testInputEvents(); 305 testStyleOfPlaintextMode(); 306 307 SimpleTest.finish(); 308 }); 309 310 function getEditor() { 311 var editingSession = SpecialPowers.wrap(window).docShell.editingSession; 312 return editingSession.getEditorForWindow(window); 313 } 314 315 function getEditorMailSupport() { 316 return getEditor().QueryInterface(SpecialPowers.Ci.nsIEditorMailSupport); 317 } 318 319 </script> 320 </body> 321 322 </html>