diff options
Diffstat (limited to 'toolkit/components/satchel/test/test_bug_511615.html')
-rw-r--r-- | toolkit/components/satchel/test/test_bug_511615.html | 194 |
1 files changed, 194 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 000000000..66972d9b3 --- /dev/null +++ b/toolkit/components/satchel/test/test_bug_511615.html @@ -0,0 +1,194 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for Form History Autocomplete Untrusted Events: Bug 511615</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script> + <script type="text/javascript" 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> + +<pre id="test"> +<script class="testbody" type="text/javascript"> + +var resolvePopupShownListener; +registerPopupShownListener(() => resolvePopupShownListener()); + +function waitForNextPopup() { + return new Promise(resolve => { resolvePopupShownListener = resolve; }); +} + +/** + * 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; + +SimpleTest.requestFlakyTimeout("Must ensure that an event does not happen."); + +/** + * Checks that the popup does not open in response to the given function. + */ +function expectPopupDoesNotOpen(triggerFn) { + let popupShown = waitForNextPopup(); + triggerFn(); + return Promise.race([ + popupShown.then(() => Promise.reject("Popup opened unexpectedly.")), + new Promise(resolve => setTimeout(resolve, POPUP_RESPONSE_WAIT_TIME_MS)), + ]); +} + +/** + * Checks that the selected index in the popup still matches the given value. + */ +function checkSelectedIndexAfterResponseTime(expectedIndex) { + return new Promise(resolve => { + setTimeout(() => getPopupState(resolve), POPUP_RESPONSE_WAIT_TIME_MS); + }).then(popupState => { + is(popupState.open, true, "Popup should still be open."); + is(popupState.selectedIndex, expectedIndex, "Selected index should match."); + }); +} + +function doKeyUnprivileged(key) { + let keyName = "DOM_VK_" + key.toUpperCase(); + let keycode, charcode; + + if (key.length == 1) { + keycode = 0; + charcode = key.charCodeAt(0); + alwaysval = charcode; + } else { + keycode = KeyEvent[keyName]; + if (!keycode) + throw "invalid keyname in test"; + charcode = 0; + alwaysval = keycode; + } + + let dnEvent = document.createEvent('KeyboardEvent'); + let prEvent = document.createEvent('KeyboardEvent'); + let upEvent = document.createEvent('KeyboardEvent'); + + dnEvent.initKeyEvent("keydown", true, true, null, false, false, false, false, alwaysval, 0); + prEvent.initKeyEvent("keypress", true, true, null, false, false, false, false, keycode, charcode); + upEvent.initKeyEvent("keyup", true, true, null, false, false, false, false, alwaysval, 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'); + + 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); + + input.dispatchEvent(dnEvent); + input.dispatchEvent(upEvent); + input.dispatchEvent(ckEvent); +} + +let input = $_(1, "field1"); + +add_task(function* test_initialize() { + yield 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(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. + yield expectPopupDoesNotOpen(triggerFn); + } + + // A privileged key press will actually open the popup. + let popupShown = waitForNextPopup(); + doKey("down"); + yield popupShown; + + // The selected autocomplete item should not change from untrusted events. + for (let triggerFn of [ + () => doKeyUnprivileged("down"), + () => doKeyUnprivileged("page_down"), + ]) { + triggerFn(); + yield checkSelectedIndexAfterResponseTime(-1); + } + + // A privileged key press will actually change the selected index. + let indexChanged = new Promise(resolve => notifySelectedIndex(0, resolve)); + doKey("down"); + yield 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(); + yield checkSelectedIndexAfterResponseTime(0); + is(input.value, "", "The selected item should not have been used."); + } + + // Close the popup. + input.blur(); +}); +</script> +</pre> +</body> +</html> |