fune/docshell/test/mochitest/file_bug1747033.sjs
Peter Van der Beken 61f7e59993 Bug 1747033 - "Back" does not work correctly for pages with multipart/x-mixed-replace and history.replaceState. r=smaug,necko-reviewers,valentin
multipart/x-mixed-replace loads don't start a new load for parts other than the first,
they just call OnStartRequest/OnStopRequest for every part. The nsDocShell code was
trying to set the active entry to the loading entry for these loads, but because we
never started a new load after the first part, the loading entry would be null and we'd
just clear the active entry. history.replaceState would then try to replace the active
entry, but finding none it would just add a new one.

Differential Revision: https://phabricator.services.mozilla.com/D138464
2022-02-19 08:30:35 +00:00

110 lines
2.6 KiB
JavaScript

"use strict";
const BOUNDARY = "BOUNDARY";
// waitForPageShow should be false if this is for multipart/x-mixed-replace
// and it's not the last part, Gecko doesn't fire pageshow for those parts.
function documentString(waitForPageShow = true) {
return `<html>
<head>
<script>
let bc = new BroadcastChannel("bug1747033");
bc.addEventListener("message", ({ data: { cmd, arg = undefined } }) => {
switch (cmd) {
case "load":
location.href = arg;
break;
case "replaceState":
history.replaceState({}, "Replaced state", arg);
bc.postMessage({ "historyLength": history.length, "location": location.href });
break;
case "back":
history.back();
break;
case "close":
close();
break;
}
});
function reply() {
bc.postMessage({ "historyLength": history.length, "location": location.href });
}
${waitForPageShow ? `addEventListener("pageshow", reply);` : "reply();"}
</script>
</head>
<body></body>
</html>
`;
}
function boundary(last = false) {
let b = `--${BOUNDARY}`;
if (last) {
b += "--";
}
return b + "\n";
}
function sendMultipart(response, last = false) {
setState("sendMore", "");
response.write(`Content-Type: text/html
${documentString(last)}
`);
response.write(boundary(last));
}
function shouldSendMore() {
return new Promise(resolve => {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(
() => {
let sendMore = getState("sendMore");
if (sendMore !== "") {
timer.cancel();
resolve(sendMore);
}
},
100,
Ci.nsITimer.TYPE_REPEATING_SLACK
);
});
}
async function handleRequest(request, response) {
if (request.queryString == "") {
// This is for non-multipart/x-mixed-replace loads.
response.write(documentString());
return;
}
if (request.queryString == "sendNextPart") {
setState("sendMore", "next");
return;
}
if (request.queryString == "sendLastPart") {
setState("sendMore", "last");
return;
}
response.processAsync();
response.setHeader(
"Content-Type",
`multipart/x-mixed-replace; boundary=${BOUNDARY}`,
false
);
response.setStatusLine(request.httpVersion, 200, "OK");
response.write(boundary());
sendMultipart(response);
while ((await shouldSendMore("sendMore")) !== "last") {
sendMultipart(response);
}
sendMultipart(response, true);
response.finish();
}