diff options
Diffstat (limited to 'toolkit/components/satchel/test/test_bug_511615.html')
-rw-r--r-- | toolkit/components/satchel/test/test_bug_511615.html | 179 |
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> |