mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1590762 - Part 1: Test that we don't double submit forms. r=kmag
Differential Revision: https://phabricator.services.mozilla.com/D79145
This commit is contained in:
parent
ba550838cd
commit
d36c42d2e0
7
docshell/test/mochitest/clicker.html
Normal file
7
docshell/test/mochitest/clicker.html
Normal file
@ -0,0 +1,7 @@
|
||||
<!doctype html>
|
||||
<script>
|
||||
"use strict";
|
||||
let target = window.opener ? window.opener : window.parent;
|
||||
|
||||
onmessage = ({data}) => target.postMessage({}, "*");
|
||||
</script>
|
122
docshell/test/mochitest/double_submit.sjs
Normal file
122
docshell/test/mochitest/double_submit.sjs
Normal file
@ -0,0 +1,122 @@
|
||||
"use strict";
|
||||
|
||||
let self = this;
|
||||
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const BinaryInputStream = CC(
|
||||
"@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream"
|
||||
);
|
||||
|
||||
const BinaryOutputStream = CC(
|
||||
"@mozilla.org/binaryoutputstream;1",
|
||||
"nsIBinaryOutputStream",
|
||||
"setOutputStream"
|
||||
);
|
||||
|
||||
function log(str) {
|
||||
// dump(`LOG: ${str}\n`);
|
||||
}
|
||||
|
||||
function* generateBody(fragments, size) {
|
||||
let result = [];
|
||||
let chunkSize = (size / fragments) | 0;
|
||||
let remaining = size;
|
||||
|
||||
log(`Chunk size ${chunkSize}`)
|
||||
while (remaining > 0) {
|
||||
let data = new Uint8Array(Math.min(remaining, chunkSize));
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
// Generate a character in the [a-z] range.
|
||||
data[i] = 97 + Math.random() * (123 - 97);
|
||||
}
|
||||
|
||||
yield data;
|
||||
log(`Remaining to chunk ${remaining}`)
|
||||
remaining -= data.length;
|
||||
}
|
||||
}
|
||||
|
||||
function readStream(inputStream) {
|
||||
let available = 0;
|
||||
let result = [];
|
||||
while ((available = inputStream.available()) > 0) {
|
||||
result.push(inputStream.readBytes(available));
|
||||
}
|
||||
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
function now() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
async function handleRequest(request, response) {
|
||||
log("Get query parameters");
|
||||
Cu.importGlobalProperties(["URLSearchParams"]);
|
||||
let params = new URLSearchParams(request.queryString);
|
||||
|
||||
let delay = parseInt(params.get("delay")) || 0;
|
||||
let delayUntil = now() + delay;
|
||||
log(`Delay until ${delayUntil}`);
|
||||
|
||||
let message = "good";
|
||||
if (request.method !== "POST") {
|
||||
message = "bad";
|
||||
} else {
|
||||
log("Read POST body");
|
||||
let body = new URLSearchParams(readStream(new BinaryInputStream(request.bodyInputStream)));
|
||||
message = body.get("token") || "bad";
|
||||
log(`The result was ${message}`);
|
||||
}
|
||||
|
||||
let fragments = parseInt(params.get("fragments")) || 1;
|
||||
let size = parseInt(params.get("size")) || 1024;
|
||||
|
||||
let outputStream = new BinaryOutputStream(response.bodyOutputStream);
|
||||
|
||||
let header = "<!doctype html><!-- ";
|
||||
let footer = ` --><script>"use strict"; let target = (opener || parent); target.postMessage('${message}', '*');</script>`;
|
||||
|
||||
log("Set headers")
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.setHeader("Content-Length", `${size + header.length + footer.length}`, false);
|
||||
response.setStatusLine(request.httpVersion, "200", "OK");
|
||||
|
||||
response.processAsync();
|
||||
log("Write header");
|
||||
response.write(header);
|
||||
log("Write body")
|
||||
for (let data of generateBody(fragments, size)) {
|
||||
delay = Math.max(0, delayUntil - now())
|
||||
log(`Delay sending fragment for ${delay / fragments}`);
|
||||
let failed = false;
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
outputStream.writeByteArray(data, data.length);
|
||||
} catch (e) {
|
||||
log(e.message);
|
||||
failed = true;
|
||||
}
|
||||
resolve();
|
||||
}, delay / fragments);
|
||||
});
|
||||
|
||||
if (failed) {
|
||||
log("Stopped sending data");
|
||||
break;
|
||||
}
|
||||
|
||||
fragments = Math.max(--fragments, 1);
|
||||
log(`Fragments left ${fragments}`)
|
||||
}
|
||||
|
||||
log("Write footer")
|
||||
response.write(footer);
|
||||
|
||||
response.finish();
|
||||
}
|
@ -119,3 +119,9 @@ support-files = file_framedhistoryframes.html
|
||||
[test_windowedhistoryframes.html]
|
||||
[test_triggeringprincipal_location_seturi.html]
|
||||
[test_bug1507702.html]
|
||||
[test_double_submit.html]
|
||||
skip-if = fission
|
||||
support-files =
|
||||
clicker.html
|
||||
ping.html
|
||||
double_submit.sjs
|
||||
|
6
docshell/test/mochitest/ping.html
Normal file
6
docshell/test/mochitest/ping.html
Normal file
@ -0,0 +1,6 @@
|
||||
<!doctype html>
|
||||
<script>
|
||||
"use strict";
|
||||
let target = (window.opener || window.parent);
|
||||
target.postMessage("ping", "*");
|
||||
</script>
|
113
docshell/test/mochitest/test_double_submit.html
Normal file
113
docshell/test/mochitest/test_double_submit.html
Normal file
@ -0,0 +1,113 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Bug 1590762</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<iframe name="targetFrame" id="targetFrame"></iframe>
|
||||
<form id="form" action="double_submit.sjs?size=4096&fragments=10&delay=1000" method="POST" target="targetFrame">
|
||||
<input id="input" type="text" name="text" value="value">
|
||||
<input id="token" type="text" name="token" value="">
|
||||
<input id="button" type="submit">
|
||||
</form>
|
||||
<script>
|
||||
"use strict";
|
||||
// For some reason this triggers the (harmless) assert at
|
||||
// https://searchfox.org/mozilla-central/source/layout/generic/nsLineLayout.cpp#958
|
||||
// See bug 1645132.
|
||||
|
||||
SimpleTest.expectAssertions(1)
|
||||
|
||||
const CROSS_ORIGIN_URI = "http://test1.example.com/tests/docshell/test/mochitest/ping.html";
|
||||
|
||||
function generateBody(size) {
|
||||
let data = new Uint8Array(size);
|
||||
for (let i = 0; i < size; ++i) {
|
||||
data[i] = 97 + Math.random() * (123 - 97);
|
||||
}
|
||||
|
||||
return new TextDecoder().decode(data);
|
||||
}
|
||||
|
||||
function asyncClick(counts) {
|
||||
let frame = document.createElement('iframe');
|
||||
frame.addEventListener(
|
||||
'load', () => frame.contentWindow.postMessage({command: "start"}, "*"),
|
||||
{ once:true });
|
||||
frame.src = "clicker.html";
|
||||
|
||||
addEventListener('message', ({source}) => {
|
||||
if (source === frame.contentWindow) {
|
||||
counts.click++;
|
||||
synthesizeMouse(document.getElementById('button'), 5, 5, {});
|
||||
}
|
||||
}, { once: true });
|
||||
|
||||
document.body.appendChild(frame);
|
||||
return stop;
|
||||
}
|
||||
|
||||
function click(button) {
|
||||
synthesizeMouse(button, 5, 5, {});
|
||||
}
|
||||
|
||||
add_task(async function runTest() {
|
||||
let frame = document.getElementById('targetFrame');
|
||||
await new Promise(resolve => {
|
||||
addEventListener('message', resolve, {once: true});
|
||||
frame.src = CROSS_ORIGIN_URI;
|
||||
});
|
||||
|
||||
let form = document.getElementById('form');
|
||||
let button = document.getElementById('button');
|
||||
|
||||
document.getElementById('input').value = generateBody(1014*1024*10);
|
||||
let token = document.getElementById('token');
|
||||
token.value = "first";
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
let counts = { click: 0, submit: 0 };
|
||||
form.addEventListener('submit', () => counts.submit++);
|
||||
asyncClick(counts);
|
||||
form.requestSubmit(button);
|
||||
token.value = "bad";
|
||||
let steps = {
|
||||
good: {
|
||||
entered: false,
|
||||
next: () => { steps.good.entered = true; resolve(); },
|
||||
assertion: () => {
|
||||
ok(steps.first.entered && !steps.bad.entered, "good comes after first, but not bad")
|
||||
}
|
||||
},
|
||||
first: {
|
||||
entered: false,
|
||||
next: () => { steps.first.entered = true; token.value = "good"; click(button); },
|
||||
assertion: () => {
|
||||
ok(!steps.good.entered && !steps.bad.entered, "first message is first")
|
||||
is(counts.click, 1, "clicked");
|
||||
is(counts.submit, 2, "did submit");
|
||||
}
|
||||
},
|
||||
bad: {
|
||||
entered: false,
|
||||
next: () => { reject(); },
|
||||
assertion: () => ok(false, "we got a bad message")
|
||||
}
|
||||
};
|
||||
addEventListener('message', ({source, data}) => {
|
||||
if (source !== frame.contentWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
let step = steps[data] || reject;
|
||||
step.assertion();
|
||||
step.next();
|
||||
})
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user