window_clipboard_events.html (46557B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>Test for Clipboard Events</title> 6 <script> 7 var SimpleTest = opener.SimpleTest; 8 var SpecialPowers = opener.SpecialPowers; 9 var ok = opener.ok; 10 var is = opener.is; 11 var isnot = opener.isnot; 12 var todo = opener.todo; 13 var todo_is = opener.todo_is; 14 var add_task = opener.add_task; 15 </script> 16 <script src="/tests/SimpleTest/EventUtils.js"></script> 17 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"> 18 </head> 19 <body> 20 <p id="display"></p> 21 <div id="content" style="border: 3px solid black; padding: 3em;">CONTENT TEXT<input id="content-input" value="INPUT TEXT"></div> 22 <img id="image" src="image_50.png"> 23 <button id="button">Button</button> 24 25 <div id="syntheticSpot" oncut="compareSynthetic(event, 'cut')" 26 oncopy="compareSynthetic(event, 'copy')" 27 onpaste="compareSynthetic(event, 'paste')">Spot</div> 28 29 <div id="contenteditableContainer"></div> 30 31 <pre id="test"> 32 <script class="testbody" type="text/javascript"> 33 var content = document.getElementById("content"); 34 var contentInput = document.getElementById("content-input"); 35 var contenteditableContainer = document.getElementById("contenteditableContainer"); 36 var clipboardInitialValue = "empty"; 37 38 var cachedCutData, cachedCopyData, cachedPasteData; 39 40 ok(SpecialPowers.getBoolPref("dom.events.dataTransfer.protected.enabled"), 41 "The following require dom.events.dataTransfer.protected.enabled is enabled"); 42 isnot(typeof window.onbeforeinput, "undefined", 43 "The following tests require onbeforeinput attribute"); 44 45 // Before each test function is run, the clipboard is initialized 46 // to clipboardInitialValue, and the contents of div#content are 47 // set as the window's selection. 48 49 add_task(async function initialize_for_tests() { 50 disableNonTestMouseEvents(true); 51 52 await SimpleTest.promiseFocus(window); 53 54 // Test that clearing and reading the clipboard works. A random number 55 // is used to make sure that leftover clipboard values from a previous 56 // test run don't cause a false-positive test. 57 try { 58 var cb_text = "empty_" + Math.random(); 59 await putOnClipboard(cb_text, () => { setClipboardText(cb_text) }, 60 "Failed to initial set/get clipboard text"); 61 } catch (e) { 62 ok(false, e.toString()); 63 } 64 }); 65 66 var eventListeners = []; 67 68 // Note that don't use EventTarget.addEventListener directly in this file 69 // because if it's remained in next test, it makes developers harder to 70 // investigate such oranges. addEventListenerTo cleans up when `reset()` 71 // is called by first of next test and then, all event listeners including 72 // marked as "once" are removed automatically. 73 function addEventListenerTo(aEventTarget, aEventType, aListener, aOptions) { 74 eventListeners.push({ 75 target: aEventTarget, 76 type: aEventType, 77 listener: aListener, 78 options: aOptions 79 }); 80 aEventTarget.addEventListener(aEventType, aListener, aOptions); 81 } 82 83 async function reset() { 84 [content, contentInput, document, document.documentElement].forEach(eventTarget => { 85 ["oncut", "oncopy", "onpaste", "oninput", "onbeforeinput"].forEach(attr => { 86 eventTarget[attr] = null; 87 }); 88 }); 89 eventListeners.forEach(data => { 90 data.target.removeEventListener(data.type, data.listener, data.options); 91 }); 92 eventListeners = []; 93 94 // Init clipboard 95 await putOnClipboard(clipboardInitialValue, 96 () => { setClipboardText(clipboardInitialValue) }, 97 "reset clipboard"); 98 99 // Reset value of editors. 100 contentInput.value = "INPUT TEXT"; 101 contenteditableContainer.innerHTML = ""; 102 } 103 104 function getClipboardText() { 105 return SpecialPowers.getClipboardData("text/plain"); 106 } 107 108 function getHTMLEditor() { 109 let editingSession = SpecialPowers.wrap(window).docShell.editingSession; 110 if (!editingSession) { 111 return null; 112 } 113 let editor = editingSession.getEditorForWindow(window); 114 if (!editor) { 115 return null; 116 } 117 return editor.QueryInterface(SpecialPowers.Ci.nsIHTMLEditor); 118 } 119 120 async function putOnClipboard(expected, operationFn, desc, type) { 121 try { 122 await SimpleTest.promiseClipboardChange(expected, operationFn, type, 1000); 123 } catch (e) { 124 throw `Failed "${desc}" due to "${e.toString()}"` 125 } 126 } 127 128 async function wontPutOnClipboard(unexpectedData, operationFn, desc, type) { 129 try { 130 // SimpleTest.promiseClipboardChange() doesn't throw exception when 131 // it unexpectedly succeeds to copy something. Therefore, we need 132 // to throw an exception by ourselves. 133 await SimpleTest.promiseClipboardChange(null, operationFn, type, 300, true, false) 134 .then(aData => { 135 if (aData == unexpectedData) { 136 throw `Failed "${desc}", the clipboard data is modified to "${aData.toString()}"`; 137 } 138 }); 139 } catch (e) { 140 throw `Failed "${desc}" due to "${e.toString()}"` 141 } 142 } 143 144 function setClipboardText(text) { 145 var helper = SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"] 146 .getService(SpecialPowers.Ci.nsIClipboardHelper); 147 helper.copyString(text); 148 } 149 150 function selectContentDiv() { 151 // Set selection 152 var selection = window.getSelection(); 153 selection.removeAllRanges(); 154 selection.selectAllChildren(content); 155 } 156 157 function selectContentInput() { 158 contentInput.focus(); 159 contentInput.select(); 160 } 161 162 add_task(async function test_dom_oncopy() { 163 try { 164 await reset(); 165 } catch (e) { 166 ok(false, `Failed to reset (${e.toString()})`); 167 return; 168 } 169 170 // Setup an oncopy event handler, fire copy. Ensure that the event 171 // handler was called, and the clipboard contents have set to CONTENT TEXT. 172 // Test firing oncopy event on ctrl-c: 173 selectContentDiv(); 174 175 var oncopy_fired = false; 176 content.oncopy = function() { oncopy_fired = true; }; 177 try { 178 await putOnClipboard("CONTENT TEXT", () => { 179 synthesizeKey("c", {accelKey: 1}); 180 }, "copy on DOM element set clipboard correctly"); 181 ok(oncopy_fired, "copy event firing on DOM element"); 182 } catch (e) { 183 ok(false, e.toString()); 184 } 185 }); 186 187 add_task(async function test_dom_oncut() { 188 try { 189 await reset(); 190 } catch (e) { 191 ok(false, `Failed to reset (${e.toString()})`); 192 return; 193 } 194 195 // Setup an oncut event handler, fire cut. Ensure that the event handler 196 // was called. The <div> doesn't handle a cut, so ensure that the 197 // clipboard text shouldn't be "CONTENT TEXT". 198 selectContentDiv(); 199 var oncut_fired = false; 200 content.oncut = function() { oncut_fired = true; }; 201 try { 202 await wontPutOnClipboard("CONTENT TEXT", () => { 203 synthesizeKey("x", {accelKey: 1}); 204 }, "cut on DOM element set clipboard correctly"); 205 ok(oncut_fired, "cut event firing on DOM element") 206 } catch (e) { 207 ok(false, e.toString()); 208 } 209 }); 210 211 add_task(async function test_dom_onpaste() { 212 try { 213 await reset(); 214 } catch (e) { 215 ok(false, `Failed to reset (${e.toString()})`); 216 return; 217 } 218 219 // Setup an onpaste event handler, fire paste. Ensure that the event 220 // handler was called. 221 selectContentDiv(); 222 var onpaste_fired = false; 223 content.onpaste = function() { onpaste_fired = true; }; 224 synthesizeKey("v", {accelKey: 1}); 225 ok(onpaste_fired, "paste event firing on DOM element"); 226 }); 227 228 add_task(async function test_dom_oncopy_abort() { 229 try { 230 await reset(); 231 } catch (e) { 232 ok(false, `Failed to reset (${e.toString()})`); 233 return; 234 } 235 236 // Setup an oncopy event handler that aborts the copy, and fire the copy 237 // event. Ensure that the event handler was fired, and the clipboard 238 // contents have not been modified. 239 selectContentDiv(); 240 var oncopy_fired = false; 241 content.oncopy = function() { oncopy_fired = true; return false; }; 242 try { 243 await wontPutOnClipboard("CONTENT TEXT", () => { 244 synthesizeKey("c", {accelKey: 1}); 245 }, "aborted copy on DOM element did not modify clipboard"); 246 ok(oncopy_fired, "copy event (to-be-cancelled) firing on DOM element"); 247 } catch (e) { 248 ok(false, e.toString()); 249 } 250 }); 251 252 add_task(async function test_input_oncopy() { 253 try { 254 await reset(); 255 } catch (e) { 256 ok(false, `Failed to reset (${e.toString()})`); 257 return; 258 } 259 260 // Setup an oncopy event handler, fire copy. Ensure that the event 261 // handler was called, and the clipboard contents have been set to 'PUT TE', 262 // which is the part that is selected below. 263 selectContentInput(); 264 contentInput.focus(); 265 contentInput.setSelectionRange(2, 8); 266 267 let oncopy_fired = false; 268 let onbeforeinput_fired = false; 269 let oninput_fired = false; 270 contentInput.oncopy = () => { oncopy_fired = true; }; 271 contentInput.onbeforeinput = () => { onbeforeinput = true; }; 272 contentInput.oninput = () => { oninput_fired = true; }; 273 try { 274 await putOnClipboard("PUT TE", () => { 275 synthesizeKey("c", {accelKey: 1}); 276 }, "copy on plaintext editor set clipboard correctly"); 277 ok(oncopy_fired, "copy event firing on plaintext editor"); 278 ok(!onbeforeinput_fired, "beforeinput event shouldn't be fired on plaintext editor by copy"); 279 ok(!oninput_fired, "input event shouldn't be fired on plaintext editor by copy"); 280 } catch (e) { 281 ok(false, e.toString()); 282 } 283 }); 284 285 add_task(async function test_input_oncut() { 286 try { 287 await reset(); 288 } catch (e) { 289 ok(false, `Failed to reset (${e.toString()})`); 290 return; 291 } 292 293 // Setup an oncut event handler, and fire cut. Ensure that the event 294 // handler was fired, the clipboard contains the INPUT TEXT, and 295 // that the input itself is empty. 296 selectContentInput(); 297 let oncut_fired = false; 298 let beforeInputEvents = []; 299 let inputEvents = []; 300 contentInput.oncut = () => { oncut_fired = true; }; 301 contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); } 302 contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); } 303 try { 304 await putOnClipboard("INPUT TEXT", () => { 305 synthesizeKey("x", {accelKey: 1}); 306 }, "cut on plaintext editor set clipboard correctly"); 307 ok(oncut_fired, "cut event firing on plaintext editor"); 308 is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by cut'); 309 if (beforeInputEvents.length) { 310 is(beforeInputEvents[0].inputType, "deleteByCut", '"inputType" of "beforeinput" event should be "deleteByCut"'); 311 is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "deleteByCut" should be cancelable'); 312 is(beforeInputEvents[0].data, null, '"data" of "beforeinput" event for "deleteByCut" should be null'); 313 is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "deleteByCut" should be null'); 314 is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "deleteByCut" should return empty array'); 315 } 316 is(inputEvents.length, 1, '"input" event should be fired once by cut'); 317 if (inputEvents.length) { 318 is(inputEvents[0].inputType, "deleteByCut", '"inputType" of "input" event should be "deleteByCut"'); 319 is(inputEvents[0].cancelable, false, '"input" event for "deleteByCut" should not be cancelable'); 320 is(inputEvents[0].data, null, '"data" of "input" event for "deleteByCut" should be null'); 321 is(inputEvents[0].dataTransfer, null, '"dataTransfer" of "input" event for "deleteByCut" should be null'); 322 is(inputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "input" event for "deleteByCut" should return empty array'); 323 } 324 is(contentInput.value, "", 325 "cut on plaintext editor emptied editor"); 326 } catch (e) { 327 ok(false, e.toString()); 328 } 329 }); 330 331 add_task(async function test_input_onpaste() { 332 try { 333 await reset(); 334 } catch (e) { 335 ok(false, `Failed to reset (${e.toString()})`); 336 return; 337 } 338 339 // Setup an onpaste event handler, and fire paste. Ensure that the event 340 // handler was fired, the clipboard contents didn't change, and that the 341 // input value did change (ie. paste succeeded). 342 selectContentInput(); 343 let onpaste_fired = false; 344 let beforeInputEvents = []; 345 let inputEvents = []; 346 contentInput.onpaste = () => { onpaste_fired = true; }; 347 contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); } 348 contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); } 349 350 synthesizeKey("v", {accelKey: 1}); 351 ok(onpaste_fired, "paste event firing on plaintext editor"); 352 is(getClipboardText(), clipboardInitialValue, 353 "paste on plaintext editor did not modify clipboard contents"); 354 is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by paste'); 355 if (beforeInputEvents.length) { 356 is(beforeInputEvents[0].inputType, "insertFromPaste", '"inputType" of "beforeinput" event should be "insertFromPaste"'); 357 is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "insertFromPaste" should be cancelable'); 358 is(beforeInputEvents[0].data, clipboardInitialValue, `"data" of "beforeinput" event for "insertFromPaste" should be "${clipboardInitialValue}"`); 359 is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "insertFromPaste" should be null'); 360 is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "insertFromPaste" should return empty array'); 361 } 362 is(inputEvents.length, 1, '"input" event should be fired once by paste'); 363 if (inputEvents.length) { 364 is(inputEvents[0].inputType, "insertFromPaste", '"inputType" of "input" event should be "insertFromPaste"'); 365 is(inputEvents[0].cancelable, false, '"input" event for "insertFromPaste" should not be cancelable'); 366 is(inputEvents[0].data, clipboardInitialValue, `"data" of "input" event for "insertFromPaste" should be "${clipboardInitialValue}"`); 367 is(inputEvents[0].dataTransfer, null, '"dataTransfer" of "input" event for "insertFromPaste" should be null'); 368 is(inputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "input" event for "insertFromPaste" should return empty array'); 369 } 370 is(contentInput.value, clipboardInitialValue, 371 "paste on plaintext editor did modify editor value"); 372 }); 373 374 add_task(async function test_input_oncopy_abort() { 375 try { 376 await reset(); 377 } catch (e) { 378 ok(false, `Failed to reset (${e.toString()})`); 379 return; 380 } 381 382 // Setup an oncopy event handler, fire copy. Ensure that the event 383 // handler was called, and that the clipboard value did NOT change. 384 selectContentInput(); 385 let oncopy_fired = false; 386 contentInput.oncopy = () => { oncopy_fired = true; return false; }; 387 contentInput.onbeforeinput = () => { 388 ok(false, '"beforeinput" event should not be fired by copy but canceled'); 389 }; 390 contentInput.oninput = function() { 391 ok(false, '"input" event should not be fired by copy but canceled'); 392 }; 393 try { 394 await wontPutOnClipboard("CONTENT TEXT", () => { 395 synthesizeKey("c", {accelKey: 1}); 396 }, "aborted copy on plaintext editor did not modify clipboard"); 397 ok(oncopy_fired, "copy event (to-be-cancelled) firing on plaintext editor"); 398 } catch (e) { 399 ok(false, e.toString()); 400 } 401 }); 402 403 add_task(async function test_input_oncut_abort() { 404 try { 405 await reset(); 406 } catch (e) { 407 ok(false, `Failed to reset (${e.toString()})`); 408 return; 409 } 410 411 // Setup an oncut event handler, and fire cut. Ensure that the event 412 // handler was fired, the clipboard contains the INPUT TEXT, and 413 // that the input itself is empty. 414 selectContentInput(); 415 let oncut_fired = false; 416 contentInput.oncut = () => { oncut_fired = true; return false; }; 417 contentInput.onbeforeinput = () => { 418 ok(false, '"beforeinput" event should not be fired by cut but canceled by "cut" event listener'); 419 }; 420 contentInput.oninput = () => { 421 ok(false, '"input" event should not be fired by cut but canceled by "cut" event listener'); 422 }; 423 try { 424 await wontPutOnClipboard("CONTENT TEXT", () => { 425 synthesizeKey("x", {accelKey: 1}); 426 }, "aborted cut on plaintext editor did not modify clipboard"); 427 ok(oncut_fired, "cut event (to-be-cancelled) firing on plaintext editor"); 428 is(contentInput.value, "INPUT TEXT", 429 "aborted cut on plaintext editor did not modify editor contents"); 430 } catch (e) { 431 ok(false, e.toString()); 432 } 433 }); 434 435 add_task(async function test_input_oncut_beforeinput_abort() { 436 try { 437 await reset(); 438 } catch (e) { 439 ok(false, `Failed to reset (${e.toString()})`); 440 return; 441 } 442 443 // Setup an oncut event handler, and fire cut. Ensure that the event 444 // handler was fired, the clipboard contains the INPUT TEXT, and 445 // that the input itself is empty. 446 selectContentInput(); 447 let oncut_fired = false; 448 let beforeInputEvents = []; 449 let inputEvents = []; 450 contentInput.oncut = () => { oncut_fired = true; }; 451 contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); aEvent.preventDefault(); } 452 contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); } 453 try { 454 await putOnClipboard("INPUT TEXT", () => { 455 synthesizeKey("x", {accelKey: 1}); 456 }, "cut on plaintext editor set clipboard correctly"); 457 ok(oncut_fired, "cut event firing on plaintext editor"); 458 is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by cut'); 459 if (beforeInputEvents.length) { 460 is(beforeInputEvents[0].inputType, "deleteByCut", '"inputType" of "beforeinput" event should be "deleteByCut"'); 461 is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "deleteByCut" should be cancelable'); 462 is(beforeInputEvents[0].data, null, '"data" of "beforeinput" event for "deleteByCut" should be null'); 463 is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "deleteByCut" should be null'); 464 is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "deleteByCut" should return empty array'); 465 } 466 is(inputEvents.length, 0, '"input" event should not be fired by cut if "beforeinput" event is canceled'); 467 is(contentInput.value, "INPUT TEXT", 468 'cut on plaintext editor should not change editor since "beforeinput" event was canceled'); 469 } catch (e) { 470 ok(false, e.toString()); 471 } 472 }); 473 474 add_task(async function test_input_onpaste_abort() { 475 try { 476 await reset(); 477 } catch (e) { 478 ok(false, `Failed to reset (${e.toString()})`); 479 return; 480 } 481 482 // Setup an onpaste event handler, and fire paste. Ensure that the event 483 // handler was fired, the clipboard contents didn't change, and that the 484 // input value did change (ie. paste succeeded). 485 selectContentInput(); 486 let onpaste_fired = false; 487 contentInput.onpaste = () => { onpaste_fired = true; return false; }; 488 contentInput.onbeforeinput = () => { 489 ok(false, '"beforeinput" event should not be fired by paste but canceled'); 490 }; 491 contentInput.oninput = () => { 492 ok(false, '"input" event should not be fired by paste but canceled'); 493 }; 494 synthesizeKey("v", {accelKey: 1}); 495 ok(onpaste_fired, 496 "paste event (to-be-cancelled) firing on plaintext editor"); 497 is(getClipboardText(), clipboardInitialValue, 498 "aborted paste on plaintext editor did not modify clipboard"); 499 is(contentInput.value, "INPUT TEXT", 500 "aborted paste on plaintext editor did not modify modified editor value"); 501 }); 502 503 add_task(async function test_input_onpaste_beforeinput_abort() { 504 try { 505 await reset(); 506 } catch (e) { 507 ok(false, `Failed to reset (${e.toString()})`); 508 return; 509 } 510 511 // Setup an onpaste event handler, and fire paste. Ensure that the event 512 // handler was fired, the clipboard contents didn't change, and that the 513 // input value did change (ie. paste succeeded). 514 selectContentInput(); 515 let onpaste_fired = false; 516 let beforeInputEvents = []; 517 let inputEvents = []; 518 contentInput.onpaste = () => { onpaste_fired = true; }; 519 contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); aEvent.preventDefault(); } 520 contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); } 521 522 synthesizeKey("v", {accelKey: 1}); 523 ok(onpaste_fired, "paste event firing on plaintext editor"); 524 is(getClipboardText(), clipboardInitialValue, 525 "paste on plaintext editor did not modify clipboard contents"); 526 is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by paste'); 527 if (beforeInputEvents.length) { 528 is(beforeInputEvents[0].inputType, "insertFromPaste", '"inputType" of "beforeinput" event should be "insertFromPaste"'); 529 is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "insertFromPaste" should be cancelable'); 530 is(beforeInputEvents[0].data, clipboardInitialValue, `"data" of "beforeinput" event for "insertFromPaste" should be "${clipboardInitialValue}"`); 531 is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "insertFromPaste" should be null'); 532 is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "insertFromPaste" should return empty array'); 533 } 534 is(inputEvents.length, 0, '"input" event should not be fired by paste when "beforeinput" is canceled'); 535 is(contentInput.value, "INPUT TEXT", 536 "paste on plaintext editor did modify editor value"); 537 }); 538 539 add_task(async function test_input_cut_dataTransfer() { 540 try { 541 await reset(); 542 } catch (e) { 543 ok(false, `Failed to reset (${e.toString()})`); 544 return; 545 } 546 547 // Cut using event.dataTransfer. The event is not cancelled so the default 548 // cut should occur 549 selectContentInput(); 550 contentInput.oncut = function(event) { 551 ok(event instanceof ClipboardEvent, "cut event is a ClipboardEvent"); 552 ok(event.clipboardData instanceof DataTransfer, "cut event dataTransfer is a DataTransfer"); 553 is(event.target, contentInput, "cut event target"); 554 is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 0, "cut event mozItemCount"); 555 is(event.clipboardData.getData("text/plain"), "", "cut event getData"); 556 event.clipboardData.setData("text/plain", "This is some dataTransfer text"); 557 cachedCutData = event.clipboardData; 558 }; 559 try { 560 await putOnClipboard("INPUT TEXT", () => { 561 synthesizeKey("x", {accelKey: 1}); 562 }, "cut using dataTransfer on plaintext editor set clipboard correctly"); 563 is(contentInput.value, "", 564 "cut using dataTransfer on plaintext editor cleared input"); 565 } catch (e) { 566 ok(false, e.toString()); 567 } 568 }); 569 570 add_task(async function test_input_cut_abort_dataTransfer() { 571 try { 572 await reset(); 573 } catch (e) { 574 ok(false, `Failed to reset (${e.toString()})`); 575 return; 576 } 577 578 // Cut using event.dataTransfer but cancel the event. The data should be 579 // put on the clipboard but since we don't modify the input value, the input 580 // should have the same value. 581 selectContentInput(); 582 contentInput.oncut = function(event) { 583 event.clipboardData.setData("text/plain", "Cut dataTransfer text"); 584 return false; 585 }; 586 try { 587 await putOnClipboard("Cut dataTransfer text", () => { 588 synthesizeKey("x", {accelKey: 1}); 589 }, "aborted cut using dataTransfer on plaintext editor set clipboard correctly"); 590 is(contentInput.value, "INPUT TEXT", 591 "aborted cut using dataTransfer on plaintext editor did not modify input"); 592 } catch (e) { 593 ok(false, e.toString()); 594 } 595 }); 596 597 add_task(async function test_input_copy_dataTransfer() { 598 try { 599 await reset(); 600 } catch (e) { 601 ok(false, `Failed to reset (${e.toString()})`); 602 return; 603 } 604 605 // Copy using event.dataTransfer 606 selectContentInput(); 607 contentInput.oncopy = function(event) { 608 ok(event instanceof ClipboardEvent, "copy event is a ClipboardEvent"); 609 ok(event.clipboardData instanceof DataTransfer, "copy event dataTransfer is a DataTransfer"); 610 is(event.target, contentInput, "copy event target"); 611 is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 0, "copy event mozItemCount"); 612 is(event.clipboardData.getData("text/plain"), "", "copy event getData"); 613 event.clipboardData.setData("text/plain", "Copied dataTransfer text"); 614 cachedCopyData = event.clipboardData; 615 }; 616 try { 617 await putOnClipboard("INPUT TEXT", () => { 618 synthesizeKey("c", {accelKey: 1}); 619 }, "copy using dataTransfer on plaintext editor set clipboard correctly"); 620 is(contentInput.value, "INPUT TEXT", 621 "copy using dataTransfer on plaintext editor did not modify input"); 622 } catch (e) { 623 ok(false, e.toString()); 624 } 625 }); 626 627 add_task(async function test_input_copy_abort_dataTransfer() { 628 try { 629 await reset(); 630 } catch (e) { 631 ok(false, `Failed to reset (${e.toString()})`); 632 return; 633 } 634 635 // Copy using event.dataTransfer but cancel the event. 636 selectContentInput(); 637 contentInput.oncopy = function(event) { 638 event.clipboardData.setData("text/plain", "Copy dataTransfer text"); 639 return false; 640 }; 641 try { 642 await putOnClipboard("Copy dataTransfer text", () => { 643 synthesizeKey("c", {accelKey: 1}); 644 }, "aborted copy using dataTransfer on plaintext editor set clipboard correctly"); 645 is(contentInput.value, "INPUT TEXT", 646 "aborted copy using dataTransfer on plaintext editor did not modify input"); 647 } catch (e) { 648 ok(false, e.toString()); 649 } 650 }); 651 652 add_task(async function test_input_paste_dataTransfer() { 653 try { 654 await reset(); 655 } catch (e) { 656 ok(false, `Failed to reset (${e.toString()})`); 657 return; 658 } 659 660 // Paste using event.dataTransfer 661 selectContentInput(); 662 contentInput.onpaste = function(event) { 663 ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent"); 664 ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer"); 665 is(event.target, contentInput, "paste event target"); 666 is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 1, "paste event mozItemCount"); 667 is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "paste event getData"); 668 cachedPasteData = event.clipboardData; 669 }; 670 synthesizeKey("v", {accelKey: 1}); 671 is(getClipboardText(), clipboardInitialValue, 672 "paste using dataTransfer on plaintext editor did not modify clipboard contents"); 673 is(contentInput.value, clipboardInitialValue, 674 "paste using dataTransfer on plaintext editor modified input"); 675 }); 676 677 add_task(async function test_input_paste_abort_dataTransfer() { 678 try { 679 await reset(); 680 } catch (e) { 681 ok(false, `Failed to reset (${e.toString()})`); 682 return; 683 } 684 685 // Paste using event.dataTransfer but cancel the event 686 selectContentInput(); 687 contentInput.onpaste = function(event) { 688 is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "get data on aborted paste"); 689 contentInput.value = "Alternate Paste"; 690 return false; 691 }; 692 synthesizeKey("v", {accelKey: 1}); 693 is(getClipboardText(), clipboardInitialValue, 694 "aborted paste using dataTransfer on plaintext editor did not modify clipboard contents"); 695 is(contentInput.value, "Alternate Paste", 696 "aborted paste using dataTransfer on plaintext editor modified input"); 697 }); 698 699 add_task(async function test_input_copypaste_dataTransfer_multiple() { 700 try { 701 await reset(); 702 } catch (e) { 703 ok(false, `Failed to reset (${e.toString()})`); 704 return; 705 } 706 707 // Cut several types of data and paste it again 708 contentInput.value = "This is a line of text"; 709 contentInput.oncopy = function(event) { 710 var cd = event.clipboardData; 711 cd.setData("text/plain", "would be a phrase"); 712 713 var exh = false; 714 try { SpecialPowers.wrap(cd).mozSetDataAt("text/plain", "Text", 1); } catch (ex) { exh = true; } 715 ok(exh, "exception occured mozSetDataAt 1"); 716 exh = false; 717 try { SpecialPowers.wrap(cd).mozTypesAt(1); } catch (ex) { exh = true; } 718 ok(exh, "exception occured mozTypesAt 1"); 719 exh = false; 720 try { SpecialPowers.wrap(cd).mozGetDataAt("text/plain", 1); } catch (ex) { exh = true; } 721 ok(exh, "exception occured mozGetDataAt 1"); 722 exh = false; 723 try { cd.mozClearDataAt("text/plain", 1); } catch (ex) { exh = true; } 724 ok(exh, "exception occured mozClearDataAt 1"); 725 726 cd.setData("text/x-moz-url", "http://www.mozilla.org\nMozilla Page Title"); 727 SpecialPowers.wrap(cd).mozSetDataAt("text/x-custom", "Custom Text with \u0000 null", 0); 728 is(SpecialPowers.wrap(cd).mozItemCount, 1, "mozItemCount after set multiple types"); 729 return false; 730 }; 731 732 try { 733 selectContentInput(); 734 735 await putOnClipboard("would be a phrase", () => { 736 synthesizeKey("c", {accelKey: 1}); 737 }, "copy multiple types text"); 738 contentInput.oncopy = null; // XXX Not sure why this is required... 739 } catch (e) { 740 ok(false, e.toString()); 741 return; 742 } 743 744 contentInput.setSelectionRange(5, 14); 745 746 contentInput.onpaste = function(event) { 747 var cd = event.clipboardData; 748 is(SpecialPowers.wrap(cd).mozItemCount, 1, "paste after copy multiple types mozItemCount"); 749 is(cd.getData("text/plain"), "would be a phrase", "paste text/plain multiple types"); 750 751 // Firefox for Android's clipboard code doesn't handle x-moz-url. Therefore 752 // disabling the following test. Enable this once bug #840101 is fixed. 753 if (!navigator.appVersion.includes("Android")) { 754 is(cd.getData("text/x-moz-url"), "http://www.mozilla.org\nMozilla Page Title", "paste text/x-moz-url multiple types"); 755 is(cd.getData("text/x-custom"), "Custom Text with \u0000 null", "paste text/custom multiple types"); 756 } else { 757 is(cd.getData("text/x-custom"), "", "paste text/custom multiple types"); 758 } 759 760 is(cd.getData("application/x-moz-custom-clipdata"), "", "application/x-moz-custom-clipdata is not present"); 761 762 exh = false; 763 try { cd.setData("application/x-moz-custom-clipdata", "Some Data"); } catch (ex) { exh = true; } 764 ok(exh, "exception occured setData with application/x-moz-custom-clipdata"); 765 766 exh = false; 767 try { cd.setData("text/plain", "Text on Paste"); } catch (ex) { exh = true; } 768 ok(exh, "exception occured setData on paste"); 769 770 is(cd.getData("text/plain"), "would be a phrase", "text/plain data unchanged"); 771 }; 772 synthesizeKey("v", {accelKey: 1}); 773 is(contentInput.value, "This would be a phrase of text", 774 "default paste after copy multiple types"); 775 }); 776 777 add_task(async function test_input_copy_button_dataTransfer() { 778 try { 779 await reset(); 780 } catch (e) { 781 ok(false, `Failed to reset (${e.toString()})`); 782 return; 783 } 784 785 // Copy using event.dataTransfer when a button is focused. 786 var button = document.getElementById("button"); 787 button.focus(); 788 button.oncopy = function(event) { 789 ok(false, "should not be firing copy event on button"); 790 return false; 791 }; 792 try { 793 // copy should not occur here because buttons don't have any controller 794 // for the copy command 795 await wontPutOnClipboard("", () => { 796 synthesizeKey("c", {accelKey: 1}); 797 }, "Accel-C on the `<button>` shouldn't modify the clipboard data"); 798 ok(true, "Accel-C on the <button> shouldn't modify the clipboard data"); 799 } catch (e) { 800 ok(false, e.toString()); 801 } 802 803 try { 804 selectContentDiv(); 805 806 await putOnClipboard("CONTENT TEXT", () => { 807 synthesizeKey("c", {accelKey: 1}); 808 }, "Accel-C with selecting the content <div> should modify the clipboard data with text in it"); 809 } catch (e) { 810 ok(false, e.toString()); 811 } 812 }); 813 814 add_task(async function test_eventspref_disabled() { 815 try { 816 await reset(); 817 } catch (e) { 818 ok(false, `Failed to reset (${e.toString()})`); 819 return; 820 } 821 822 // Disable clipboard events 823 try { 824 await SpecialPowers.pushPrefEnv({ 825 set: [['dom.event.clipboardevents.enabled', false]] 826 }); 827 828 var event_fired = false; 829 var input_data = undefined; 830 contentInput.oncut = function() { event_fired = true; }; 831 contentInput.oncopy = function() { event_fired = true; }; 832 contentInput.onpaste = function() { event_fired = true; }; 833 contentInput.oninput = function(event) { input_data = event.data; }; 834 835 selectContentInput(); 836 contentInput.setSelectionRange(1, 4); 837 838 await putOnClipboard("NPU", () => { 839 synthesizeKey("x", {accelKey: 1}); 840 }, "cut changed clipboard when preference is disabled"); 841 is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled"); 842 ok(!event_fired, "cut event did not fire when preference is disabled"); 843 is(input_data, null, "cut should cause input event whose data value is null"); 844 845 event_fired = false; 846 input_data = undefined; 847 contentInput.setSelectionRange(3, 6); 848 await putOnClipboard("TEX", () => { 849 synthesizeKey("c", {accelKey: 1}); 850 }, "copy changed clipboard when preference is disabled"); 851 ok(!event_fired, "copy event did not fire when preference is disabled") 852 is(input_data, undefined, "copy shouldn't cause input event"); 853 854 event_fired = false; 855 contentInput.setSelectionRange(0, 2); 856 synthesizeKey("v", {accelKey: 1}); 857 is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled"); 858 ok(!event_fired, "paste event did not fire when preference is disabled"); 859 is(input_data, "", 860 "paste should cause input event but whose data value should be empty string if clipboard event is disabled"); 861 } catch (e) { 862 ok(false, e.toString()); 863 } finally { 864 await SpecialPowers.popPrefEnv(); 865 } 866 }); 867 868 let expectedData = []; 869 870 // Check to make that synthetic events do not change the clipboard 871 add_task(async function test_synthetic_events() { 872 try { 873 await reset(); 874 } catch (e) { 875 ok(false, `Failed to reset (${e.toString()})`); 876 return; 877 } 878 879 let syntheticSpot = document.getElementById("syntheticSpot"); 880 881 // No dataType specified 882 let event = new ClipboardEvent("cut", { data: "something" }); 883 expectedData = { type: "cut", data: null } 884 compareSynthetic(event, "before"); 885 syntheticSpot.dispatchEvent(event); 886 ok(expectedData.eventFired, "cut event fired"); 887 compareSynthetic(event, "after"); 888 889 event = new ClipboardEvent("cut", { dataType: "text/plain", data: "something" }); 890 expectedData = { type: "cut", dataType: "text/plain", data: "something" } 891 compareSynthetic(event, "before"); 892 syntheticSpot.dispatchEvent(event); 893 ok(expectedData.eventFired, "cut event fired"); 894 compareSynthetic(event, "after"); 895 896 event = new ClipboardEvent("copy", { dataType: "text/plain", data: "something" }); 897 expectedData = { type: "copy", dataType: "text/plain", data: "something" } 898 compareSynthetic(event, "before"); 899 syntheticSpot.dispatchEvent(event); 900 ok(expectedData.eventFired, "copy event fired"); 901 compareSynthetic(event, "after"); 902 903 event = new ClipboardEvent("copy", { dataType: "text/plain" }); 904 expectedData = { type: "copy", dataType: "text/plain", data: "" } 905 compareSynthetic(event, "before"); 906 syntheticSpot.dispatchEvent(event); 907 ok(expectedData.eventFired, "copy event fired"); 908 compareSynthetic(event, "after"); 909 910 event = new ClipboardEvent("paste", { dataType: "text/plain", data: "something" }); 911 expectedData = { type: "paste", dataType: "text/plain", data: "something" } 912 compareSynthetic(event, "before"); 913 syntheticSpot.dispatchEvent(event); 914 ok(expectedData.eventFired, "paste event fired"); 915 compareSynthetic(event, "after"); 916 917 event = new ClipboardEvent("paste", { dataType: "application/unknown", data: "unknown" }); 918 expectedData = { type: "paste", dataType: "application/unknown", data: "unknown" } 919 compareSynthetic(event, "before"); 920 syntheticSpot.dispatchEvent(event); 921 ok(expectedData.eventFired, "paste event fired"); 922 compareSynthetic(event, "after"); 923 }); 924 925 function compareSynthetic(event, eventtype) { 926 let step = (eventtype == "cut" || eventtype == "copy" || eventtype == "paste") ? "during" : eventtype; 927 if (step == "during") { 928 is(eventtype, expectedData.type, "synthetic " + eventtype + " event fired"); 929 } 930 931 ok(event.clipboardData instanceof DataTransfer, "clipboardData is assigned"); 932 933 is(event.type, expectedData.type, "synthetic " + eventtype + " event type"); 934 if (expectedData.data === null) { 935 is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 0, "synthetic " + eventtype + " empty data"); 936 } 937 else { 938 is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 1, "synthetic " + eventtype + " item count"); 939 is(event.clipboardData.types.length, 1, "synthetic " + eventtype + " types length"); 940 is(event.clipboardData.getData(expectedData.dataType), expectedData.data, 941 "synthetic " + eventtype + " data"); 942 } 943 944 is(getClipboardText(), "empty", "event does not change the clipboard " + step + " dispatch"); 945 946 if (step == "during") { 947 expectedData.eventFired = true; 948 } 949 } 950 951 async function checkCachedDataTransfer(cd, eventtype) { 952 var testprefix = "cached " + eventtype + " dataTransfer"; 953 954 try { 955 await putOnClipboard("Some Clipboard Text", () => { setClipboardText("Some Clipboard Text") }, 956 "change clipboard outside of event"); 957 } catch (e) { 958 ok(false, e.toString()); 959 return; 960 } 961 962 var oldtext = cd.getData("text/plain"); 963 ok(!oldtext, "clipboard get using " + testprefix); 964 965 try { 966 SpecialPowers.wrap(cd).mozSetDataAt("text/plain", "Test Cache Data", 0); 967 } catch (ex) {} 968 ok(!cd.getData("text/plain"), "clipboard set using " + testprefix); 969 970 is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix); 971 972 try { 973 cd.mozClearDataAt("text/plain", 0); 974 } catch (ex) {} 975 ok(!cd.getData("text/plain"), "clipboard clear using " + testprefix); 976 977 is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix); 978 } 979 980 add_task(async function test_modify_datatransfer_outofevent() { 981 try { 982 await reset(); 983 } catch (e) { 984 ok(false, `Failed to reset (${e.toString()})`); 985 return; 986 } 987 988 // Check if the cached clipboard data can be accessed or modified 989 // and whether it modifies the real clipboard 990 991 // XXX Depends on test_input_cut_dataTransfer() 992 if (cachedCutData) { 993 await checkCachedDataTransfer(cachedCutData, "cut"); 994 } else { 995 todo(false, "test_input_cut_dataTransfer must have been failed, skipping tests with its dataTransfer"); 996 } 997 // XXX Depends on test_input_copy_dataTransfer() 998 if (cachedCopyData) { 999 await checkCachedDataTransfer(cachedCopyData, "copy"); 1000 } else { 1001 todo(false, "test_input_copy_dataTransfer must have been failed, skipping tests with its dataTransfer"); 1002 } 1003 // XXX Depends on test_input_paste_dataTransfer() 1004 if (cachedPasteData) { 1005 await checkCachedDataTransfer(cachedPasteData, "paste"); 1006 } else { 1007 todo(false, "test_input_paste_dataTransfer must have been failed, skipping tests with its dataTransfer"); 1008 } 1009 }); 1010 1011 add_task(async function test_input_cut_disallowed_types_dataTransfer() { 1012 try { 1013 await reset(); 1014 } catch (e) { 1015 ok(false, `Failed to reset (${e.toString()})`); 1016 return; 1017 } 1018 1019 selectContentInput(); 1020 let oncutExecuted = false; 1021 contentInput.oncut = function(event) { 1022 // Setting an arbitrary type should be OK 1023 try { 1024 event.clipboardData.setData("apple/cider", "Anything your heart desires"); 1025 ok(true, "We should have successfully executed the setData call"); 1026 } catch(e) { 1027 ok(false, "We should not have gotten an exception for trying to set that data"); 1028 } 1029 1030 // Unless that type happens to be application/x-moz-custom-clipdata 1031 try { 1032 event.clipboardData.setData("application/x-moz-custom-clipdata", "Anything your heart desires"); 1033 ok(false, "We should not have successfully executed the setData call"); 1034 } catch(e) { 1035 is(e.name, "NotSupportedError", 1036 "We should have gotten an NotSupportedError exception for trying to set that data"); 1037 } 1038 oncutExecuted = true; 1039 }; 1040 1041 try { 1042 await putOnClipboard("INPUT TEXT", () => { 1043 synthesizeKey("x", {accelKey: 1}); 1044 }, "The oncut handler should have been executed data"); 1045 ok(oncutExecuted, "The oncut handler should have been executed"); 1046 } catch (e) { 1047 ok(false, "Failed to copy the data given by the oncut"); 1048 } 1049 }); 1050 1051 // Try copying an image to the clipboard and make sure that it looks correct when pasting it. 1052 add_task(async function test_image_dataTransfer() { 1053 // cmd_copyImageContents errors on Android (bug 1299578). 1054 if (navigator.userAgent.includes("Android")) { 1055 return; 1056 } 1057 1058 try { 1059 await reset(); 1060 } catch (e) { 1061 ok(false, `Failed to reset (${e.toString()})`); 1062 return; 1063 } 1064 1065 // Copy the image's data to the clipboard 1066 try { 1067 await putOnClipboard("", () => { 1068 SpecialPowers.setCommandNode(window, document.getElementById("image")); 1069 SpecialPowers.doCommand(window, "cmd_copyImageContents"); 1070 }, "copy changed clipboard when preference is disabled"); 1071 } catch (e) { 1072 ok(false, e.toString()); 1073 } 1074 1075 let onpasteCalled = false; 1076 document.onpaste = function(event) { 1077 ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent"); 1078 ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer"); 1079 let items = event.clipboardData.items; 1080 let foundData = false; 1081 for (let i = 0; i < items.length; ++i) { 1082 if (items[i].kind == "file") { 1083 foundData = true; 1084 is(items[i].type, "image/png", "The type of the data must be image/png"); 1085 is(items[i].getAsFile().type, "image/png", "The attached file must be image/png"); 1086 } 1087 } 1088 ok(foundData, "Should have found a file entry in the DataTransferItemList"); 1089 let files = event.clipboardData.files; 1090 is(files.length, 1, "There should only be one file on the DataTransfer"); 1091 is(files[0].type, "image/png", "The only file should be an image/png"); 1092 onpasteCalled = true; 1093 } 1094 1095 synthesizeKey("v", {accelKey: 1}); 1096 ok(onpasteCalled, "The paste event listener must have been called"); 1097 }); 1098 1099 add_task(async function test_event_target() { 1100 try { 1101 await reset(); 1102 } catch (e) { 1103 ok(false, `Failed to reset (${e.toString()})`); 1104 return; 1105 } 1106 1107 let copyTarget = null; 1108 addEventListenerTo(document, "copy", (event) => { copyTarget = event.target; }, {once: true}); 1109 1110 if (document.activeElement) { 1111 document.activeElement.blur(); 1112 } 1113 1114 let selection = document.getSelection(); 1115 selection.setBaseAndExtent(content.firstChild, "CONTENT ".length, 1116 content.firstChild, "CONTENT TEXT".length); 1117 1118 try { 1119 await putOnClipboard("TEXT", () => { 1120 synthesizeKey("c", {accelKey: 1}); 1121 }, "copy text from non-editable element"); 1122 } catch (e) { 1123 ok(false, e.toString()); 1124 } 1125 1126 is(copyTarget.getAttribute("id"), "content", "Copy event's target should be always an element"); 1127 1128 // Create a contenteditable element to check complicated event target. 1129 contenteditableContainer.innerHTML = '<div contenteditable><p id="p1">foo</p><p id="p2">bar</p></div>'; 1130 contenteditableContainer.firstChild.focus(); 1131 1132 let p1 = document.getElementById("p1"); 1133 let p2 = document.getElementById("p2"); 1134 selection.setBaseAndExtent(p1.firstChild, 1, p2.firstChild, 1); 1135 1136 let pasteTarget = null; 1137 let pasteEventCount = 0; 1138 function pasteEventLogger(event) { 1139 pasteTarget = event.target; 1140 pasteEventCount++; 1141 } 1142 addEventListenerTo(document, "paste", pasteEventLogger); 1143 synthesizeKey("v", {accelKey: 1}); 1144 is(pasteTarget.getAttribute("id"), "p1", 1145 "'paste' event's target should be always an element which includes start container of the first Selection range"); 1146 is(pasteEventCount, 1, 1147 "'paste' event should be fired only once when Accel+'v' is pressed"); 1148 }); 1149 1150 add_task(async function test_paste_event_for_middle_click_without_HTMLEditor() { 1151 await SpecialPowers.pushPrefEnv({"set": [["middlemouse.paste", true], 1152 ["middlemouse.contentLoadURL", false]]}); 1153 1154 try { 1155 await reset(); 1156 } catch (e) { 1157 ok(false, `Failed to reset (${e.toString()})`); 1158 return; 1159 } 1160 1161 contenteditableContainer.innerHTML = '<div id="non-editable-target">non-editable</div>'; 1162 let noneditableDiv = document.getElementById("non-editable-target"); 1163 1164 ok(!getHTMLEditor(), "There should not be HTMLEditor"); 1165 1166 let selection = document.getSelection(); 1167 selection.setBaseAndExtent(content.firstChild, 0, 1168 content.firstChild, "CONTENT".length); 1169 1170 try { 1171 await putOnClipboard("CONTENT", () => { 1172 synthesizeKey("c", {accelKey: 1}); 1173 }, "copy text from non-editable element"); 1174 } catch (e) { 1175 ok(false, e.toString()); 1176 return; 1177 } 1178 1179 let auxclickFired = false; 1180 function onAuxClick(event) { 1181 auxclickFired = true; 1182 } 1183 addEventListenerTo(document, "auxclick", onAuxClick); 1184 1185 let pasteEventCount = 0; 1186 function onPaste(event) { 1187 pasteEventCount++; 1188 ok(auxclickFired, "'auxclick' event should be fired before 'paste' event"); 1189 is(event.target, noneditableDiv, 1190 "'paste' event should be fired on the clicked element"); 1191 } 1192 addEventListenerTo(document, "paste", onPaste); 1193 1194 synthesizeMouseAtCenter(noneditableDiv, {button: 1}); 1195 is(pasteEventCount, 1, "'paste' event should be fired just once"); 1196 1197 pasteEventCount = 0; 1198 auxclickFired = false; 1199 addEventListenerTo(document, "mouseup", (event) => { event.preventDefault(); }, {once: true}); 1200 synthesizeMouseAtCenter(noneditableDiv, {button: 1}); 1201 is(pasteEventCount, 1, 1202 "Even if 'mouseup' event is consumed, 'paste' event should be fired"); 1203 1204 pasteEventCount = 0; 1205 auxclickFired = false; 1206 addEventListenerTo(document, "auxclick", (event) => { event.preventDefault(); }, {once: true, capture: true}); 1207 synthesizeMouseAtCenter(noneditableDiv, {button: 1}); 1208 ok(auxclickFired, "'auxclickFired' fired"); 1209 is(pasteEventCount, 0, 1210 "If 'auxclick' event is consumed at capturing phase at the document node, 'paste' event should not be fired"); 1211 1212 pasteEventCount = 0; 1213 auxclickFired = false; 1214 addEventListenerTo(noneditableDiv, "auxclick", (event) => { event.preventDefault(); }, {once: true}); 1215 synthesizeMouseAtCenter(noneditableDiv, {button: 1}); 1216 ok(auxclickFired, "'auxclick' fired"); 1217 is(pasteEventCount, 0, 1218 "If 'auxclick' event listener is added to the click event target, 'paste' event should not be fired"); 1219 1220 pasteEventCount = 0; 1221 auxclickFired = false; 1222 addEventListenerTo(document, "auxclick", (event) => { event.preventDefault(); }, {once: true}); 1223 synthesizeMouseAtCenter(noneditableDiv, {button: 1}); 1224 ok(auxclickFired, "'auxclick' fired"); 1225 is(pasteEventCount, 0, 1226 "If 'auxclick' event is consumed, 'paste' event should be not be fired"); 1227 }); 1228 1229 add_task(function cleaning_up() { 1230 try { 1231 disableNonTestMouseEvents(false); 1232 } finally { 1233 window.close(); 1234 } 1235 }); 1236 </script> 1237 </pre> 1238 </body> 1239 </html>