summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/general/window_clipboard_events.html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /dom/tests/mochitest/general/window_clipboard_events.html
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/tests/mochitest/general/window_clipboard_events.html')
-rw-r--r--dom/tests/mochitest/general/window_clipboard_events.html1239
1 files changed, 1239 insertions, 0 deletions
diff --git a/dom/tests/mochitest/general/window_clipboard_events.html b/dom/tests/mochitest/general/window_clipboard_events.html
new file mode 100644
index 0000000000..ee88cc5fa0
--- /dev/null
+++ b/dom/tests/mochitest/general/window_clipboard_events.html
@@ -0,0 +1,1239 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Clipboard Events</title>
+ <script>
+ var SimpleTest = opener.SimpleTest;
+ var SpecialPowers = opener.SpecialPowers;
+ var ok = opener.ok;
+ var is = opener.is;
+ var isnot = opener.isnot;
+ var todo = opener.todo;
+ var todo_is = opener.todo_is;
+ var add_task = opener.add_task;
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="border: 3px solid black; padding: 3em;">CONTENT TEXT<input id="content-input" value="INPUT TEXT"></div>
+<img id="image" src="image_50.png">
+<button id="button">Button</button>
+
+<div id="syntheticSpot" oncut="compareSynthetic(event, 'cut')"
+ oncopy="compareSynthetic(event, 'copy')"
+ onpaste="compareSynthetic(event, 'paste')">Spot</div>
+
+<div id="contenteditableContainer"></div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var content = document.getElementById("content");
+var contentInput = document.getElementById("content-input");
+var contenteditableContainer = document.getElementById("contenteditableContainer");
+var clipboardInitialValue = "empty";
+
+var cachedCutData, cachedCopyData, cachedPasteData;
+
+ok(SpecialPowers.getBoolPref("dom.events.dataTransfer.protected.enabled"),
+ "The following require dom.events.dataTransfer.protected.enabled is enabled");
+isnot(typeof window.onbeforeinput, "undefined",
+ "The following tests require onbeforeinput attribute");
+
+// Before each test function is run, the clipboard is initialized
+// to clipboardInitialValue, and the contents of div#content are
+// set as the window's selection.
+
+add_task(async function initialize_for_tests() {
+ disableNonTestMouseEvents(true);
+
+ await SimpleTest.promiseFocus(window);
+
+ // Test that clearing and reading the clipboard works. A random number
+ // is used to make sure that leftover clipboard values from a previous
+ // test run don't cause a false-positive test.
+ try {
+ var cb_text = "empty_" + Math.random();
+ await putOnClipboard(cb_text, () => { setClipboardText(cb_text) },
+ "Failed to initial set/get clipboard text");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+var eventListeners = [];
+
+// Note that don't use EventTarget.addEventListener directly in this file
+// because if it's remained in next test, it makes developers harder to
+// investigate such oranges. addEventListenerTo cleans up when `reset()`
+// is called by first of next test and then, all event listeners including
+// marked as "once" are removed automatically.
+function addEventListenerTo(aEventTarget, aEventType, aListener, aOptions) {
+ eventListeners.push({
+ target: aEventTarget,
+ type: aEventType,
+ listener: aListener,
+ options: aOptions
+ });
+ aEventTarget.addEventListener(aEventType, aListener, aOptions);
+}
+
+async function reset() {
+ [content, contentInput, document, document.documentElement].forEach(eventTarget => {
+ ["oncut", "oncopy", "onpaste", "oninput", "onbeforeinput"].forEach(attr => {
+ eventTarget[attr] = null;
+ });
+ });
+ eventListeners.forEach(data => {
+ data.target.removeEventListener(data.type, data.listener, data.options);
+ });
+ eventListeners = [];
+
+ // Init clipboard
+ await putOnClipboard(clipboardInitialValue,
+ () => { setClipboardText(clipboardInitialValue) },
+ "reset clipboard");
+
+ // Reset value of editors.
+ contentInput.value = "INPUT TEXT";
+ contenteditableContainer.innerHTML = "";
+}
+
+function getClipboardText() {
+ return SpecialPowers.getClipboardData("text/plain");
+}
+
+function getHTMLEditor() {
+ let editingSession = SpecialPowers.wrap(window).docShell.editingSession;
+ if (!editingSession) {
+ return null;
+ }
+ let editor = editingSession.getEditorForWindow(window);
+ if (!editor) {
+ return null;
+ }
+ return editor.QueryInterface(SpecialPowers.Ci.nsIHTMLEditor);
+}
+
+async function putOnClipboard(expected, operationFn, desc, type) {
+ try {
+ await SimpleTest.promiseClipboardChange(expected, operationFn, type, 1000);
+ } catch (e) {
+ throw `Failed "${desc}" due to "${e.toString()}"`
+ }
+}
+
+async function wontPutOnClipboard(unexpectedData, operationFn, desc, type) {
+ try {
+ // SimpleTest.promiseClipboardChange() doesn't throw exception when
+ // it unexpectedly succeeds to copy something. Therefore, we need
+ // to throw an exception by ourselves.
+ await SimpleTest.promiseClipboardChange(null, operationFn, type, 300, true, false)
+ .then(aData => {
+ if (aData == unexpectedData) {
+ throw `Failed "${desc}", the clipboard data is modified to "${aData.toString()}"`;
+ }
+ });
+ } catch (e) {
+ throw `Failed "${desc}" due to "${e.toString()}"`
+ }
+}
+
+function setClipboardText(text) {
+ var helper = SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(SpecialPowers.Ci.nsIClipboardHelper);
+ helper.copyString(text);
+}
+
+function selectContentDiv() {
+ // Set selection
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.selectAllChildren(content);
+}
+
+function selectContentInput() {
+ contentInput.focus();
+ contentInput.select();
+}
+
+add_task(async function test_dom_oncopy() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncopy event handler, fire copy. Ensure that the event
+ // handler was called, and the clipboard contents have set to CONTENT TEXT.
+ // Test firing oncopy event on ctrl-c:
+ selectContentDiv();
+
+ var oncopy_fired = false;
+ content.oncopy = function() { oncopy_fired = true; };
+ try {
+ await putOnClipboard("CONTENT TEXT", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "copy on DOM element set clipboard correctly");
+ ok(oncopy_fired, "copy event firing on DOM element");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_dom_oncut() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncut event handler, fire cut. Ensure that the event handler
+ // was called. The <div> doesn't handle a cut, so ensure that the
+ // clipboard text shouldn't be "CONTENT TEXT".
+ selectContentDiv();
+ var oncut_fired = false;
+ content.oncut = function() { oncut_fired = true; };
+ try {
+ await wontPutOnClipboard("CONTENT TEXT", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "cut on DOM element set clipboard correctly");
+ ok(oncut_fired, "cut event firing on DOM element")
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_dom_onpaste() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an onpaste event handler, fire paste. Ensure that the event
+ // handler was called.
+ selectContentDiv();
+ var onpaste_fired = false;
+ content.onpaste = function() { onpaste_fired = true; };
+ synthesizeKey("v", {accelKey: 1});
+ ok(onpaste_fired, "paste event firing on DOM element");
+});
+
+add_task(async function test_dom_oncopy_abort() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncopy event handler that aborts the copy, and fire the copy
+ // event. Ensure that the event handler was fired, and the clipboard
+ // contents have not been modified.
+ selectContentDiv();
+ var oncopy_fired = false;
+ content.oncopy = function() { oncopy_fired = true; return false; };
+ try {
+ await wontPutOnClipboard("CONTENT TEXT", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "aborted copy on DOM element did not modify clipboard");
+ ok(oncopy_fired, "copy event (to-be-cancelled) firing on DOM element");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_oncopy() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncopy event handler, fire copy. Ensure that the event
+ // handler was called, and the clipboard contents have been set to 'PUT TE',
+ // which is the part that is selected below.
+ selectContentInput();
+ contentInput.focus();
+ contentInput.setSelectionRange(2, 8);
+
+ let oncopy_fired = false;
+ let onbeforeinput_fired = false;
+ let oninput_fired = false;
+ contentInput.oncopy = () => { oncopy_fired = true; };
+ contentInput.onbeforeinput = () => { onbeforeinput = true; };
+ contentInput.oninput = () => { oninput_fired = true; };
+ try {
+ await putOnClipboard("PUT TE", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "copy on plaintext editor set clipboard correctly");
+ ok(oncopy_fired, "copy event firing on plaintext editor");
+ ok(!onbeforeinput_fired, "beforeinput event shouldn't be fired on plaintext editor by copy");
+ ok(!oninput_fired, "input event shouldn't be fired on plaintext editor by copy");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_oncut() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncut event handler, and fire cut. Ensure that the event
+ // handler was fired, the clipboard contains the INPUT TEXT, and
+ // that the input itself is empty.
+ selectContentInput();
+ let oncut_fired = false;
+ let beforeInputEvents = [];
+ let inputEvents = [];
+ contentInput.oncut = () => { oncut_fired = true; };
+ contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); }
+ contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); }
+ try {
+ await putOnClipboard("INPUT TEXT", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "cut on plaintext editor set clipboard correctly");
+ ok(oncut_fired, "cut event firing on plaintext editor");
+ is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by cut');
+ if (beforeInputEvents.length) {
+ is(beforeInputEvents[0].inputType, "deleteByCut", '"inputType" of "beforeinput" event should be "deleteByCut"');
+ is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "deleteByCut" should be cancelable');
+ is(beforeInputEvents[0].data, null, '"data" of "beforeinput" event for "deleteByCut" should be null');
+ is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "deleteByCut" should be null');
+ is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "deleteByCut" should return empty array');
+ }
+ is(inputEvents.length, 1, '"input" event should be fired once by cut');
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType, "deleteByCut", '"inputType" of "input" event should be "deleteByCut"');
+ is(inputEvents[0].cancelable, false, '"input" event for "deleteByCut" should not be cancelable');
+ is(inputEvents[0].data, null, '"data" of "input" event for "deleteByCut" should be null');
+ is(inputEvents[0].dataTransfer, null, '"dataTransfer" of "input" event for "deleteByCut" should be null');
+ is(inputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "input" event for "deleteByCut" should return empty array');
+ }
+ is(contentInput.value, "",
+ "cut on plaintext editor emptied editor");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_onpaste() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an onpaste event handler, and fire paste. Ensure that the event
+ // handler was fired, the clipboard contents didn't change, and that the
+ // input value did change (ie. paste succeeded).
+ selectContentInput();
+ let onpaste_fired = false;
+ let beforeInputEvents = [];
+ let inputEvents = [];
+ contentInput.onpaste = () => { onpaste_fired = true; };
+ contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); }
+ contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); }
+
+ synthesizeKey("v", {accelKey: 1});
+ ok(onpaste_fired, "paste event firing on plaintext editor");
+ is(getClipboardText(), clipboardInitialValue,
+ "paste on plaintext editor did not modify clipboard contents");
+ is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by paste');
+ if (beforeInputEvents.length) {
+ is(beforeInputEvents[0].inputType, "insertFromPaste", '"inputType" of "beforeinput" event should be "insertFromPaste"');
+ is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "insertFromPaste" should be cancelable');
+ is(beforeInputEvents[0].data, clipboardInitialValue, `"data" of "beforeinput" event for "insertFromPaste" should be "${clipboardInitialValue}"`);
+ is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "insertFromPaste" should be null');
+ is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "insertFromPaste" should return empty array');
+ }
+ is(inputEvents.length, 1, '"input" event should be fired once by paste');
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType, "insertFromPaste", '"inputType" of "input" event should be "insertFromPaste"');
+ is(inputEvents[0].cancelable, false, '"input" event for "insertFromPaste" should not be cancelable');
+ is(inputEvents[0].data, clipboardInitialValue, `"data" of "input" event for "insertFromPaste" should be "${clipboardInitialValue}"`);
+ is(inputEvents[0].dataTransfer, null, '"dataTransfer" of "input" event for "insertFromPaste" should be null');
+ is(inputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "input" event for "insertFromPaste" should return empty array');
+ }
+ is(contentInput.value, clipboardInitialValue,
+ "paste on plaintext editor did modify editor value");
+});
+
+add_task(async function test_input_oncopy_abort() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncopy event handler, fire copy. Ensure that the event
+ // handler was called, and that the clipboard value did NOT change.
+ selectContentInput();
+ let oncopy_fired = false;
+ contentInput.oncopy = () => { oncopy_fired = true; return false; };
+ contentInput.onbeforeinput = () => {
+ ok(false, '"beforeinput" event should not be fired by copy but canceled');
+ };
+ contentInput.oninput = function() {
+ ok(false, '"input" event should not be fired by copy but canceled');
+ };
+ try {
+ await wontPutOnClipboard("CONTENT TEXT", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "aborted copy on plaintext editor did not modify clipboard");
+ ok(oncopy_fired, "copy event (to-be-cancelled) firing on plaintext editor");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_oncut_abort() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncut event handler, and fire cut. Ensure that the event
+ // handler was fired, the clipboard contains the INPUT TEXT, and
+ // that the input itself is empty.
+ selectContentInput();
+ let oncut_fired = false;
+ contentInput.oncut = () => { oncut_fired = true; return false; };
+ contentInput.onbeforeinput = () => {
+ ok(false, '"beforeinput" event should not be fired by cut but canceled by "cut" event listener');
+ };
+ contentInput.oninput = () => {
+ ok(false, '"input" event should not be fired by cut but canceled by "cut" event listener');
+ };
+ try {
+ await wontPutOnClipboard("CONTENT TEXT", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "aborted cut on plaintext editor did not modify clipboard");
+ ok(oncut_fired, "cut event (to-be-cancelled) firing on plaintext editor");
+ is(contentInput.value, "INPUT TEXT",
+ "aborted cut on plaintext editor did not modify editor contents");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_oncut_beforeinput_abort() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an oncut event handler, and fire cut. Ensure that the event
+ // handler was fired, the clipboard contains the INPUT TEXT, and
+ // that the input itself is empty.
+ selectContentInput();
+ let oncut_fired = false;
+ let beforeInputEvents = [];
+ let inputEvents = [];
+ contentInput.oncut = () => { oncut_fired = true; };
+ contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); aEvent.preventDefault(); }
+ contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); }
+ try {
+ await putOnClipboard("INPUT TEXT", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "cut on plaintext editor set clipboard correctly");
+ ok(oncut_fired, "cut event firing on plaintext editor");
+ is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by cut');
+ if (beforeInputEvents.length) {
+ is(beforeInputEvents[0].inputType, "deleteByCut", '"inputType" of "beforeinput" event should be "deleteByCut"');
+ is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "deleteByCut" should be cancelable');
+ is(beforeInputEvents[0].data, null, '"data" of "beforeinput" event for "deleteByCut" should be null');
+ is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "deleteByCut" should be null');
+ is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "deleteByCut" should return empty array');
+ }
+ is(inputEvents.length, 0, '"input" event should not be fired by cut if "beforeinput" event is canceled');
+ is(contentInput.value, "INPUT TEXT",
+ 'cut on plaintext editor should not change editor since "beforeinput" event was canceled');
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_onpaste_abort() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an onpaste event handler, and fire paste. Ensure that the event
+ // handler was fired, the clipboard contents didn't change, and that the
+ // input value did change (ie. paste succeeded).
+ selectContentInput();
+ let onpaste_fired = false;
+ contentInput.onpaste = () => { onpaste_fired = true; return false; };
+ contentInput.onbeforeinput = () => {
+ ok(false, '"beforeinput" event should not be fired by paste but canceled');
+ };
+ contentInput.oninput = () => {
+ ok(false, '"input" event should not be fired by paste but canceled');
+ };
+ synthesizeKey("v", {accelKey: 1});
+ ok(onpaste_fired,
+ "paste event (to-be-cancelled) firing on plaintext editor");
+ is(getClipboardText(), clipboardInitialValue,
+ "aborted paste on plaintext editor did not modify clipboard");
+ is(contentInput.value, "INPUT TEXT",
+ "aborted paste on plaintext editor did not modify modified editor value");
+});
+
+add_task(async function test_input_onpaste_beforeinput_abort() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Setup an onpaste event handler, and fire paste. Ensure that the event
+ // handler was fired, the clipboard contents didn't change, and that the
+ // input value did change (ie. paste succeeded).
+ selectContentInput();
+ let onpaste_fired = false;
+ let beforeInputEvents = [];
+ let inputEvents = [];
+ contentInput.onpaste = () => { onpaste_fired = true; };
+ contentInput.onbeforeinput = (aEvent) => { beforeInputEvents.push(aEvent); aEvent.preventDefault(); }
+ contentInput.oninput = (aEvent) => { inputEvents.push(aEvent); }
+
+ synthesizeKey("v", {accelKey: 1});
+ ok(onpaste_fired, "paste event firing on plaintext editor");
+ is(getClipboardText(), clipboardInitialValue,
+ "paste on plaintext editor did not modify clipboard contents");
+ is(beforeInputEvents.length, 1, '"beforeinput" event should be fired once by paste');
+ if (beforeInputEvents.length) {
+ is(beforeInputEvents[0].inputType, "insertFromPaste", '"inputType" of "beforeinput" event should be "insertFromPaste"');
+ is(beforeInputEvents[0].cancelable, true, '"beforeinput" event for "insertFromPaste" should be cancelable');
+ is(beforeInputEvents[0].data, clipboardInitialValue, `"data" of "beforeinput" event for "insertFromPaste" should be "${clipboardInitialValue}"`);
+ is(beforeInputEvents[0].dataTransfer, null, '"dataTransfer" of "beforeinput" event for "insertFromPaste" should be null');
+ is(beforeInputEvents[0].getTargetRanges().length, 0, 'getTargetRanges() of "beforeinput" event for "insertFromPaste" should return empty array');
+ }
+ is(inputEvents.length, 0, '"input" event should not be fired by paste when "beforeinput" is canceled');
+ is(contentInput.value, "INPUT TEXT",
+ "paste on plaintext editor did modify editor value");
+});
+
+add_task(async function test_input_cut_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Cut using event.dataTransfer. The event is not cancelled so the default
+ // cut should occur
+ selectContentInput();
+ contentInput.oncut = function(event) {
+ ok(event instanceof ClipboardEvent, "cut event is a ClipboardEvent");
+ ok(event.clipboardData instanceof DataTransfer, "cut event dataTransfer is a DataTransfer");
+ is(event.target, contentInput, "cut event target");
+ is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 0, "cut event mozItemCount");
+ is(event.clipboardData.getData("text/plain"), "", "cut event getData");
+ event.clipboardData.setData("text/plain", "This is some dataTransfer text");
+ cachedCutData = event.clipboardData;
+ };
+ try {
+ await putOnClipboard("INPUT TEXT", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "cut using dataTransfer on plaintext editor set clipboard correctly");
+ is(contentInput.value, "",
+ "cut using dataTransfer on plaintext editor cleared input");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_cut_abort_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Cut using event.dataTransfer but cancel the event. The data should be
+ // put on the clipboard but since we don't modify the input value, the input
+ // should have the same value.
+ selectContentInput();
+ contentInput.oncut = function(event) {
+ event.clipboardData.setData("text/plain", "Cut dataTransfer text");
+ return false;
+ };
+ try {
+ await putOnClipboard("Cut dataTransfer text", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "aborted cut using dataTransfer on plaintext editor set clipboard correctly");
+ is(contentInput.value, "INPUT TEXT",
+ "aborted cut using dataTransfer on plaintext editor did not modify input");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_copy_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Copy using event.dataTransfer
+ selectContentInput();
+ contentInput.oncopy = function(event) {
+ ok(event instanceof ClipboardEvent, "copy event is a ClipboardEvent");
+ ok(event.clipboardData instanceof DataTransfer, "copy event dataTransfer is a DataTransfer");
+ is(event.target, contentInput, "copy event target");
+ is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 0, "copy event mozItemCount");
+ is(event.clipboardData.getData("text/plain"), "", "copy event getData");
+ event.clipboardData.setData("text/plain", "Copied dataTransfer text");
+ cachedCopyData = event.clipboardData;
+ };
+ try {
+ await putOnClipboard("INPUT TEXT", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "copy using dataTransfer on plaintext editor set clipboard correctly");
+ is(contentInput.value, "INPUT TEXT",
+ "copy using dataTransfer on plaintext editor did not modify input");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_copy_abort_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Copy using event.dataTransfer but cancel the event.
+ selectContentInput();
+ contentInput.oncopy = function(event) {
+ event.clipboardData.setData("text/plain", "Copy dataTransfer text");
+ return false;
+ };
+ try {
+ await putOnClipboard("Copy dataTransfer text", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "aborted copy using dataTransfer on plaintext editor set clipboard correctly");
+ is(contentInput.value, "INPUT TEXT",
+ "aborted copy using dataTransfer on plaintext editor did not modify input");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_input_paste_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Paste using event.dataTransfer
+ selectContentInput();
+ contentInput.onpaste = function(event) {
+ ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent");
+ ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer");
+ is(event.target, contentInput, "paste event target");
+ is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 1, "paste event mozItemCount");
+ is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "paste event getData");
+ cachedPasteData = event.clipboardData;
+ };
+ synthesizeKey("v", {accelKey: 1});
+ is(getClipboardText(), clipboardInitialValue,
+ "paste using dataTransfer on plaintext editor did not modify clipboard contents");
+ is(contentInput.value, clipboardInitialValue,
+ "paste using dataTransfer on plaintext editor modified input");
+});
+
+add_task(async function test_input_paste_abort_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Paste using event.dataTransfer but cancel the event
+ selectContentInput();
+ contentInput.onpaste = function(event) {
+ is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "get data on aborted paste");
+ contentInput.value = "Alternate Paste";
+ return false;
+ };
+ synthesizeKey("v", {accelKey: 1});
+ is(getClipboardText(), clipboardInitialValue,
+ "aborted paste using dataTransfer on plaintext editor did not modify clipboard contents");
+ is(contentInput.value, "Alternate Paste",
+ "aborted paste using dataTransfer on plaintext editor modified input");
+});
+
+add_task(async function test_input_copypaste_dataTransfer_multiple() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Cut several types of data and paste it again
+ contentInput.value = "This is a line of text";
+ contentInput.oncopy = function(event) {
+ var cd = event.clipboardData;
+ cd.setData("text/plain", "would be a phrase");
+
+ var exh = false;
+ try { SpecialPowers.wrap(cd).mozSetDataAt("text/plain", "Text", 1); } catch (ex) { exh = true; }
+ ok(exh, "exception occured mozSetDataAt 1");
+ exh = false;
+ try { SpecialPowers.wrap(cd).mozTypesAt(1); } catch (ex) { exh = true; }
+ ok(exh, "exception occured mozTypesAt 1");
+ exh = false;
+ try { SpecialPowers.wrap(cd).mozGetDataAt("text/plain", 1); } catch (ex) { exh = true; }
+ ok(exh, "exception occured mozGetDataAt 1");
+ exh = false;
+ try { cd.mozClearDataAt("text/plain", 1); } catch (ex) { exh = true; }
+ ok(exh, "exception occured mozClearDataAt 1");
+
+ cd.setData("text/x-moz-url", "http://www.mozilla.org");
+ SpecialPowers.wrap(cd).mozSetDataAt("text/x-custom", "Custom Text with \u0000 null", 0);
+ is(SpecialPowers.wrap(cd).mozItemCount, 1, "mozItemCount after set multiple types");
+ return false;
+ };
+
+ try {
+ selectContentInput();
+
+ await putOnClipboard("would be a phrase", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "copy multiple types text");
+ contentInput.oncopy = null; // XXX Not sure why this is required...
+ } catch (e) {
+ ok(false, e.toString());
+ return;
+ }
+
+ contentInput.setSelectionRange(5, 14);
+
+ contentInput.onpaste = function(event) {
+ var cd = event.clipboardData;
+ is(SpecialPowers.wrap(cd).mozItemCount, 1, "paste after copy multiple types mozItemCount");
+ is(cd.getData("text/plain"), "would be a phrase", "paste text/plain multiple types");
+
+ // Firefox for Android's clipboard code doesn't handle x-moz-url. Therefore
+ // disabling the following test. Enable this once bug #840101 is fixed.
+ if (!navigator.appVersion.includes("Android")) {
+ is(cd.getData("text/x-moz-url"), "http://www.mozilla.org", "paste text/x-moz-url multiple types");
+ is(cd.getData("text/x-custom"), "Custom Text with \u0000 null", "paste text/custom multiple types");
+ } else {
+ is(cd.getData("text/x-custom"), "", "paste text/custom multiple types");
+ }
+
+ is(cd.getData("application/x-moz-custom-clipdata"), "", "application/x-moz-custom-clipdata is not present");
+
+ exh = false;
+ try { cd.setData("application/x-moz-custom-clipdata", "Some Data"); } catch (ex) { exh = true; }
+ ok(exh, "exception occured setData with application/x-moz-custom-clipdata");
+
+ exh = false;
+ try { cd.setData("text/plain", "Text on Paste"); } catch (ex) { exh = true; }
+ ok(exh, "exception occured setData on paste");
+
+ is(cd.getData("text/plain"), "would be a phrase", "text/plain data unchanged");
+ };
+ synthesizeKey("v", {accelKey: 1});
+ is(contentInput.value, "This would be a phrase of text",
+ "default paste after copy multiple types");
+});
+
+add_task(async function test_input_copy_button_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Copy using event.dataTransfer when a button is focused.
+ var button = document.getElementById("button");
+ button.focus();
+ button.oncopy = function(event) {
+ ok(false, "should not be firing copy event on button");
+ return false;
+ };
+ try {
+ // copy should not occur here because buttons don't have any controller
+ // for the copy command
+ await wontPutOnClipboard("", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "Accel-C on the `<button>` shouldn't modify the clipboard data");
+ ok(true, "Accel-C on the <button> shouldn't modify the clipboard data");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+
+ try {
+ selectContentDiv();
+
+ await putOnClipboard("CONTENT TEXT", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "Accel-C with selecting the content <div> should modify the clipboard data with text in it");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+});
+
+add_task(async function test_eventspref_disabled() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Disable clipboard events
+ try {
+ await SpecialPowers.pushPrefEnv({
+ set: [['dom.event.clipboardevents.enabled', false]]
+ });
+
+ var event_fired = false;
+ var input_data = undefined;
+ contentInput.oncut = function() { event_fired = true; };
+ contentInput.oncopy = function() { event_fired = true; };
+ contentInput.onpaste = function() { event_fired = true; };
+ contentInput.oninput = function(event) { input_data = event.data; };
+
+ selectContentInput();
+ contentInput.setSelectionRange(1, 4);
+
+ await putOnClipboard("NPU", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "cut changed clipboard when preference is disabled");
+ is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
+ ok(!event_fired, "cut event did not fire when preference is disabled");
+ is(input_data, null, "cut should cause input event whose data value is null");
+
+ event_fired = false;
+ input_data = undefined;
+ contentInput.setSelectionRange(3, 6);
+ await putOnClipboard("TEX", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "copy changed clipboard when preference is disabled");
+ ok(!event_fired, "copy event did not fire when preference is disabled")
+ is(input_data, undefined, "copy shouldn't cause input event");
+
+ event_fired = false;
+ contentInput.setSelectionRange(0, 2);
+ synthesizeKey("v", {accelKey: 1});
+ is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
+ ok(!event_fired, "paste event did not fire when preference is disabled");
+ is(input_data, "",
+ "paste should cause input event but whose data value should be empty string if clipboard event is disabled");
+ } catch (e) {
+ ok(false, e.toString());
+ } finally {
+ await SpecialPowers.popPrefEnv();
+ }
+});
+
+let expectedData = [];
+
+// Check to make that synthetic events do not change the clipboard
+add_task(async function test_synthetic_events() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ let syntheticSpot = document.getElementById("syntheticSpot");
+
+ // No dataType specified
+ let event = new ClipboardEvent("cut", { data: "something" });
+ expectedData = { type: "cut", data: null }
+ compareSynthetic(event, "before");
+ syntheticSpot.dispatchEvent(event);
+ ok(expectedData.eventFired, "cut event fired");
+ compareSynthetic(event, "after");
+
+ event = new ClipboardEvent("cut", { dataType: "text/plain", data: "something" });
+ expectedData = { type: "cut", dataType: "text/plain", data: "something" }
+ compareSynthetic(event, "before");
+ syntheticSpot.dispatchEvent(event);
+ ok(expectedData.eventFired, "cut event fired");
+ compareSynthetic(event, "after");
+
+ event = new ClipboardEvent("copy", { dataType: "text/plain", data: "something" });
+ expectedData = { type: "copy", dataType: "text/plain", data: "something" }
+ compareSynthetic(event, "before");
+ syntheticSpot.dispatchEvent(event);
+ ok(expectedData.eventFired, "copy event fired");
+ compareSynthetic(event, "after");
+
+ event = new ClipboardEvent("copy", { dataType: "text/plain" });
+ expectedData = { type: "copy", dataType: "text/plain", data: "" }
+ compareSynthetic(event, "before");
+ syntheticSpot.dispatchEvent(event);
+ ok(expectedData.eventFired, "copy event fired");
+ compareSynthetic(event, "after");
+
+ event = new ClipboardEvent("paste", { dataType: "text/plain", data: "something" });
+ expectedData = { type: "paste", dataType: "text/plain", data: "something" }
+ compareSynthetic(event, "before");
+ syntheticSpot.dispatchEvent(event);
+ ok(expectedData.eventFired, "paste event fired");
+ compareSynthetic(event, "after");
+
+ event = new ClipboardEvent("paste", { dataType: "application/unknown", data: "unknown" });
+ expectedData = { type: "paste", dataType: "application/unknown", data: "unknown" }
+ compareSynthetic(event, "before");
+ syntheticSpot.dispatchEvent(event);
+ ok(expectedData.eventFired, "paste event fired");
+ compareSynthetic(event, "after");
+});
+
+function compareSynthetic(event, eventtype) {
+ let step = (eventtype == "cut" || eventtype == "copy" || eventtype == "paste") ? "during" : eventtype;
+ if (step == "during") {
+ is(eventtype, expectedData.type, "synthetic " + eventtype + " event fired");
+ }
+
+ ok(event.clipboardData instanceof DataTransfer, "clipboardData is assigned");
+
+ is(event.type, expectedData.type, "synthetic " + eventtype + " event type");
+ if (expectedData.data === null) {
+ is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 0, "synthetic " + eventtype + " empty data");
+ }
+ else {
+ is(SpecialPowers.wrap(event.clipboardData).mozItemCount, 1, "synthetic " + eventtype + " item count");
+ is(event.clipboardData.types.length, 1, "synthetic " + eventtype + " types length");
+ is(event.clipboardData.getData(expectedData.dataType), expectedData.data,
+ "synthetic " + eventtype + " data");
+ }
+
+ is(getClipboardText(), "empty", "event does not change the clipboard " + step + " dispatch");
+
+ if (step == "during") {
+ expectedData.eventFired = true;
+ }
+}
+
+async function checkCachedDataTransfer(cd, eventtype) {
+ var testprefix = "cached " + eventtype + " dataTransfer";
+
+ try {
+ await putOnClipboard("Some Clipboard Text", () => { setClipboardText("Some Clipboard Text") },
+ "change clipboard outside of event");
+ } catch (e) {
+ ok(false, e.toString());
+ return;
+ }
+
+ var oldtext = cd.getData("text/plain");
+ ok(!oldtext, "clipboard get using " + testprefix);
+
+ try {
+ SpecialPowers.wrap(cd).mozSetDataAt("text/plain", "Test Cache Data", 0);
+ } catch (ex) {}
+ ok(!cd.getData("text/plain"), "clipboard set using " + testprefix);
+
+ is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix);
+
+ try {
+ cd.mozClearDataAt("text/plain", 0);
+ } catch (ex) {}
+ ok(!cd.getData("text/plain"), "clipboard clear using " + testprefix);
+
+ is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix);
+}
+
+add_task(async function test_modify_datatransfer_outofevent() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Check if the cached clipboard data can be accessed or modified
+ // and whether it modifies the real clipboard
+
+ // XXX Depends on test_input_cut_dataTransfer()
+ if (cachedCutData) {
+ await checkCachedDataTransfer(cachedCutData, "cut");
+ } else {
+ todo(false, "test_input_cut_dataTransfer must have been failed, skipping tests with its dataTransfer");
+ }
+ // XXX Depends on test_input_copy_dataTransfer()
+ if (cachedCopyData) {
+ await checkCachedDataTransfer(cachedCopyData, "copy");
+ } else {
+ todo(false, "test_input_copy_dataTransfer must have been failed, skipping tests with its dataTransfer");
+ }
+ // XXX Depends on test_input_paste_dataTransfer()
+ if (cachedPasteData) {
+ await checkCachedDataTransfer(cachedPasteData, "paste");
+ } else {
+ todo(false, "test_input_paste_dataTransfer must have been failed, skipping tests with its dataTransfer");
+ }
+});
+
+add_task(async function test_input_cut_disallowed_types_dataTransfer() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ selectContentInput();
+ let oncutExecuted = false;
+ contentInput.oncut = function(event) {
+ // Setting an arbitrary type should be OK
+ try {
+ event.clipboardData.setData("apple/cider", "Anything your heart desires");
+ ok(true, "We should have successfully executed the setData call");
+ } catch(e) {
+ ok(false, "We should not have gotten an exception for trying to set that data");
+ }
+
+ // Unless that type happens to be application/x-moz-custom-clipdata
+ try {
+ event.clipboardData.setData("application/x-moz-custom-clipdata", "Anything your heart desires");
+ ok(false, "We should not have successfully executed the setData call");
+ } catch(e) {
+ is(e.name, "NotSupportedError",
+ "We should have gotten an NotSupportedError exception for trying to set that data");
+ }
+ oncutExecuted = true;
+ };
+
+ try {
+ await putOnClipboard("INPUT TEXT", () => {
+ synthesizeKey("x", {accelKey: 1});
+ }, "The oncut handler should have been executed data");
+ ok(oncutExecuted, "The oncut handler should have been executed");
+ } catch (e) {
+ ok(false, "Failed to copy the data given by the oncut");
+ }
+});
+
+// Try copying an image to the clipboard and make sure that it looks correct when pasting it.
+add_task(async function test_image_dataTransfer() {
+ // cmd_copyImageContents errors on Android (bug 1299578).
+ if (navigator.userAgent.includes("Android")) {
+ return;
+ }
+
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ // Copy the image's data to the clipboard
+ try {
+ await putOnClipboard("", () => {
+ SpecialPowers.setCommandNode(window, document.getElementById("image"));
+ SpecialPowers.doCommand(window, "cmd_copyImageContents");
+ }, "copy changed clipboard when preference is disabled");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+
+ let onpasteCalled = false;
+ document.onpaste = function(event) {
+ ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent");
+ ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer");
+ let items = event.clipboardData.items;
+ let foundData = false;
+ for (let i = 0; i < items.length; ++i) {
+ if (items[i].kind == "file") {
+ foundData = true;
+ is(items[i].type, "image/png", "The type of the data must be image/png");
+ is(items[i].getAsFile().type, "image/png", "The attached file must be image/png");
+ }
+ }
+ ok(foundData, "Should have found a file entry in the DataTransferItemList");
+ let files = event.clipboardData.files;
+ is(files.length, 1, "There should only be one file on the DataTransfer");
+ is(files[0].type, "image/png", "The only file should be an image/png");
+ onpasteCalled = true;
+ }
+
+ synthesizeKey("v", {accelKey: 1});
+ ok(onpasteCalled, "The paste event listener must have been called");
+});
+
+add_task(async function test_event_target() {
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ let copyTarget = null;
+ addEventListenerTo(document, "copy", (event) => { copyTarget = event.target; }, {once: true});
+
+ if (document.activeElement) {
+ document.activeElement.blur();
+ }
+
+ let selection = document.getSelection();
+ selection.setBaseAndExtent(content.firstChild, "CONTENT ".length,
+ content.firstChild, "CONTENT TEXT".length);
+
+ try {
+ await putOnClipboard("TEXT", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "copy text from non-editable element");
+ } catch (e) {
+ ok(false, e.toString());
+ }
+
+ is(copyTarget.getAttribute("id"), "content", "Copy event's target should be always an element");
+
+ // Create a contenteditable element to check complicated event target.
+ contenteditableContainer.innerHTML = '<div contenteditable><p id="p1">foo</p><p id="p2">bar</p></div>';
+ contenteditableContainer.firstChild.focus();
+
+ let p1 = document.getElementById("p1");
+ let p2 = document.getElementById("p2");
+ selection.setBaseAndExtent(p1.firstChild, 1, p2.firstChild, 1);
+
+ let pasteTarget = null;
+ let pasteEventCount = 0;
+ function pasteEventLogger(event) {
+ pasteTarget = event.target;
+ pasteEventCount++;
+ }
+ addEventListenerTo(document, "paste", pasteEventLogger);
+ synthesizeKey("v", {accelKey: 1});
+ is(pasteTarget.getAttribute("id"), "p1",
+ "'paste' event's target should be always an element which includes start container of the first Selection range");
+ is(pasteEventCount, 1,
+ "'paste' event should be fired only once when Accel+'v' is pressed");
+});
+
+add_task(async function test_paste_event_for_middle_click_without_HTMLEditor() {
+ await SpecialPowers.pushPrefEnv({"set": [["middlemouse.paste", true],
+ ["middlemouse.contentLoadURL", false]]});
+
+ try {
+ await reset();
+ } catch (e) {
+ ok(false, `Failed to reset (${e.toString()})`);
+ return;
+ }
+
+ contenteditableContainer.innerHTML = '<div id="non-editable-target">non-editable</div>';
+ let noneditableDiv = document.getElementById("non-editable-target");
+
+ ok(!getHTMLEditor(), "There should not be HTMLEditor");
+
+ let selection = document.getSelection();
+ selection.setBaseAndExtent(content.firstChild, 0,
+ content.firstChild, "CONTENT".length);
+
+ try {
+ await putOnClipboard("CONTENT", () => {
+ synthesizeKey("c", {accelKey: 1});
+ }, "copy text from non-editable element");
+ } catch (e) {
+ ok(false, e.toString());
+ return;
+ }
+
+ let auxclickFired = false;
+ function onAuxClick(event) {
+ auxclickFired = true;
+ }
+ addEventListenerTo(document, "auxclick", onAuxClick);
+
+ let pasteEventCount = 0;
+ function onPaste(event) {
+ pasteEventCount++;
+ ok(auxclickFired, "'auxclick' event should be fired before 'paste' event");
+ is(event.target, noneditableDiv,
+ "'paste' event should be fired on the clicked element");
+ }
+ addEventListenerTo(document, "paste", onPaste);
+
+ synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+ is(pasteEventCount, 1, "'paste' event should be fired just once");
+
+ pasteEventCount = 0;
+ auxclickFired = false;
+ addEventListenerTo(document, "mouseup", (event) => { event.preventDefault(); }, {once: true});
+ synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+ is(pasteEventCount, 1,
+ "Even if 'mouseup' event is consumed, 'paste' event should be fired");
+
+ pasteEventCount = 0;
+ auxclickFired = false;
+ addEventListenerTo(document, "auxclick", (event) => { event.preventDefault(); }, {once: true, capture: true});
+ synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+ ok(auxclickFired, "'auxclickFired' fired");
+ is(pasteEventCount, 0,
+ "If 'auxclick' event is consumed at capturing phase at the document node, 'paste' event should not be fired");
+
+ pasteEventCount = 0;
+ auxclickFired = false;
+ addEventListenerTo(noneditableDiv, "auxclick", (event) => { event.preventDefault(); }, {once: true});
+ synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+ ok(auxclickFired, "'auxclick' fired");
+ is(pasteEventCount, 0,
+ "If 'auxclick' event listener is added to the click event target, 'paste' event should not be fired");
+
+ pasteEventCount = 0;
+ auxclickFired = false;
+ addEventListenerTo(document, "auxclick", (event) => { event.preventDefault(); }, {once: true});
+ synthesizeMouseAtCenter(noneditableDiv, {button: 1});
+ ok(auxclickFired, "'auxclick' fired");
+ is(pasteEventCount, 0,
+ "If 'auxclick' event is consumed, 'paste' event should be not be fired");
+});
+
+add_task(function cleaning_up() {
+ try {
+ disableNonTestMouseEvents(false);
+ } finally {
+ window.close();
+ }
+});
+</script>
+</pre>
+</body>
+</html>