summaryrefslogtreecommitdiffstats
path: root/dom/tests/mochitest/keyhandling
diff options
context:
space:
mode:
Diffstat (limited to 'dom/tests/mochitest/keyhandling')
-rw-r--r--dom/tests/mochitest/keyhandling/browsertest.html108
-rw-r--r--dom/tests/mochitest/keyhandling/chrome.toml22
-rw-r--r--dom/tests/mochitest/keyhandling/mochitest.toml12
-rw-r--r--dom/tests/mochitest/keyhandling/test_browser.xhtml252
-rw-r--r--dom/tests/mochitest/keyhandling/test_editor.xhtml268
-rw-r--r--dom/tests/mochitest/keyhandling/test_input.html226
-rw-r--r--dom/tests/mochitest/keyhandling/test_textarea.html226
-rw-r--r--dom/tests/mochitest/keyhandling/test_windowed.xhtml33
8 files changed, 1147 insertions, 0 deletions
diff --git a/dom/tests/mochitest/keyhandling/browsertest.html b/dom/tests/mochitest/keyhandling/browsertest.html
new file mode 100644
index 0000000000..7121e6b02f
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/browsertest.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+
+<html>
+<body>
+<p style="white-space: nowrap">
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+ A long paragraph to make a horizontal scrollbar.
+</p>
+<p id="paragraph">Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+<p>Lots of paragraphs to make a vertical scrollbar.</p>
+</body>
+</html>
diff --git a/dom/tests/mochitest/keyhandling/chrome.toml b/dom/tests/mochitest/keyhandling/chrome.toml
new file mode 100644
index 0000000000..5dbecb7979
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/chrome.toml
@@ -0,0 +1,22 @@
+[DEFAULT]
+# test_input.html and test_textarea.html are also used as mochitests, hence
+# we have to flip the skip assertion flag for the chrome tests here.
+prefs = ["dom.security.skip_remote_script_assertion_in_system_priv_context=true"]
+
+# nsIWidget::SynthesizeNativeKeyEvent() required (Bug 1410525 for headless)
+skip-if = [
+ "os == 'linux'",
+ "os == 'android'",
+ "headless",
+]
+
+["test_browser.xhtml"]
+support-files = ["browsertest.html"]
+
+["test_editor.xhtml"]
+
+["test_windowed.xhtml"]
+support-files = [
+ "test_input.html",
+ "test_textarea.html",
+]
diff --git a/dom/tests/mochitest/keyhandling/mochitest.toml b/dom/tests/mochitest/keyhandling/mochitest.toml
new file mode 100644
index 0000000000..461defc36c
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/mochitest.toml
@@ -0,0 +1,12 @@
+[DEFAULT]
+# nsIWidget::SynthesizeNativeKeyEvent() required (Bug 1410525 for headless)
+skip-if = [
+ "os == 'linux'",
+ "os == 'android'",
+ "headless",
+]
+
+["test_input.html"]
+
+["test_textarea.html"]
+skip-if = ["os == 'win' && asan"] # Bug 1658513
diff --git a/dom/tests/mochitest/keyhandling/test_browser.xhtml b/dom/tests/mochitest/keyhandling/test_browser.xhtml
new file mode 100644
index 0000000000..3cddf5ba76
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/test_browser.xhtml
@@ -0,0 +1,252 @@
+<?xml version="1.0"?>
+
+<window title="Browser element keyhandling tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ const IS_MAC = navigator.platform.indexOf("Mac") === 0;
+ const VK = {};
+ const CHARS = {};
+
+ // Copied values from NativeKeyCodes.js and EventUtils.js
+ if (IS_MAC) {
+ VK.LEFT = MAC_VK_LeftArrow;
+ CHARS.LEFT = "\uF702";
+ VK.RIGHT = MAC_VK_RightArrow;
+ CHARS.RIGHT = "\uF703";
+ VK.UP = MAC_VK_UpArrow;
+ CHARS.UP = "\uF700";
+ VK.DOWN = MAC_VK_DownArrow;
+ CHARS.DOWN = "\uF701";
+ VK.SPACE = MAC_VK_Space;
+ VK.PGDOWN = MAC_VK_PageDown;
+ CHARS.PGDOWN = "\uF72D";
+ VK.PGUP = MAC_VK_PageUp;
+ CHARS.PGUP = "\uF72C";
+ VK.C = MAC_VK_ANSI_C;
+ VK.HOME = MAC_VK_Home;
+ CHARS.HOME = "\uF729";
+ VK.END = MAC_VK_End;
+ CHARS.END = "\uF72B";
+ } else {
+ VK.LEFT = WIN_VK_LEFT;
+ CHARS.LEFT = "";
+ VK.RIGHT = WIN_VK_RIGHT;
+ CHARS.RIGHT = "";
+ VK.UP = WIN_VK_UP;
+ CHARS.UP = "";
+ VK.DOWN = WIN_VK_DOWN;
+ CHARS.DOWN = "";
+ VK.SPACE = WIN_VK_SPACE;
+ VK.PGDOWN = WIN_VK_NEXT;
+ CHARS.PGDOWN = "";
+ VK.PGUP = WIN_VK_PRIOR;
+ CHARS.PGUP = "";
+ VK.C = WIN_VK_C;
+ VK.HOME = WIN_VK_HOME;
+ CHARS.HOME = "";
+ VK.END = WIN_VK_END;
+ CHARS.END = "";
+ }
+
+ function waitForEvent(target, event) {
+ info(`Waiting for ${event} event.`);
+ return new Promise(resolve => {
+ browser.addEventListener(event, resolve, { once: true });
+ });
+ }
+
+ function synthesizeKey(keyCode, modifiers, chars) {
+ return new Promise((resolve, reject) => {
+ if (!synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, keyCode, modifiers, chars, chars, resolve)) {
+ reject();
+ }
+ });
+ }
+
+ function getWindowProperties(browser, properties) {
+ let results = {};
+ for (let prop of properties) {
+ results[prop] = browser.contentWindow[prop];
+ }
+
+ return results;
+ }
+
+ function getScrollPosition(browser) {
+ return getWindowProperties(browser, ["scrollX", "scrollY"]);
+ }
+
+ async function test() {
+ // Smooth scrolling makes scroll events take time and it's difficult to know
+ // when they've ended, so turn it off for this test.
+ await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]] });
+
+ let browser = document.getElementById("browser");
+ browser.focus();
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, 0, "Should not be scrolled");
+
+ info("down");
+ await synthesizeKey(VK.DOWN, {}, CHARS.DOWN);
+ await waitForEvent(browser.contentWindow, "scroll");
+ let { scrollX: lineScrollX, scrollY: lineScrollY } = await getScrollPosition(browser);
+ is(lineScrollX, 0, "Should not be scrolled");
+ ok(lineScrollY > 0, "Should be scrolled");
+
+ info("up");
+ await synthesizeKey(VK.UP, {}, CHARS.UP);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, 0, "Should not be scrolled");
+ }
+
+ info("right");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await waitForEvent(browser.contentWindow, "scroll");
+ let { scrollX: rightScrollX, scrollY: rightScrollY } = await getScrollPosition(browser);
+ ok(rightScrollX > 0, "Should be scrolled");
+ is(rightScrollY, 0, "Should not be scrolled");
+
+ info("left");
+ await synthesizeKey(VK.LEFT, {}, CHARS.LEFT);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, 0, "Should not be scrolled");
+ }
+
+ info("space");
+ await synthesizeKey(VK.SPACE, {}, " ");
+ await waitForEvent(browser.contentWindow, "scroll");
+ let { scrollX: pageScrollX, scrollY: pageScrollY } = await getScrollPosition(browser);
+ is(pageScrollX, 0, "Should not be scrolled");
+ ok(pageScrollY > lineScrollY, "Should be scrolled more than a single line");
+
+ info("shift+space");
+ await synthesizeKey(VK.SPACE, { shiftKey: true }, " ");
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, 0, "Should not be scrolled");
+ }
+
+ info("page down");
+ await synthesizeKey(VK.PGDOWN, {}, CHARS.PGDOWN);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, pageScrollY, "Should be scrolled a page");
+ }
+
+ info("page up");
+ await synthesizeKey(VK.PGUP, {}, CHARS.PGUP);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, 0, "Should not be scrolled");
+ }
+
+ info("accel+down");
+ await synthesizeKey(VK.DOWN, { accelKey: true }, CHARS.DOWN);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY, innerHeight } = await getWindowProperties(browser, ["scrollX", "scrollY", "innerHeight"]);
+ is(scrollX, 0, "Should not be scrolled");
+ // We can't know the scrollbar height so check that we're scrolled to within 100px of what we expect.
+ isfuzzy(scrollY, browser.contentDocument.body.clientHeight - innerHeight, 100, "Should be scrolled to the end.");
+ }
+
+ info("accel+up");
+ await synthesizeKey(VK.UP, { accelKey: true }, CHARS.UP);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, 0, "Should not be scrolled");
+ }
+
+ info("end");
+ await synthesizeKey(VK.END, {}, CHARS.END);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY, innerHeight } = await getWindowProperties(browser, ["scrollX", "scrollY", "innerHeight"]);
+ is(scrollX, 0, "Should not be scrolled");
+ // We can't know the scrollbar height so check that we're scrolled to within 100px of what we expect.
+ isfuzzy(scrollY, browser.contentDocument.body.clientHeight - innerHeight, 100, "Should be scrolled to the end.");
+ }
+
+ info("home");
+ await synthesizeKey(VK.HOME, {}, CHARS.HOME);
+ await waitForEvent(browser.contentWindow, "scroll");
+ {
+ let { scrollX, scrollY } = await getScrollPosition(browser);
+ is(scrollX, 0, "Should not be scrolled");
+ is(scrollY, 0, "Should not be scrolled");
+ }
+
+ // Select the start of the first paragraph
+ let paragraph = browser.contentDocument.getElementById("paragraph");
+ let selection = browser.contentWindow.getSelection();
+ selection.setBaseAndExtent(paragraph.firstChild, 0, paragraph.firstChild, "Lots of".length);
+
+ info("copy");
+ await SimpleTest.promiseClipboardChange("Lots of", () => {
+ synthesizeKey(VK.C, { accelKey: true }, "c");
+ });
+
+ for (let i = 0; i < " paragraphs".length; i++) {
+ info("select right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ }
+
+ info("copy");
+ await SimpleTest.promiseClipboardChange("Lots of paragraphs", () => {
+ synthesizeKey(VK.C, { accelKey: true }, "c");
+ });
+
+ for (let i = 0; i < " paragraphs".length; i++) {
+ info("select left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ }
+
+ info("copy");
+ await SimpleTest.promiseClipboardChange("Lots of", () => {
+ synthesizeKey(VK.C, { accelKey: true }, "c");
+ });
+
+ info("select down");
+ await synthesizeKey(VK.DOWN, { shiftKey: true }, CHARS.DOWN);
+
+ info("copy");
+ await SimpleTest.promiseClipboardChange("Lots of paragraphs to make a vertical scrollbar.\n\nLots of", () => {
+ synthesizeKey(VK.C, { accelKey: true }, "c");
+ });
+
+ SimpleTest.finish();
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <browser id="browser" src="browsertest.html" style="height: 500px"/>
+</window>
diff --git a/dom/tests/mochitest/keyhandling/test_editor.xhtml b/dom/tests/mochitest/keyhandling/test_editor.xhtml
new file mode 100644
index 0000000000..9ddbe4adc0
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/test_editor.xhtml
@@ -0,0 +1,268 @@
+<?xml version="1.0"?>
+
+<window title="Browser element keyhandling tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/NativeKeyCodes.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ const IS_MAC = navigator.platform.indexOf("Mac") === 0;
+ const VK = {};
+ const CHARS = {};
+
+ // Copied values from NativeKeyCodes.js and EventUtils.js
+ if (IS_MAC) {
+ VK.LEFT = MAC_VK_LeftArrow;
+ CHARS.LEFT = "\uF702";
+ VK.RIGHT = MAC_VK_RightArrow;
+ CHARS.RIGHT = "\uF703";
+ VK.UP = MAC_VK_UpArrow;
+ CHARS.UP = "\uF700";
+ VK.DOWN = MAC_VK_DownArrow;
+ CHARS.DOWN = "\uF701";
+ VK.SPACE = MAC_VK_Space;
+ VK.X = MAC_VK_ANSI_X;
+ VK.V = MAC_VK_ANSI_V;
+ VK.A = MAC_VK_ANSI_A;
+ VK.Z = MAC_VK_ANSI_Z;
+ VK.F = MAC_VK_ANSI_F;
+ VK.O = MAC_VK_ANSI_O;
+ VK.BACKSPACE = MAC_VK_PC_Backspace;
+ CHARS.BACKSPACE = "\u007F";
+ } else {
+ VK.LEFT = WIN_VK_LEFT;
+ CHARS.LEFT = "";
+ VK.RIGHT = WIN_VK_RIGHT;
+ CHARS.RIGHT = "";
+ VK.HOME = WIN_VK_HOME;
+ CHARS.HOME = "";
+ VK.END = WIN_VK_END;
+ CHARS.END = "";
+ VK.SPACE = WIN_VK_SPACE;
+ VK.X = WIN_VK_X;
+ VK.V = WIN_VK_V;
+ VK.A = WIN_VK_A;
+ VK.Z = WIN_VK_Z;
+ VK.Y = WIN_VK_Y;
+ VK.F = WIN_VK_F;
+ VK.O = WIN_VK_O;
+ VK.BACKSPACE = WIN_VK_BACK;
+ CHARS.BACKSPACE = "";
+ }
+
+ function waitForEvent(target, event) {
+ info(`Waiting for ${event} event.`);
+ return new Promise(resolve => {
+ browser.addEventListener(event, resolve, { once: true });
+ });
+ }
+
+ function synthesizeKey(keyCode, modifiers, chars) {
+ return new Promise((resolve, reject) => {
+ if (!synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, keyCode, modifiers, chars, chars, resolve)) {
+ reject();
+ }
+ });
+ }
+
+ function* nodes(element) {
+ let node = element.firstChild;
+ while (node) {
+ yield node;
+
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ yield* nodes(node);
+ }
+
+ node = node.nextSibling;
+ }
+ }
+
+ async function checkElement(element, start, selectedText, content = "Test text") {
+ selectionPosition = (element, range) => {
+ let pos = 0;
+ for (let node of nodes(element)) {
+ if (node.nodeType === Node.TEXT_NODE) {
+ if (node === range.startContainer) {
+ return pos + range.startOffset;
+ } else {
+ pos += node.nodeValue.length;
+ }
+ } else if (node === range.startContainer) {
+ for (let i = 0; i < range.startOffset; i++) {
+ pos += node.childNodes[i].textContent.length;
+ }
+
+ return pos;
+ }
+ }
+
+ throw new Error("startContainer of range never found.");
+ }
+
+ isReady = () => {
+ let selection = element.contentWindow.getSelection();
+ let range = selection.getRangeAt(0);
+ let pos = selectionPosition(element.contentDocument.documentElement, range);
+
+ if (start != pos) {
+ return false;
+ }
+ if (selectedText != selection.toString()) {
+ return false;
+ }
+ if (content != element.contentDocument.documentElement.textContent) {
+ return false;
+ }
+ return true;
+ };
+
+ for (let i = 0; i < 10; i++) {
+ if (isReady()) {
+ return;
+ }
+
+ SimpleTest.requestFlakyTimeout("Polling for changes to apply");
+ await new Promise(resolve => setTimeout(resolve, 50));
+ }
+ ok(false, `Timed out waiting for state ${start} "${selectedText}" "${content}"`);
+ let selection = element.contentWindow.getSelection();
+ let range = selection.getRangeAt(0);
+ info(`${selectionPosition(element.contentDocument.documentElement, range)} "${selection.toString()}" "${element.contentDocument.documentElement.textContent}"`);
+ }
+
+ async function test() {
+ let editor = document.getElementById("editor");
+ editor.contentDocument.designMode = "on";
+ editor.contentWindow.focus();
+ let edit = editor.getEditor(editor.contentWindow);
+ edit.beginningOfDocument();
+
+ await checkElement(editor, 0, "");
+
+ info("right");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await checkElement(editor, 1, "");
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(editor, 1, "e");
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(editor, 1, "es");
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(editor, 1, "e");
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(editor, 1, "");
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(editor, 0, "T");
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(editor, 1, "");
+
+ info("left");
+ await synthesizeKey(VK.LEFT, {}, CHARS.LEFT);
+ await checkElement(editor, 0, "");
+
+ if (IS_MAC) {
+ info("down");
+ await synthesizeKey(VK.DOWN, { shiftKey: true }, CHARS.DOWN);
+ } else {
+ info("end");
+ await synthesizeKey(VK.END, { shiftKey: true }, CHARS.END);
+ }
+ await checkElement(editor, 0, "Test text");
+
+ info("cut");
+ await synthesizeKey(VK.X, { accelKey: true }, "x");
+ await checkElement(editor, 0, "", "");
+ let text = SpecialPowers.getClipboardData("text/plain");
+ is(text, "Test text", "Should have cut to the clipboard");
+ SpecialPowers.clipboardCopyString("New text");
+
+ info("paste");
+ await synthesizeKey(VK.V, { accelKey: true }, "v");
+ await checkElement(editor, 8, "", "New text");
+
+ if (IS_MAC) {
+ info("up");
+ await synthesizeKey(VK.UP, {}, CHARS.UP);
+ } else {
+ info("home");
+ await synthesizeKey(VK.HOME, {}, CHARS.HOME);
+ }
+ await checkElement(editor, 0, "", "New text");
+
+ info("select all");
+ await synthesizeKey(VK.A, { accelKey: true}, "a", "select");
+ await checkElement(editor, 0, "New text", "New text");
+
+ info("right");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await checkElement(editor, 8, "", "New text");
+
+ info("word left");
+ if (IS_MAC) {
+ await synthesizeKey(VK.LEFT, { altKey: true }, CHARS.LEFT);
+ } else {
+ await synthesizeKey(VK.LEFT, { ctrlKey: true }, CHARS.LEFT);
+ }
+ await checkElement(editor, 4, "", "New text");
+
+ info("delete word left");
+ if (IS_MAC) {
+ await synthesizeKey(VK.BACKSPACE, { altKey: true }, CHARS.BACKSPACE);
+ } else {
+ await synthesizeKey(VK.BACKSPACE, { ctrlKey: true }, CHARS.BACKSPACE);
+ }
+ await checkElement(editor, 0, "", "text");
+
+ info("undo");
+ await synthesizeKey(VK.Z, { accelKey: true }, "z");
+ await checkElement(editor, 4, "", "New text");
+
+ info("redo");
+ if (IS_MAC) {
+ await synthesizeKey(VK.Z, { accelKey: true, shiftKey: true }, "z");
+ } else {
+ await synthesizeKey(VK.Y, { accelKey: true }, "y");
+ }
+ await checkElement(editor, 0, "", "text");
+
+ info("typing");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.SPACE, {}, " ");
+ await synthesizeKey(VK.F, {}, "f");
+ await synthesizeKey(VK.O, {}, "o");
+ await synthesizeKey(VK.O, {}, "o");
+ await checkElement(editor, 8, "", "text foo");
+
+ SimpleTest.finish();
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <editor id="editor" editortype="text" src="data:text/plain,Test text" style="height: 500px"/>
+</window>
diff --git a/dom/tests/mochitest/keyhandling/test_input.html b/dom/tests/mochitest/keyhandling/test_input.html
new file mode 100644
index 0000000000..f4ec1dddf5
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/test_input.html
@@ -0,0 +1,226 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>input key handling</title>
+
+<script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/EventUtils.js"></script>
+<script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/NativeKeyCodes.js"></script>
+<link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+const IS_MAC = navigator.platform.indexOf("Mac") == 0;
+const VK = {};
+const CHARS = {};
+
+if (IS_MAC) {
+ VK.LEFT = MAC_VK_LeftArrow;
+ CHARS.LEFT = "\uF702";
+ VK.RIGHT = MAC_VK_RightArrow;
+ CHARS.RIGHT = "\uF703";
+ VK.UP = MAC_VK_UpArrow;
+ CHARS.UP = "\uF700";
+ VK.DOWN = MAC_VK_DownArrow;
+ CHARS.DOWN = "\uF701";
+ VK.X = MAC_VK_ANSI_X;
+ VK.V = MAC_VK_ANSI_V;
+ VK.A = MAC_VK_ANSI_A;
+ VK.F = MAC_VK_ANSI_F;
+ VK.O = MAC_VK_ANSI_O;
+ VK.BACKSPACE = MAC_VK_PC_Backspace;
+ CHARS.BACKSPACE = "\u007F";
+ VK.Z = MAC_VK_ANSI_Z;
+ VK.SPACE = MAC_VK_Space;
+} else {
+ VK.LEFT = WIN_VK_LEFT;
+ CHARS.LEFT = "";
+ VK.RIGHT = WIN_VK_RIGHT;
+ CHARS.RIGHT = "";
+ VK.UP = WIN_VK_UP;
+ CHARS.UP = "";
+ VK.DOWN = WIN_VK_DOWN;
+ CHARS.DOWN = "";
+ VK.X = WIN_VK_X;
+ VK.V = WIN_VK_V;
+ VK.A = WIN_VK_A;
+ VK.F = WIN_VK_F;
+ VK.O = WIN_VK_O;
+ VK.END = WIN_VK_END;
+ CHARS.END = "";
+ VK.HOME = WIN_VK_HOME;
+ CHARS.HOME = "";
+ VK.BACKSPACE = WIN_VK_BACK;
+ CHARS.BACKSPACE = "";
+ VK.Z = WIN_VK_Z;
+ VK.SPACE = WIN_VK_SPACE;
+}
+
+if (window.arguments && window.arguments[0]) {
+ ok = window.arguments[0].ok;
+ is = window.arguments[0].is;
+}
+
+function synthesizeKey(keyCode, modifiers, chars, event = "keyup") {
+ return new Promise((resolve, reject) => {
+ window.addEventListener(event, resolve, { once: true });
+
+ if (!synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, keyCode, modifiers, chars, chars)) {
+ reject();
+ }
+ });
+}
+
+async function checkElement(element, start, end, content = "Test text") {
+ isReady = () => {
+ if (start != element.selectionStart) {
+ return false;
+ }
+ if (end != element.selectionEnd) {
+ return false;
+ }
+ if (content != element.value) {
+ return false;
+ }
+ return true;
+ };
+
+ for (let i = 0; i < 10; i++) {
+ if (isReady()) {
+ return;
+ }
+
+ SimpleTest.requestFlakyTimeout("Polling for changes to apply");
+ await new Promise(resolve => setTimeout(resolve, 50));
+ }
+ ok(false, "Timed out waiting for state");
+ is(element.selectionStart, start, "Should have the right selectionStart");
+ is(element.selectionEnd, end, "Should have the right selectionEnd");
+ is(element.value, content, "Should have the right value");
+}
+
+async function startTest() {
+ let input = document.getElementById("input");
+ input.focus();
+ await checkElement(input, 0, 0);
+
+ info("right");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await checkElement(input, 1, 1);
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(input, 1, 2);
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(input, 1, 3);
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(input, 1, 2);
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(input, 1, 1);
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(input, 0, 1);
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(input, 1, 1);
+
+ info("left");
+ await synthesizeKey(VK.LEFT, {}, CHARS.LEFT);
+ await checkElement(input, 0, 0);
+
+ if (IS_MAC) {
+ info("down");
+ await synthesizeKey(VK.DOWN, { shiftKey: true }, CHARS.DOWN);
+ } else {
+ info("end");
+ await synthesizeKey(VK.END, { shiftKey: true }, CHARS.END);
+ }
+ await checkElement(input, 0, 9);
+
+ info("cut");
+ await synthesizeKey(VK.X, { accelKey: true }, "x", "input");
+ await checkElement(input, 0, 0, "");
+ let text = SpecialPowers.getClipboardData("text/plain");
+ is(text, "Test text", "Should have cut to the clipboard");
+ SpecialPowers.clipboardCopyString("New text");
+
+ info("paste");
+ await synthesizeKey(VK.V, { accelKey: true }, "v", "input");
+ await checkElement(input, 8, 8, "New text");
+
+ if (IS_MAC) {
+ info("up");
+ await synthesizeKey(VK.UP, {}, CHARS.UP);
+ } else {
+ info("home");
+ await synthesizeKey(VK.HOME, {}, CHARS.HOME);
+ }
+ await checkElement(input, 0, 0, "New text");
+
+ info("select all");
+ await synthesizeKey(VK.A, { accelKey: true}, "a", "select");
+ await checkElement(input, 0, 8, "New text");
+
+ info("right");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await checkElement(input, 8, 8, "New text");
+
+ info("word left");
+ if (IS_MAC) {
+ await synthesizeKey(VK.LEFT, { altKey: true }, CHARS.LEFT);
+ } else {
+ await synthesizeKey(VK.LEFT, { ctrlKey: true }, CHARS.LEFT);
+ }
+ await checkElement(input, 4, 4, "New text");
+
+ info("delete word left");
+ if (IS_MAC) {
+ await synthesizeKey(VK.BACKSPACE, { altKey: true }, CHARS.BACKSPACE);
+ } else {
+ await synthesizeKey(VK.BACKSPACE, { ctrlKey: true }, CHARS.BACKSPACE);
+ }
+ await checkElement(input, 0, 0, "text");
+
+ info("undo");
+ await synthesizeKey(VK.Z, { accelKey: true }, "", "input");
+ await checkElement(input, 4, 4, "New text");
+
+ info("redo");
+ await synthesizeKey(VK.Z, { accelKey: true, shiftKey: true }, "", "input");
+ await checkElement(input, 0, 0, "text");
+
+ info("typing");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.SPACE, {}, " ");
+ await synthesizeKey(VK.F, {}, "f");
+ await synthesizeKey(VK.O, {}, "o");
+ await synthesizeKey(VK.O, {}, "o");
+ await checkElement(input, 8, 8, "text foo");
+}
+
+async function runTest() {
+ // When running in windowed mode the caller will start the test once we have
+ // focus.
+ if (window.arguments && window.arguments[0]) {
+ return;
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ await startTest();
+ SimpleTest.finish();
+}
+</script>
+</head>
+<body onload="runTest();">
+<input id=input value="Test text"/>
+</body>
+</html>
diff --git a/dom/tests/mochitest/keyhandling/test_textarea.html b/dom/tests/mochitest/keyhandling/test_textarea.html
new file mode 100644
index 0000000000..613baac0c9
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/test_textarea.html
@@ -0,0 +1,226 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>input key handling</title>
+
+<script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/EventUtils.js"></script>
+<script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/NativeKeyCodes.js"></script>
+<link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" />
+
+<script type="text/javascript">
+const IS_MAC = navigator.platform.indexOf("Mac") == 0;
+const VK = {};
+const CHARS = {};
+
+if (IS_MAC) {
+ VK.LEFT = MAC_VK_LeftArrow;
+ CHARS.LEFT = "\uF702";
+ VK.RIGHT = MAC_VK_RightArrow;
+ CHARS.RIGHT = "\uF703";
+ VK.UP = MAC_VK_UpArrow;
+ CHARS.UP = "\uF700";
+ VK.DOWN = MAC_VK_DownArrow;
+ CHARS.DOWN = "\uF701";
+ VK.X = MAC_VK_ANSI_X;
+ VK.V = MAC_VK_ANSI_V;
+ VK.A = MAC_VK_ANSI_A;
+ VK.F = MAC_VK_ANSI_F;
+ VK.O = MAC_VK_ANSI_O;
+ VK.BACKSPACE = MAC_VK_PC_Backspace;
+ CHARS.BACKSPACE = "\u007F";
+ VK.Z = MAC_VK_ANSI_Z;
+ VK.SPACE = MAC_VK_Space;
+} else {
+ VK.LEFT = WIN_VK_LEFT;
+ CHARS.LEFT = "";
+ VK.RIGHT = WIN_VK_RIGHT;
+ CHARS.RIGHT = "";
+ VK.UP = WIN_VK_UP;
+ CHARS.UP = "";
+ VK.DOWN = WIN_VK_DOWN;
+ CHARS.DOWN = "";
+ VK.X = WIN_VK_X;
+ VK.V = WIN_VK_V;
+ VK.A = WIN_VK_A;
+ VK.F = WIN_VK_F;
+ VK.O = WIN_VK_O;
+ VK.END = WIN_VK_END;
+ CHARS.END = "";
+ VK.HOME = WIN_VK_HOME;
+ CHARS.HOME = "";
+ VK.BACKSPACE = WIN_VK_BACK;
+ CHARS.BACKSPACE = "";
+ VK.Z = WIN_VK_Z;
+ VK.SPACE = WIN_VK_SPACE;
+}
+
+if (window.arguments && window.arguments[0]) {
+ ok = window.arguments[0].ok;
+ is = window.arguments[0].is;
+}
+
+function synthesizeKey(keyCode, modifiers, chars, event = "keyup") {
+ return new Promise((resolve, reject) => {
+ window.addEventListener(event, resolve, { once: true });
+
+ if (!synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, keyCode, modifiers, chars, chars)) {
+ reject();
+ }
+ });
+}
+
+async function checkElement(element, start, end, content = "Test text") {
+ isReady = () => {
+ if (start != element.selectionStart) {
+ return false;
+ }
+ if (end != element.selectionEnd) {
+ return false;
+ }
+ if (content != element.value) {
+ return false;
+ }
+ return true;
+ };
+
+ for (let i = 0; i < 10; i++) {
+ if (isReady()) {
+ return;
+ }
+
+ SimpleTest.requestFlakyTimeout("Polling for changes to apply");
+ await new Promise(resolve => setTimeout(resolve, 50));
+ }
+ ok(false, "Timed out waiting for state");
+ is(element.selectionStart, start, "Should have the right selectionStart");
+ is(element.selectionEnd, end, "Should have the right selectionEnd");
+ is(element.value, content, "Should have the right value");
+}
+
+async function startTest() {
+ let input = document.getElementById("input");
+ input.focus();
+ await checkElement(input, 0, 0);
+
+ info("right");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await checkElement(input, 1, 1);
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(input, 1, 2);
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(input, 1, 3);
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(input, 1, 2);
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(input, 1, 1);
+
+ info("shift+left");
+ await synthesizeKey(VK.LEFT, { shiftKey: true }, CHARS.LEFT);
+ await checkElement(input, 0, 1);
+
+ info("shift+right");
+ await synthesizeKey(VK.RIGHT, { shiftKey: true }, CHARS.RIGHT);
+ await checkElement(input, 1, 1);
+
+ info("left");
+ await synthesizeKey(VK.LEFT, {}, CHARS.LEFT);
+ await checkElement(input, 0, 0);
+
+ if (IS_MAC) {
+ info("down");
+ await synthesizeKey(VK.DOWN, { shiftKey: true }, CHARS.DOWN);
+ } else {
+ info("end");
+ await synthesizeKey(VK.END, { shiftKey: true }, CHARS.END);
+ }
+ await checkElement(input, 0, 9);
+
+ info("cut");
+ await synthesizeKey(VK.X, { accelKey: true }, "x", "input");
+ await checkElement(input, 0, 0, "");
+ let text = SpecialPowers.getClipboardData("text/plain");
+ is(text, "Test text", "Should have cut to the clipboard");
+ SpecialPowers.clipboardCopyString("New text");
+
+ info("paste");
+ await synthesizeKey(VK.V, { accelKey: true }, "v", "input");
+ await checkElement(input, 8, 8, "New text");
+
+ if (IS_MAC) {
+ info("up");
+ await synthesizeKey(VK.UP, {}, CHARS.UP);
+ } else {
+ info("home");
+ await synthesizeKey(VK.HOME, {}, CHARS.HOME);
+ }
+ await checkElement(input, 0, 0, "New text");
+
+ info("select all");
+ await synthesizeKey(VK.A, { accelKey: true}, "a", "select");
+ await checkElement(input, 0, 8, "New text");
+
+ info("right");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await checkElement(input, 8, 8, "New text");
+
+ info("word left");
+ if (IS_MAC) {
+ await synthesizeKey(VK.LEFT, { altKey: true }, CHARS.LEFT);
+ } else {
+ await synthesizeKey(VK.LEFT, { ctrlKey: true }, CHARS.LEFT);
+ }
+ await checkElement(input, 4, 4, "New text");
+
+ info("delete word left");
+ if (IS_MAC) {
+ await synthesizeKey(VK.BACKSPACE, { altKey: true }, CHARS.BACKSPACE);
+ } else {
+ await synthesizeKey(VK.BACKSPACE, { ctrlKey: true }, CHARS.BACKSPACE);
+ }
+ await checkElement(input, 0, 0, "text");
+
+ info("undo");
+ await synthesizeKey(VK.Z, { accelKey: true }, "", "input");
+ await checkElement(input, 4, 4, "New text");
+
+ info("redo");
+ await synthesizeKey(VK.Z, { accelKey: true, shiftKey: true }, "", "input");
+ await checkElement(input, 0, 0, "text");
+
+ info("typing");
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.RIGHT, {}, CHARS.RIGHT);
+ await synthesizeKey(VK.SPACE, {}, " ");
+ await synthesizeKey(VK.F, {}, "f");
+ await synthesizeKey(VK.O, {}, "o");
+ await synthesizeKey(VK.O, {}, "o");
+ await checkElement(input, 8, 8, "text foo");
+}
+
+async function runTest() {
+ // When running in windowed mode the caller will start the test once we have
+ // focus.
+ if (window.arguments && window.arguments[0]) {
+ return;
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ await startTest();
+ SimpleTest.finish();
+}
+</script>
+</head>
+<body onload="runTest();">
+<textarea id=input>Test text</textarea>
+</body>
+</html>
diff --git a/dom/tests/mochitest/keyhandling/test_windowed.xhtml b/dom/tests/mochitest/keyhandling/test_windowed.xhtml
new file mode 100644
index 0000000000..c89027b5c6
--- /dev/null
+++ b/dom/tests/mochitest/keyhandling/test_windowed.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+
+<window title="Top-level window keyhandling tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ async function run_test(path) {
+ let win = window.browsingContext.topChromeWindow
+ .openDialog(path, "_blank", "width=500,height=500", window);
+ await SimpleTest.promiseFocus(win);
+ await win.startTest();
+ }
+
+ async function test() {
+ await run_test("test_input.html");
+ await run_test("test_textarea.html");
+ SimpleTest.finish();
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+</window>