test_nsITableEditor_insertTableRow.html (21140B)
1 <!DOCTYPE> 2 <html> 3 <head> 4 <title>Test for nsITableEditor.insertTableRow()</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <link rel="stylesheet" href="/tests/SimpleTest/test.css"> 7 </head> 8 <body> 9 <div id="display"> 10 </div> 11 <div id="content" contenteditable>out of table<table><tr><td>default content</td></tr></table></div> 12 <pre id="test"> 13 </pre> 14 15 <script class="testbody" type="application/javascript"> 16 "use strict"; 17 18 SimpleTest.waitForExplicitFinish(); 19 SimpleTest.waitForFocus(() => { 20 let editor = document.getElementById("content"); 21 let selection = document.getSelection(); 22 let selectionRanges = []; 23 24 function checkInputEvent(aEvent, aDescription) { 25 ok(aEvent instanceof InputEvent, 26 `"${aEvent.type}" event should be dispatched with InputEvent interface ${aDescription}`); 27 is(aEvent.cancelable, false, 28 `"${aEvent.type}" event should be never cancelable ${aDescription}`); 29 is(aEvent.bubbles, true, 30 `"${aEvent.type}" event should always bubble ${aDescription}`); 31 is(aEvent.inputType, "", 32 `inputType of "${aEvent.type}" event should be empty string ${aDescription}`); 33 is(aEvent.data, null, 34 `data of "${aEvent.type}" event should be null ${aDescription}`); 35 is(aEvent.dataTransfer, null, 36 `dataTransfer of "${aEvent.type}" event should be null ${aDescription}`); 37 let targetRanges = aEvent.getTargetRanges(); 38 if (aEvent.type === "beforeinput") { 39 is(targetRanges.length, selectionRanges.length, 40 `getTargetRanges() of "beforeinput" event should return selection ranges ${aDescription}`); 41 if (targetRanges.length === selectionRanges.length) { 42 for (let i = 0; i < selectionRanges.length; i++) { 43 is(targetRanges[i].startContainer, selectionRanges[i].startContainer, 44 `startContainer of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 45 is(targetRanges[i].startOffset, selectionRanges[i].startOffset, 46 `startOffset of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 47 is(targetRanges[i].endContainer, selectionRanges[i].endContainer, 48 `endContainer of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 49 is(targetRanges[i].endOffset, selectionRanges[i].endOffset, 50 `endOffset of getTargetRanges()[${i}] of "beforeinput" event does not match ${aDescription}`); 51 } 52 } 53 } else { 54 is(targetRanges.length, 0, 55 `getTargetRanges() of "${aEvent.type}" event should return empty array ${aDescription}`); 56 } 57 } 58 59 let beforeInputEvents = []; 60 let inputEvents = []; 61 function onBeforeInput(aEvent) { 62 beforeInputEvents.push(aEvent); 63 selectionRanges = []; 64 for (let i = 0; i < selection.rangeCount; i++) { 65 let range = selection.getRangeAt(i); 66 selectionRanges.push({startContainer: range.startContainer, startOffset: range.startOffset, 67 endContainer: range.endContainer, endOffset: range.endOffset}); 68 } 69 } 70 function onInput(aEvent) { 71 inputEvents.push(aEvent); 72 } 73 editor.addEventListener("beforeinput", onBeforeInput); 74 editor.addEventListener("input", onInput); 75 76 beforeInputEvents = []; 77 inputEvents = []; 78 selection.collapse(editor.firstChild, 0); 79 getTableEditor().insertTableRow(1, false); 80 is(editor.innerHTML, "out of table<table><tbody><tr><td>default content</td></tr></tbody></table>", 81 "nsITableEditor.insertTableRow(1, false) should do nothing if selection is not in <table>"); 82 is(beforeInputEvents.length, 1, 83 '"beforeinput" event should be fired when a call of nsITableEditor.insertTableRow(1, false) even though it will do nothing'); 84 checkInputEvent(beforeInputEvents[0], "when selection is collapsed outside of table element (nsITableEditor.insertTableRow(1, false))"); 85 is(inputEvents.length, 0, 86 'No "input" event should be fired when a call of nsITableEditor.insertTableRow(1, false) does nothing'); 87 88 beforeInputEvents = []; 89 inputEvents = []; 90 getTableEditor().insertTableRow(1, true); 91 is(editor.innerHTML, "out of table<table><tbody><tr><td>default content</td></tr></tbody></table>", 92 "nsITableEditor.insertTableRow(1, true) should do nothing if selection is not in <table>"); 93 is(beforeInputEvents.length, 1, 94 '"beforeinput" event should be fired when a call of nsITableEditor.insertTableRow(1, true) even though it will do nothing'); 95 checkInputEvent(beforeInputEvents[0], "when selection is collapsed outside of table element (nsITableEditor.insertTableRow(1, true))"); 96 is(inputEvents.length, 0, 97 'No "input" event should be fired when a call of nsITableEditor.insertTableRow(1, true) does nothing'); 98 99 selection.removeAllRanges(); 100 try { 101 beforeInputEvents = []; 102 inputEvents = []; 103 getTableEditor().insertTableRow(1, false); 104 ok(false, "getTableEditor().insertTableRow(1, false) without selection ranges should throw exception"); 105 } catch (e) { 106 ok(true, "getTableEditor().insertTableRow(1, false) without selection ranges should throw exception"); 107 is(beforeInputEvents.length, 0, 108 'No "beforeinput" event should be fired when nsITableEditor.insertTableRow(1, false) causes exception due to no selection range'); 109 is(inputEvents.length, 0, 110 'No "input" event should be fired when nsITableEditor.insertTableRow(1, false) causes exception due to no selection range'); 111 } 112 try { 113 beforeInputEvents = []; 114 inputEvents = []; 115 getTableEditor().insertTableRow(1, true); 116 ok(false, "getTableEditor().insertTableRow(1, true) without selection ranges should throw exception"); 117 } catch (e) { 118 ok(true, "getTableEditor().insertTableRow(1, true) without selection ranges should throw exception"); 119 is(beforeInputEvents.length, 0, 120 'No "beforeinput" event should be fired when nsITableEditor.insertTableRow(1, true) causes exception due to no selection range'); 121 is(inputEvents.length, 0, 122 'No "input" event should be fired when nsITableEditor.insertTableRow(1, true) causes exception due to no selection range'); 123 } 124 125 selection.removeAllRanges(); 126 editor.innerHTML = "<table>" + 127 "<tr><td>cell1-1</td><td>cell1-2</td></tr>" + 128 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>' + 129 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 130 "</table>"; 131 editor.focus(); 132 beforeInputEvents = []; 133 inputEvents = []; 134 selection.setBaseAndExtent(document.getElementById("select").firstChild, 0, 135 document.getElementById("select").firstChild, 0); 136 getTableEditor().insertTableRow(1, false); 137 is(editor.innerHTML, "<table><tbody>" + 138 "<tr><td>cell1-1</td><td>cell1-2</td></tr>" + 139 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 140 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>' + 141 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 142 "</tbody></table>", 143 "nsITableEditor.insertTableRow(1, false) should insert a row above the second row"); 144 is(beforeInputEvents.length, 1, 145 'Only one "beforeinput" event should be fired when selection is collapsed in a cell in second row (before)'); 146 checkInputEvent(beforeInputEvents[0], "when selection is collapsed in a cell in second row (before)"); 147 is(inputEvents.length, 1, 148 'Only one "input" event should be fired when selection is collapsed in a cell in second row (before)'); 149 checkInputEvent(inputEvents[0], "when selection is collapsed in a cell in second row (before)"); 150 151 selection.removeAllRanges(); 152 editor.innerHTML = "<table>" + 153 "<tr><td>cell1-1</td><td>cell1-2</td></tr>" + 154 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>' + 155 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 156 "</table>"; 157 editor.focus(); 158 beforeInputEvents = []; 159 inputEvents = []; 160 selection.setBaseAndExtent(document.getElementById("select").firstChild, 0, 161 document.getElementById("select").firstChild, 0); 162 getTableEditor().insertTableRow(1, true); 163 is(editor.innerHTML, "<table><tbody>" + 164 "<tr><td>cell1-1</td><td>cell1-2</td></tr>" + 165 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>' + 166 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 167 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 168 "</tbody></table>", 169 "nsITableEditor.insertTableRow(1, true) should insert a row below the second row"); 170 is(beforeInputEvents.length, 1, 171 'Only one "beforeinput" event should be fired when selection is collapsed in a cell in second row (after)'); 172 checkInputEvent(beforeInputEvents[0], "when selection is collapsed in a cell in second row (after)"); 173 is(inputEvents.length, 1, 174 'Only one "input" event should be fired when selection is collapsed in a cell in second row (after)'); 175 checkInputEvent(inputEvents[0], "when selection is collapsed in a cell in second row (after)"); 176 177 selection.removeAllRanges(); 178 editor.innerHTML = "<table>" + 179 '<tr><td>cell1-1</td><td rowspan="2">cell1-2</td></tr>' + 180 '<tr><td id="select">cell2-1</td></tr>' + 181 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 182 "</table>"; 183 editor.focus(); 184 beforeInputEvents = []; 185 inputEvents = []; 186 selection.setBaseAndExtent(document.getElementById("select").firstChild, 0, 187 document.getElementById("select").firstChild, 0); 188 getTableEditor().insertTableRow(1, false); 189 is(editor.innerHTML, "<table><tbody>" + 190 '<tr><td>cell1-1</td><td rowspan="3">cell1-2</td></tr>' + 191 '<tr><td valign="top"><br></td></tr>' + 192 '<tr><td id="select">cell2-1</td></tr>' + 193 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 194 "</tbody></table>", 195 "nsITableEditor.insertTableRow(1, false) should insert a row above the second row and rowspan in the first row should be increased"); 196 is(beforeInputEvents.length, 1, 197 'Only one "beforeinput" event should be fired when selection is collapsed in a cell in second row which has row-spanned cell (before)'); 198 checkInputEvent(beforeInputEvents[0], "when selection is collapsed in a cell in second row which has row-spanned cell (before)"); 199 is(inputEvents.length, 1, 200 'Only one "input" event should be fired when selection is collapsed in a cell in second row which has row-spanned cell (before)'); 201 checkInputEvent(inputEvents[0], "when selection is collapsed in a cell in second row which has row-spanned cell (before)"); 202 203 selection.removeAllRanges(); 204 editor.innerHTML = "<table>" + 205 '<tr><td>cell1-1</td><td rowspan="3">cell1-2</td></tr>' + 206 '<tr><td id="select">cell2-1</td></tr>' + 207 "<tr><td>cell3-1</td></tr>" + 208 "</table>"; 209 editor.focus(); 210 beforeInputEvents = []; 211 inputEvents = []; 212 selection.setBaseAndExtent(document.getElementById("select").firstChild, 0, 213 document.getElementById("select").firstChild, 0); 214 getTableEditor().insertTableRow(1, true); 215 is(editor.innerHTML, "<table><tbody>" + 216 '<tr><td>cell1-1</td><td rowspan="4">cell1-2</td></tr>' + 217 '<tr><td id="select">cell2-1</td></tr>' + 218 '<tr><td valign="top"><br></td></tr>' + 219 "<tr><td>cell3-1</td></tr>" + 220 "</tbody></table>", 221 "nsITableEditor.insertTableRow(1, true) should insert a row below the second row and rowspan in the first row should be increased"); 222 is(beforeInputEvents.length, 1, 223 'Only one "beforeinput" event should be fired when selection is collapsed in a cell in second row which has row-spanned cell (after)'); 224 checkInputEvent(beforeInputEvents[0], "when selection is collapsed in a cell in second row which has row-spanned cell (after)"); 225 is(inputEvents.length, 1, 226 'Only one "input" event should be fired when selection is collapsed in a cell in second row which has row-spanned cell (after)'); 227 checkInputEvent(inputEvents[0], "when selection is collapsed in a cell in second row which has row-spanned cell (after)"); 228 229 selection.removeAllRanges(); 230 editor.innerHTML = "<table>" + 231 '<tr><td>cell1-1</td><td id="select" rowspan="2">cell1-2</td></tr>' + 232 "<tr><td>cell2-1</td></tr>" + 233 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 234 "</table>"; 235 editor.focus(); 236 beforeInputEvents = []; 237 inputEvents = []; 238 selection.setBaseAndExtent(document.getElementById("select").firstChild, 0, 239 document.getElementById("select").firstChild, 1); 240 getTableEditor().insertTableRow(2, false); 241 is(editor.innerHTML, "<table><tbody>" + 242 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 243 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 244 '<tr><td>cell1-1</td><td id="select" rowspan="2">cell1-2</td></tr>' + 245 "<tr><td>cell2-1</td></tr>" + 246 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 247 "</tbody></table>", 248 "nsITableEditor.insertTableRow(2, false) should insert 2 rows above the first row"); 249 is(beforeInputEvents.length, 1, 250 'Only one "beforeinput" event should be fired when selection is collapsed in a cell which is row-spanning (before)'); 251 checkInputEvent(beforeInputEvents[0], "when selection is collapsed in a cell which is row-spanning (before)"); 252 is(inputEvents.length, 1, 253 'Only one "input" event should be fired when selection is collapsed in a cell which is row-spanning (before)'); 254 checkInputEvent(inputEvents[0], "when selection is collapsed in a cell which is row-spanning (before)"); 255 256 selection.removeAllRanges(); 257 editor.innerHTML = "<table>" + 258 '<tr><td>cell1-1</td><td id="select" rowspan="2">cell1-2</td></tr>' + 259 "<tr><td>cell2-1</td></tr>" + 260 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 261 "</table>"; 262 editor.focus(); 263 beforeInputEvents = []; 264 inputEvents = []; 265 selection.setBaseAndExtent(document.getElementById("select").firstChild, 0, 266 document.getElementById("select").firstChild, 1); 267 getTableEditor().insertTableRow(2, true); 268 is(editor.innerHTML, "<table><tbody>" + 269 '<tr><td>cell1-1</td><td id="select" rowspan="2">cell1-2</td></tr>' + 270 "<tr><td>cell2-1</td></tr>" + 271 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 272 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 273 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 274 "</tbody></table>", 275 "nsITableEditor.insertTableRow(2, false) should insert 2 rows below the second row (i.e., below the bottom row of the row-spanning cell"); 276 is(beforeInputEvents.length, 1, 277 'Only one "beforeinput" event should be fired when selection is collapsed in a cell which is row-spanning (after)'); 278 checkInputEvent(beforeInputEvents[0], "when selection is collapsed in a cell which is row-spanning (after)"); 279 is(inputEvents.length, 1, 280 'Only one "input" event should be fired when selection is collapsed in a cell which is row-spanning (after)'); 281 checkInputEvent(inputEvents[0], "when selection is collapsed in a cell which is row-spanning (after)"); 282 283 (function testInsertBeforeRowFollowingTextNode() { 284 selection.removeAllRanges(); 285 editor.innerHTML = 286 "<table>" + 287 "<tr><td>cell1-1</td><td>cell1-2</td></tr>\n" + 288 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>\n' + 289 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 290 "</table>"; 291 editor.focus(); 292 beforeInputEvents = []; 293 inputEvents = []; 294 selection.setBaseAndExtent( 295 document.getElementById("select").firstChild, 296 0, 297 document.getElementById("select").firstChild, 298 0 299 ); 300 getTableEditor().insertTableRow(1, false); 301 is( 302 editor.innerHTML, 303 "<table><tbody>" + 304 "<tr><td>cell1-1</td><td>cell1-2</td></tr>\n" + 305 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 306 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>\n' + 307 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 308 "</tbody></table>", 309 "testInsertBeforeRowFollowingTextNode: nsITableEditor.insertTableRow(1, false) should insert a row above the second row"); 310 is( 311 beforeInputEvents.length, 312 1, 313 'testInsertBeforeRowFollowingTextNode: Only one "beforeinput" event should be fired' 314 ); 315 checkInputEvent( 316 beforeInputEvents[0], 317 "when selection is collapsed in a cell whose row follows a text node (testInsertBeforeRowFollowingTextNode)" 318 ); 319 is( 320 inputEvents.length, 321 1, 322 'testInsertBeforeRowFollowingTextNode: Only one "input" event should be fired' 323 ); 324 checkInputEvent( 325 inputEvents[0], 326 "when selection is collapsed in a cell whose row follows a text node (testInsertBeforeRowFollowingTextNode)" 327 ); 328 })(); 329 330 (function testInsertAfterRowFollowedTextNode() { 331 selection.removeAllRanges(); 332 editor.innerHTML = 333 "<table>" + 334 "<tr><td>cell1-1</td><td>cell1-2</td></tr>\n" + 335 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>\n' + 336 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 337 "</table>"; 338 editor.focus(); 339 beforeInputEvents = []; 340 inputEvents = []; 341 selection.setBaseAndExtent( 342 document.getElementById("select").firstChild, 343 0, 344 document.getElementById("select").firstChild, 345 0 346 ); 347 getTableEditor().insertTableRow(1, true); 348 is( 349 editor.innerHTML, 350 "<table><tbody>" + 351 "<tr><td>cell1-1</td><td>cell1-2</td></tr>\n" + 352 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>' + 353 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>\n' + 354 "<tr><td>cell3-1</td><td>cell3-2</td></tr>" + 355 "</tbody></table>", 356 "testInsertAfterRowFollowedTextNode: nsITableEditor.insertTableRow(1, true) should insert a row above the second row"); 357 is( 358 beforeInputEvents.length, 359 1, 360 'testInsertAfterRowFollowedTextNode: Only one "beforeinput" event should be fired' 361 ); 362 checkInputEvent( 363 beforeInputEvents[0], 364 "when selection is collapsed in a cell whose row follows a text node (testInsertAfterRowFollowedTextNode)" 365 ); 366 is( 367 inputEvents.length, 368 1, 369 'testInsertAfterRowFollowedTextNode: Only one "input" event should be fired' 370 ); 371 checkInputEvent( 372 inputEvents[0], 373 "when selection is collapsed in a cell whose row follows a text node (testInsertAfterRowFollowedTextNode)" 374 ); 375 })(); 376 377 (function testInsertAfterLastRow() { 378 selection.removeAllRanges(); 379 editor.innerHTML = 380 "<table>" + 381 "<tr><td>cell1-1</td><td>cell1-2</td></tr>" + 382 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>' + 383 "</table>"; 384 editor.focus(); 385 beforeInputEvents = []; 386 inputEvents = []; 387 selection.collapse(document.getElementById("select").firstChild, 0); 388 getTableEditor().insertTableRow(1, true); 389 is( 390 editor.innerHTML, 391 "<table><tbody>" + 392 "<tr><td>cell1-1</td><td>cell1-2</td></tr>" + 393 '<tr><td id="select">cell2-1</td><td>cell2-2</td></tr>' + 394 '<tr><td valign="top"><br></td><td valign="top"><br></td></tr>' + 395 "</tbody></table>", 396 "testInsertAfterLastRow: nsITableEditor.insertTableRow(1, true) should insert a row after the last row" 397 ); 398 is( 399 beforeInputEvents.length, 400 1, 401 'testInsertAfterLastRow: Only one "beforeinput" event should be fired' 402 ); 403 checkInputEvent( 404 beforeInputEvents[0], 405 "when selection is collapsed in a cell whose row follows a text node (testInsertAfterLastRow)" 406 ); 407 is( 408 inputEvents.length, 409 1, 410 'testInsertAfterLastRow: Only one "input" event should be fired' 411 ); 412 checkInputEvent( 413 inputEvents[0], 414 "when selection is collapsed in a cell whose row follows a text node (testInsertAfterLastRow)" 415 ); 416 })(); 417 418 editor.removeEventListener("beforeinput", onBeforeInput); 419 editor.removeEventListener("input", onInput); 420 421 SimpleTest.finish(); 422 }); 423 424 function getTableEditor() { 425 var editingSession = SpecialPowers.wrap(window).docShell.editingSession; 426 return editingSession.getEditorForWindow(window).QueryInterface(SpecialPowers.Ci.nsITableEditor); 427 } 428 429 </script> 430 </body> 431 432 </html>