forked from mirrors/gecko-dev
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
110 lines
2.6 KiB
JavaScript
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();
|
|
}
|