From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- remote/cdp/test/browser/input/browser.toml | 31 ++++ .../test/browser/input/browser_dispatchKeyEvent.js | 169 +++++++++++++++++++++ .../input/browser_dispatchKeyEvent_events.js | 77 ++++++++++ .../browser/input/browser_dispatchKeyEvent_race.js | 92 +++++++++++ .../browser/input/browser_dispatchMouseEvent.js | 136 +++++++++++++++++ .../browser/input/doc_dispatchKeyEvent_race.html | 28 ++++ remote/cdp/test/browser/input/doc_events.html | 148 ++++++++++++++++++ remote/cdp/test/browser/input/head.js | 150 ++++++++++++++++++ 8 files changed, 831 insertions(+) create mode 100644 remote/cdp/test/browser/input/browser.toml create mode 100644 remote/cdp/test/browser/input/browser_dispatchKeyEvent.js create mode 100644 remote/cdp/test/browser/input/browser_dispatchKeyEvent_events.js create mode 100644 remote/cdp/test/browser/input/browser_dispatchKeyEvent_race.js create mode 100644 remote/cdp/test/browser/input/browser_dispatchMouseEvent.js create mode 100644 remote/cdp/test/browser/input/doc_dispatchKeyEvent_race.html create mode 100644 remote/cdp/test/browser/input/doc_events.html create mode 100644 remote/cdp/test/browser/input/head.js (limited to 'remote/cdp/test/browser/input') diff --git a/remote/cdp/test/browser/input/browser.toml b/remote/cdp/test/browser/input/browser.toml new file mode 100644 index 0000000000..b103734fc0 --- /dev/null +++ b/remote/cdp/test/browser/input/browser.toml @@ -0,0 +1,31 @@ +[DEFAULT] +tags = "cdp" +subsuite = "remote" +args = [ + "--remote-debugging-port", + "--remote-allow-origins=null", +] +prefs = [ # Bug 1600054: Make CDP Fission compatible + "fission.bfcacheInParent=false", + "fission.webContentIsolationStrategy=0", +] +skip-if = [ + "display == 'wayland'" # Bug 1861933: Timestamp unreliable due to worker setup +] +support-files = [ + "!/remote/cdp/test/browser/chrome-remote-interface.js", + "!/remote/cdp/test/browser/head.js", + "head.js", + "doc_events.html", + "doc_dispatchKeyEvent_race.html", +] + +["browser_dispatchKeyEvent.js"] + +["browser_dispatchKeyEvent_events.js"] +https_first_disabled = true + +["browser_dispatchKeyEvent_race.js"] +https_first_disabled = true + +["browser_dispatchMouseEvent.js"] diff --git a/remote/cdp/test/browser/input/browser_dispatchKeyEvent.js b/remote/cdp/test/browser/input/browser_dispatchKeyEvent.js new file mode 100644 index 0000000000..e2b214503b --- /dev/null +++ b/remote/cdp/test/browser/input/browser_dispatchKeyEvent.js @@ -0,0 +1,169 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function testTypingPrintableCharacters({ client }) { + await setupForInput(toDataURL("")); + const { Input } = client; + + info("Write 'h'"); + await sendTextKey(Input, "h"); + await checkInputContent("h", 1); + + info("Write 'H'"); + await sendTextKey(Input, "H"); + await checkInputContent("hH", 2); + + info("Send char type event for char [’]"); + await Input.dispatchKeyEvent({ + type: "char", + modifiers: 0, + key: "’", + }); + await checkInputContent("hH’", 3); +}); + +add_task(async function testArrowKeys({ client }) { + await setupForInput(toDataURL("")); + const { Input } = client; + + await sendText(Input, "hH’"); + info("Send Left"); + await sendRawKey(Input, "ArrowLeft"); + await checkInputContent("hH’", 2); + + info("Write 'a'"); + await sendTextKey(Input, "a"); + await checkInputContent("hHa’", 3); + + info("Send Left"); + await sendRawKey(Input, "ArrowLeft"); + await checkInputContent("hHa’", 2); + + info("Send Left"); + await sendRawKey(Input, "ArrowLeft"); + await checkInputContent("hHa’", 1); + + info("Write 'a'"); + await sendTextKey(Input, "a"); + await checkInputContent("haHa’", 2); + + info("Send ALT/CONTROL + Right"); + const modCode = AppInfo.isMac ? alt : ctrl; + const modKey = AppInfo.isMac ? "Alt" : "Control"; + await dispatchKeyEvent(Input, modKey, "rawKeyDown", modCode); + await dispatchKeyEvent(Input, "ArrowRight", "rawKeyDown", modCode); + await dispatchKeyEvent(Input, "ArrowRight", "keyUp"); + await dispatchKeyEvent(Input, modKey, "keyUp"); + await checkInputContent("haHa’", 5); +}); + +add_task(async function testBackspace({ client }) { + await setupForInput(toDataURL("")); + const { Input } = client; + + await sendText(Input, "haHa’"); + + info("Delete every character in the input"); + await checkBackspace(Input, "haHa"); + await checkBackspace(Input, "haH"); + await checkBackspace(Input, "ha"); + await checkBackspace(Input, "h"); + await checkBackspace(Input, ""); +}); + +add_task(async function testShiftSelect({ client }) { + await setupForInput(toDataURL("")); + const { Input } = client; + await resetInput("word 2 word3"); + + info("Send Shift + Left (select one char to the left)"); + await dispatchKeyEvent(Input, "Shift", "rawKeyDown", shift); + await sendRawKey(Input, "ArrowLeft", shift); + await sendRawKey(Input, "ArrowLeft", shift); + await sendRawKey(Input, "ArrowLeft", shift); + info("(deleteContentBackward)"); + await checkBackspace(Input, "word 2 wo"); + await dispatchKeyEvent(Input, "Shift", "keyUp"); + + await resetInput("word 2 wo"); + info("Send Shift + Left (select one char to the left)"); + await dispatchKeyEvent(Input, "Shift", "rawKeyDown", shift); + await sendRawKey(Input, "ArrowLeft", shift); + await sendRawKey(Input, "ArrowLeft", shift); + await sendTextKey(Input, "H"); + await checkInputContent("word 2 H", 8); + await dispatchKeyEvent(Input, "Shift", "keyUp"); +}); + +add_task(async function testSelectWord({ client }) { + await setupForInput(toDataURL("")); + const { Input } = client; + await resetInput("word 2 word3"); + + info("Send Shift + Ctrl/Alt + Left (select one word to the left)"); + const { primary, primaryKey } = keyForPlatform(); + const combined = shift | primary; + await dispatchKeyEvent(Input, "Shift", "rawKeyDown", shift); + await dispatchKeyEvent(Input, primaryKey, "rawKeyDown", combined); + await sendRawKey(Input, "ArrowLeft", combined); + await sendRawKey(Input, "ArrowLeft", combined); + await dispatchKeyEvent(Input, "Shift", "keyUp", primary); + await dispatchKeyEvent(Input, primaryKey, "keyUp"); + info("(deleteContentBackward)"); + await checkBackspace(Input, "word "); +}); + +add_task(async function testSelectDelete({ client }) { + await setupForInput(toDataURL("")); + const { Input } = client; + await resetInput("word 2 word3"); + + info("Send Ctrl/Alt + Backspace (deleteWordBackward)"); + const { primary, primaryKey } = keyForPlatform(); + await dispatchKeyEvent(Input, primaryKey, "rawKeyDown", primary); + await checkBackspace(Input, "word 2 ", primary); + await dispatchKeyEvent(Input, primaryKey, "keyUp"); + + await resetInput("word 2 "); + await sendText(Input, "word4"); + await sendRawKey(Input, "ArrowLeft"); + await sendRawKey(Input, "ArrowLeft"); + await checkInputContent("word 2 word4", 10); + + if (AppInfo.isMac) { + info("Send Meta + Backspace (deleteSoftLineBackward)"); + await dispatchKeyEvent(Input, "Meta", "rawKeyDown", meta); + await sendRawKey(Input, "Backspace", meta); + await dispatchKeyEvent(Input, "Meta", "keyUp"); + await checkInputContent("d4", 0); + } +}); + +add_task(async function testCtrlShiftArrows({ client }) { + await loadURL( + toDataURL('') + ); + const { Input } = client; + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const select = content.document.querySelector("select"); + select.selectedIndex = 0; + select.focus(); + }); + + const combined = shift | ctrl; + await dispatchKeyEvent(Input, "Control", "rawKeyDown", shift); + await dispatchKeyEvent(Input, "Shift", "rawKeyDown", combined); + await sendRawKey(Input, "ArrowDown", combined); + await dispatchKeyEvent(Input, "Control", "keyUp", shift); + await dispatchKeyEvent(Input, "Shift", "keyUp"); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const select = content.document.querySelector("select"); + ok(select[0].selected, "First option should be selected"); + ok(select[1].selected, "Second option should be selected"); + ok(!select[2].selected, "Third option should not be selected"); + }); +}); diff --git a/remote/cdp/test/browser/input/browser_dispatchKeyEvent_events.js b/remote/cdp/test/browser/input/browser_dispatchKeyEvent_events.js new file mode 100644 index 0000000000..195043019e --- /dev/null +++ b/remote/cdp/test/browser/input/browser_dispatchKeyEvent_events.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const PAGE_URL = + "https://example.com/browser/remote/cdp/test/browser/input/doc_events.html"; + +add_task(async function testShiftEvents({ client }) { + await setupForInput(PAGE_URL); + const { Input } = client; + await resetEvents(); + + await withModifier(Input, "Shift", "shift", "A"); + await checkInputContent("A", 1); + let events = await getEvents(); + checkEvent(events[0], "keydown", "Shift", "shift", true); + checkEvent(events[1], "keydown", "A", "shift", true); + checkEvent(events[2], "keypress", "A", "shift", true); + checkProperties({ data: "A", inputType: "insertText" }, events[3]); + checkEvent(events[4], "keyup", "A", "shift", true); + checkEvent(events[5], "keyup", "Shift", "shift", false); + await resetEvents(); + + await withModifier(Input, "Shift", "shift", "Enter"); + events = await getEvents(); + checkEvent(events[2], "keypress", "Enter", "shift", true); + await resetEvents(); + + await withModifier(Input, "Shift", "shift", "Tab"); + events = await getEvents(); + checkEvent(events[1], "keydown", "Tab", "shift", true); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const input = content.document.querySelector("input"); + isnot(input, content.document.activeElement, "input should lose focus"); + }); +}); + +add_task(async function testAltEvents({ client }) { + await setupForInput(PAGE_URL); + const { Input } = client; + + await withModifier(Input, "Alt", "alt", "a"); + if (AppInfo.isMac) { + await checkInputContent("a", 1); + } else { + await checkInputContent("", 0); + } + let events = await getEvents(); + checkEvent(events[1], "keydown", "a", "alt", true); + checkEvent(events[events.length - 1], "keyup", "Alt", "alt", false); +}); + +add_task(async function testControlEvents({ client }) { + await setupForInput(PAGE_URL); + const { Input } = client; + + await withModifier(Input, "Control", "ctrl", "`"); + let events = await getEvents(); + // no keypress or input event + checkEvent(events[1], "keydown", "`", "ctrl", true); + checkEvent(events[events.length - 1], "keyup", "Control", "ctrl", false); +}); + +add_task(async function testMetaEvents({ client }) { + if (!AppInfo.isMac) { + return; + } + await setupForInput(PAGE_URL); + const { Input } = client; + + await withModifier(Input, "Meta", "meta", "a"); + let events = await getEvents(); + // no keypress or input event + checkEvent(events[1], "keydown", "a", "meta", true); + checkEvent(events[events.length - 1], "keyup", "Meta", "meta", false); +}); diff --git a/remote/cdp/test/browser/input/browser_dispatchKeyEvent_race.js b/remote/cdp/test/browser/input/browser_dispatchKeyEvent_race.js new file mode 100644 index 0000000000..ee1cd5be39 --- /dev/null +++ b/remote/cdp/test/browser/input/browser_dispatchKeyEvent_race.js @@ -0,0 +1,92 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Here we test that the `dispatchKeyEvent` API resolves after all the synchronous event +// handlers from the content page have been flushed. +// +// Say the content page has an event handler such as: +// +// el.addEventListener("keyup", () => { +// doSomeVeryLongProcessing(); // <- takes a long time but is synchronous! +// window.myVariable = "newValue"; +// }); +// +// And imagine this is tested via: +// +// await Input.dispatchKeyEvent(...); +// const myVariable = await Runtime.evaluate({ expression: "window.myVariable" }); +// equals(myVariable, "newValue"); +// +// In order for this to work, we need to be sure that `await Input.dispatchKeyEvent` +// resolves only after the content page flushed the event handlers (and +// `window.myVariable = "newValue"` was executed). +// +// This can be racy because Input.dispatchKeyEvent and window.myVariable = "newValue" run +// in different processes. + +const PAGE_URL = + "https://example.com/browser/remote/cdp/test/browser/input/doc_dispatchKeyEvent_race.html"; + +add_task(async function ({ client }) { + await loadURL(PAGE_URL); + + const { Input, Runtime } = client; + + // Need an enabled Runtime domain to run evaluate. + info("Enable the Runtime domain"); + await Runtime.enable(); + const { context } = await Runtime.executionContextCreated(); + + info("Focus the input on the page"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const input = content.document.querySelector("input"); + input.focus(); + is(input, content.document.activeElement, "Input should be focused"); + }); + + // See doc_input_dispatchKeyEvent_race.html + // The page listens to `input` events to update a property on window. + // We will check that the value is updated as soon dispatchKeyEvent has resolved. + await checkWindowTestValue("initial-value", context.id, Runtime); + + info("Write 'hhhhhh' ('h' times 6)"); + for (let i = 0; i < 6; i++) { + await dispatchKeyEvent(Input, "h", 72, "keyDown"); + await dispatchKeyEvent(Input, "h", 72, "keyUp"); + } + await checkWindowTestValue("hhhhhh", context.id, Runtime); + + info("Write 'aaaaaa' with 6 consecutive keydown and one keyup"); + await Promise.all([ + dispatchKeyEvent(Input, "a", 65, "keyDown"), + dispatchKeyEvent(Input, "a", 65, "keyDown"), + dispatchKeyEvent(Input, "a", 65, "keyDown"), + dispatchKeyEvent(Input, "a", 65, "keyDown"), + dispatchKeyEvent(Input, "a", 65, "keyDown"), + dispatchKeyEvent(Input, "a", 65, "keyDown"), + ]); + await dispatchKeyEvent(Input, "a", 65, "keyUp"); + await checkWindowTestValue("hhhhhhaaaaaa", context.id, Runtime); +}); + +function dispatchKeyEvent(Input, key, keyCode, type, modifiers = 0) { + info(`Send ${type} for key ${key}`); + return Input.dispatchKeyEvent({ + type, + modifiers, + windowsVirtualKeyCode: keyCode, + key, + }); +} + +async function checkWindowTestValue(expected, contextId, Runtime) { + info("Retrieve the value of `window.testValue` in the test page"); + const { result } = await Runtime.evaluate({ + contextId, + expression: "window.testValue", + }); + + is(result.value, expected, "Content window test value is correct"); +} diff --git a/remote/cdp/test/browser/input/browser_dispatchMouseEvent.js b/remote/cdp/test/browser/input/browser_dispatchMouseEvent.js new file mode 100644 index 0000000000..9ab2b74a67 --- /dev/null +++ b/remote/cdp/test/browser/input/browser_dispatchMouseEvent.js @@ -0,0 +1,136 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const PAGE_URL = + "https://example.com/browser/remote/cdp/test/browser/input/doc_events.html"; + +add_task(async function testPressedReleasedAndClick({ client }) { + const { Input } = client; + + await loadURL(PAGE_URL); + await resetEvents(); + + info("Click the 'pointers' div."); + await Input.dispatchMouseEvent({ + type: "mousePressed", + x: 80, + y: 180, + }); + await Input.dispatchMouseEvent({ + type: "mouseReleased", + x: 80, + y: 180, + }); + + const events = await getEvents(); + ["mousedown", "mouseup", "click"].forEach((type, index) => { + info(`Checking properties for event ${type}`); + checkProperties({ type, button: 0 }, events[index]); + }); +}); + +add_task(async function testModifiers({ client }) { + const { Input } = client; + + await loadURL(PAGE_URL); + + const testable_modifiers = [ + [alt], + [ctrl], + [meta], + [shift], + [alt, meta], + [ctrl, shift], + [alt, ctrl, meta, shift], + ]; + + for (let modifiers of testable_modifiers) { + info(`MousePressed with modifier ${modifiers} on the 'pointers' div.`); + + await resetEvents(); + await Input.dispatchMouseEvent({ + type: "mousePressed", + x: 80, + y: 180, + modifiers: modifiers.reduce((previous, current) => previous | current), + }); + + const events = await getEvents(); + const expectedEvent = { + type: "mousedown", + button: 0, + altKey: modifiers.includes(alt), + ctrlKey: modifiers.includes(ctrl), + metaKey: modifiers.includes(meta), + shiftKey: modifiers.includes(shift), + }; + + checkProperties(expectedEvent, events[0]); + } +}); + +add_task(async function testClickCount({ client }) { + const { Input } = client; + + await loadURL(PAGE_URL); + + const testable_clickCounts = [ + { type: "click", clickCount: 1 }, + { type: "dblclick", clickCount: 2 }, + ]; + + for (const { clickCount, type } of testable_clickCounts) { + info(`MousePressed with clickCount ${clickCount} on the 'pointers' div.`); + + await resetEvents(); + await Input.dispatchMouseEvent({ + type: "mousePressed", + x: 80, + y: 180, + clickCount, + }); + await Input.dispatchMouseEvent({ + type: "mouseReleased", + x: 80, + y: 180, + clickCount, + }); + + const events = await getEvents(); + checkProperties({ type, button: 0 }, events[events.length - 1]); + } +}); + +add_task(async function testDispatchMouseEventAwaitClick({ client }) { + const { Input } = client; + + await setupForInput(PAGE_URL); + await loadURL( + toDataURL(` +
foo
+ `) + ); + + const { x, y } = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + () => { + const div = content.document.querySelector("div"); + return div.getBoundingClientRect(); + } + ); + + await Input.dispatchMouseEvent({ type: "mousePressed", x, y }); + await Input.dispatchMouseEvent({ type: "mouseReleased", x, y }); + + const context = await enableRuntime(client); + const { result } = await evaluate( + client, + context.id, + () => globalThis.result + ); + + is(result.value, "clicked", "Awaited click event handler"); +}); diff --git a/remote/cdp/test/browser/input/doc_dispatchKeyEvent_race.html b/remote/cdp/test/browser/input/doc_dispatchKeyEvent_race.html new file mode 100644 index 0000000000..c761a224e6 --- /dev/null +++ b/remote/cdp/test/browser/input/doc_dispatchKeyEvent_race.html @@ -0,0 +1,28 @@ + + + + + Test page for dispatchKeyEvent race test + + + + + + diff --git a/remote/cdp/test/browser/input/doc_events.html b/remote/cdp/test/browser/input/doc_events.html new file mode 100644 index 0000000000..74df931233 --- /dev/null +++ b/remote/cdp/test/browser/input/doc_events.html @@ -0,0 +1,148 @@ + + + + + Input Events + + + + +
+
+

KeyReporter

+ +
+
+

ClickReporter

+
+
+
+
+

Events

+
+
+ + diff --git a/remote/cdp/test/browser/input/head.js b/remote/cdp/test/browser/input/head.js new file mode 100644 index 0000000000..4076935802 --- /dev/null +++ b/remote/cdp/test/browser/input/head.js @@ -0,0 +1,150 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/remote/cdp/test/browser/head.js", + this +); + +const { Input: I } = ChromeUtils.importESModule( + "chrome://remote/content/cdp/domains/parent/Input.sys.mjs" +); +const { AppInfo } = ChromeUtils.importESModule( + "chrome://remote/content/shared/AppInfo.sys.mjs" +); + +const { alt, ctrl, meta, shift } = I.Modifier; + +// Map of key codes used in Input tests. +const KEYCODES = { + a: KeyboardEvent.DOM_VK_A, + A: KeyboardEvent.DOM_VK_A, + b: KeyboardEvent.DOM_VK_B, + B: KeyboardEvent.DOM_VK_B, + c: KeyboardEvent.DOM_VK_C, + C: KeyboardEvent.DOM_VK_C, + h: KeyboardEvent.DOM_VK_H, + H: KeyboardEvent.DOM_VK_H, + Alt: KeyboardEvent.DOM_VK_ALT, + ArrowLeft: KeyboardEvent.DOM_VK_LEFT, + ArrowRight: KeyboardEvent.DOM_VK_RIGHT, + ArrowDown: KeyboardEvent.DOM_VK_DOWN, + Backspace: KeyboardEvent.DOM_VK_BACK_SPACE, + Control: KeyboardEvent.DOM_VK_CONTROL, + Meta: KeyboardEvent.DM_VK_META, + Shift: KeyboardEvent.DOM_VK_SHIFT, + Tab: KeyboardEvent.DOM_VK_TAB, +}; + +async function setupForInput(url) { + await loadURL(url); + info("Focus the input on the page"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const input = content.document.querySelector("input"); + input.focus(); + is(input, content.document.activeElement, "Input should be focused"); + is(input.value, "", "Check input content"); + is(input.selectionStart, 0, "Check position of input caret"); + }); +} + +async function withModifier(Input, modKey, mod, key) { + await dispatchKeyEvent(Input, modKey, "rawKeyDown", I.Modifier[mod]); + await dispatchKeyEvent(Input, key, "keyDown", I.Modifier[mod]); + await dispatchKeyEvent(Input, key, "keyUp", I.Modifier[mod]); + await dispatchKeyEvent(Input, modKey, "keyUp"); +} + +function dispatchKeyEvent(Input, key, type, modifiers = 0) { + info(`Send ${type} for key ${key}`); + return Input.dispatchKeyEvent({ + type, + modifiers, + windowsVirtualKeyCode: KEYCODES[key], + key, + }); +} + +async function getEvents() { + const events = await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + return content.wrappedJSObject.allEvents; + }); + info(`Events: ${JSON.stringify(events)}`); + return events; +} + +function getInputContent() { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const input = content.document.querySelector("input"); + return { value: input.value, caret: input.selectionStart }; + }); +} + +function checkEvent(event, type, key, property, expectedValue) { + let expected = { type, key }; + expected[property] = expectedValue; + checkProperties(expected, event, "Event"); +} + +async function checkInputContent(expectedValue, expectedCaret) { + const { value, caret } = await getInputContent(); + is(value, expectedValue, "Check input content"); + is(caret, expectedCaret, "Check position of input caret"); +} + +function checkProperties(expectedObj, targetObj, message = "Compare objects") { + for (const prop in expectedObj) { + is(targetObj[prop], expectedObj[prop], message + `: check ${prop}`); + } +} + +function keyForPlatform() { + // TODO add cases for other key-combinations as the need arises + let primary = ctrl; + let primaryKey = "Control"; + if (AppInfo.isMac) { + primary = alt; + primaryKey = "Alt"; + } + return { primary, primaryKey }; +} + +async function sendTextKey(Input, key, modifiers = 0) { + await dispatchKeyEvent(Input, key, "keyDown", modifiers); + await dispatchKeyEvent(Input, key, "keyUp", modifiers); +} + +async function sendText(Input, text) { + for (const sym of text) { + await sendTextKey(Input, sym); + } +} + +async function sendRawKey(Input, key, modifiers = 0) { + await dispatchKeyEvent(Input, key, "rawKeyDown", modifiers); + await dispatchKeyEvent(Input, key, "keyUp", modifiers); +} + +async function checkBackspace(Input, expected, modifiers = 0) { + info("Send Backspace"); + await sendRawKey(Input, "Backspace", modifiers); + await checkInputContent(expected, expected.length); +} + +async function resetEvents() { + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + content.wrappedJSObject.resetEvents(); + const events = content.wrappedJSObject.allEvents; + is(events.length, 0, "List of events should be empty"); + }); +} + +function resetInput(value = "") { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [value], function (arg) { + const input = content.document.querySelector("input"); + input.value = arg; + input.focus(); + }); +} -- cgit v1.2.3