summaryrefslogtreecommitdiffstats
path: root/toolkit/components/satchel/test/test_bug_511615.html
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/satchel/test/test_bug_511615.html')
-rw-r--r--toolkit/components/satchel/test/test_bug_511615.html179
1 files changed, 179 insertions, 0 deletions
diff --git a/toolkit/components/satchel/test/test_bug_511615.html b/toolkit/components/satchel/test/test_bug_511615.html
new file mode 100644
index 0000000000..2d67c5468b
--- /dev/null
+++ b/toolkit/components/satchel/test/test_bug_511615.html
@@ -0,0 +1,179 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Form History Autocomplete Untrusted Events: Bug 511615</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="satchel_common.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Test for Form History Autocomplete Untrusted Events: Bug 511615
+<p id="display"></p>
+
+<!-- we presumably can't hide the content for this test. -->
+<div id="content">
+ <!-- normal, basic form -->
+ <form id="form1" onsubmit="return false;">
+ <input type="text" name="field1">
+ <button type="submit">Submit</button>
+ </form>
+</div>
+
+<script>
+/**
+ * Indicates the time to wait before checking that the state of the autocomplete
+ * popup, including whether it is open, has not changed in response to events.
+ *
+ * Manual testing on a fast machine revealed that 80ms was still unreliable,
+ * while 100ms detected a simulated failure reliably. Unfortunately, this means
+ * that to take into account slower machines we should use a larger value.
+ *
+ * Note that if a machine takes more than this time to show the popup, this
+ * would not cause a failure, conversely the machine would not be able to detect
+ * whether the test should have failed. In other words, this use of timeouts is
+ * never expected to cause intermittent failures with test automation.
+ */
+const POPUP_RESPONSE_WAIT_TIME_MS = 200;
+
+/**
+ * Checks that the selected index in the popup still matches the given value.
+ *
+ * @param {number} expectedIndex
+ * @returns {Promise}
+ */
+async function checkSelectedIndexAfterResponseTime(expectedIndex) {
+ const popupState = await new Promise(resolve => {
+ setTimeout(() => getPopupState(resolve), POPUP_RESPONSE_WAIT_TIME_MS);
+ });
+ is(popupState.open, true, "Popup should still be open.");
+ is(popupState.selectedIndex, expectedIndex, "Selected index should match.");
+}
+
+const input = document.querySelector("#form1 > input");
+
+function doKeyUnprivileged(key) {
+ const keyName = "DOM_VK_" + key.toUpperCase();
+ let keycode, charcode, alwaysval;
+
+ if (key.length == 1) {
+ [keycode, charcode, alwaysval] = [0, key.charCodeAt(0), charcode];
+ } else {
+ [keycode, charcode, alwaysval] = [KeyEvent[keyName], 0, keycode];
+ if (!keycode) {
+ throw new Error("invalid keyname in test");
+ }
+ }
+
+ const dnEvent = new KeyboardEvent("keydown", {
+ bubbles: true,
+ cancelable: true,
+ view: null,
+ keyCode: alwaysval,
+ charCode: 0,
+ });
+ const prEvent = new KeyboardEvent("keypress", {
+ bubbles: true,
+ cancelable: true,
+ view: null,
+ keyCode: keycode,
+ charCode: charcode,
+ });
+ const upEvent = new KeyboardEvent("keyup", {
+ bubbles: true,
+ cancelable: true,
+ view: null,
+ keyCode: alwaysval,
+ charCode: 0,
+ });
+
+ input.dispatchEvent(dnEvent);
+ input.dispatchEvent(prEvent);
+ input.dispatchEvent(upEvent);
+}
+
+function doClickWithMouseEventUnprivileged() {
+ let dnEvent = document.createEvent("MouseEvent");
+ let upEvent = document.createEvent("MouseEvent");
+ let ckEvent = document.createEvent("MouseEvent");
+
+ /* eslint-disable no-multi-spaces, max-len */
+ dnEvent.initMouseEvent("mousedown", true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
+ upEvent.initMouseEvent("mouseup", true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
+ ckEvent.initMouseEvent("mouseclick", true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
+ /* eslint-enable no-multi-spaces, max-len */
+
+ input.dispatchEvent(dnEvent);
+ input.dispatchEvent(upEvent);
+ input.dispatchEvent(ckEvent);
+}
+
+add_setup(async () => {
+ await new Promise(resolve => updateFormHistory([
+ { op: "remove" },
+ { op: "add", fieldname: "field1", value: "value1" },
+ { op: "add", fieldname: "field1", value: "value2" },
+ { op: "add", fieldname: "field1", value: "value3" },
+ { op: "add", fieldname: "field1", value: "value4" },
+ { op: "add", fieldname: "field1", value: "value5" },
+ { op: "add", fieldname: "field1", value: "value6" },
+ { op: "add", fieldname: "field1", value: "value7" },
+ { op: "add", fieldname: "field1", value: "value8" },
+ { op: "add", fieldname: "field1", value: "value9" },
+ ], resolve));
+});
+
+add_task(async function test_untrusted_events_ignored() {
+ // The autocomplete popup should not open from untrusted events.
+ for (let triggerFn of [
+ () => input.focus(),
+ () => input.click(),
+ () => doClickWithMouseEventUnprivileged(),
+ () => doKeyUnprivileged("down"),
+ () => doKeyUnprivileged("page_down"),
+ () => doKeyUnprivileged("return"),
+ () => doKeyUnprivileged("v"),
+ () => doKeyUnprivileged(" "),
+ () => doKeyUnprivileged("back_space"),
+ ]) {
+ // We must wait for the entire timeout for each individual test, because the
+ // next event in the list might prevent the popup from opening.
+ await noPopupBy(triggerFn);
+ }
+
+ // A privileged key press will actually open the popup.
+ await openPopupOn(input);
+
+ // The selected autocomplete item should not change from untrusted events.
+ for (let triggerFn of [
+ () => doKeyUnprivileged("down"),
+ () => doKeyUnprivileged("page_down"),
+ ]) {
+ triggerFn();
+ await checkSelectedIndexAfterResponseTime(-1);
+ }
+
+ // A privileged key press will actually change the selected index.
+ const indexChanged = notifySelectedIndex(0);
+ synthesizeKey("KEY_ArrowDown");
+ await indexChanged;
+
+ // The selected autocomplete item should not change and it should not be
+ // possible to use it from untrusted events.
+ for (let triggerFn of [
+ () => doKeyUnprivileged("down"),
+ () => doKeyUnprivileged("page_down"),
+ () => doKeyUnprivileged("right"),
+ () => doKeyUnprivileged(" "),
+ () => doKeyUnprivileged("back_space"),
+ () => doKeyUnprivileged("back_space"),
+ () => doKeyUnprivileged("return"),
+ ]) {
+ triggerFn();
+ await checkSelectedIndexAfterResponseTime(0);
+ is(input.value, "", "The selected item should not have been used.");
+ }
+});
+</script>
+</body>
+</html>