Bug 918719 - Only fire one loading readystatechange per XHR, but keep the old behavior available behind the preference dom.send_multiple_xhr_loading_readystatechanges. r=smaug

--HG--
extra : rebase_source : 296943a67ae8bd3065749cc424a4379320169585
This commit is contained in:
Thomas Wisniewski 2016-09-12 22:39:01 -04:00
parent 4dd22032fa
commit 3cf91c9343
7 changed files with 154 additions and 6 deletions

View File

@ -0,0 +1,47 @@
// Keep track of one in-flight XHR by allowing secondary ones to
// tell us when the client has received a Progress event and wants
// more data or to stop the in-flight XHR.
const STATE_NAME = "bug918719_loading_event_test";
const CHUNK_DATA = "chunk";
function setReq(req) {
setObjectState(STATE_NAME, req);
}
function getReq() {
let req;
getObjectState(STATE_NAME, function(v) {
req = v;
});
return req;
}
function handleRequest(request, response)
{
var pairs = request.queryString.split("&");
var command = pairs.shift();
response.setHeader("Content-Type", "text/plain");
response.setHeader("Cache-Control", "no-cache", false);
switch(command) {
case "more":
getReq().write(CHUNK_DATA);
break;
case "done":
getReq().finish();
setReq(null);
break;
default:
response.processAsync();
response.write(CHUNK_DATA);
setReq(response);
return;
}
response.setHeader("Content-Length", "2", false);
response.write("ok");
}

View File

@ -7,6 +7,7 @@ support-files =
bug346659-parent-echoer.html
bug346659-parent.html
bug458091_child.html
bug918719.sjs
child_bug260264.html
devicemotion_inner.html
devicemotion_outer.html
@ -155,6 +156,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
[test_bug857555.html]
[test_bug862540.html]
[test_bug876098.html]
[test_bug918719.html]
[test_bug927901.html]
[test_devicemotion_multiple_listeners.html]
skip-if = toolkit == 'android' #bug 775227

View File

@ -0,0 +1,89 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=918719
-->
<head>
<title>Test for Bug 918719</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=918719">Mozilla Bug 918719</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
const SERVER_URL = "bug918719.sjs";
function sendCommand(cmd) {
let xhr = new XMLHttpRequest();
xhr.open("get", SERVER_URL + "?" + cmd);
xhr.send();
}
function runTest() {
// Manipulate one in-flight XHR using secondary command XHRs, to guarantee
// that multiple OnDataAvailable events are triggered (which are where
// LOADING readystatechanges are triggered). We return a promise that will
// resolve with a count of the number of LOADING events that were detected.
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
let numProgressEvents = 0;
let numLoadingEvents = 0;
xhr.onreadystatechange = e => {
if (xhr.readyState === xhr.LOADING) {
++numLoadingEvents;
}
};
xhr.onprogress = e => {
if (++numProgressEvents < 2) {
sendCommand("more");
} else {
sendCommand("done");
}
};
xhr.onerror = e => {
reject(e);
};
xhr.onloadend = e => {
resolve(numLoadingEvents);
};
xhr.open("GET", SERVER_URL);
xhr.send();
});
}
function prefChangePromise(args) {
return new Promise(function(resolve) {
SpecialPowers.pushPrefEnv(args, resolve);
});
}
runTest().then(function(count) {
ok(count === 1, "Only one loading readystatechange event should have been fired with the pref off.");
}).then(function() {
return prefChangePromise({"set": [["dom.fire_extra_xhr_loading_readystatechanges", true]]});
}).then(function() {
return runTest();
}).then(function(count) {
ok(count > 1, "Multiple loading readystatechange events should have been fired with the pref on.");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@ -169,6 +169,7 @@ XMLHttpRequestMainThread::XMLHttpRequestMainThread()
mFlagSyncLooping(false), mFlagBackgroundRequest(false),
mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
mSendExtraLoadingEvents(Preferences::GetBool("dom.fire_extra_xhr_loading_readystatechanges", true)),
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
mProgressSinceLastProgressEvent(false),
mRequestSentTime(0), mTimeoutMilliseconds(0),
@ -1731,7 +1732,9 @@ XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
mDataAvailable += totalRead;
ChangeState(State::loading);
if (mState == State::headers_received || mSendExtraLoadingEvents) {
ChangeState(State::loading);
}
if (!mFlagSynchronous && !mProgressTimerIsActive) {
StartProgressEventTimer();

View File

@ -703,6 +703,14 @@ protected:
// late, and ensure the XHR only handles one in-flight request at once.
bool mFlagSend;
// Before ProgressEvents were a thing, multiple readystatechange events were
// fired during the loading state to give sites a way to monitor XHR progress.
// The XHR spec now has proper progress events and dictates that only one
// "loading" readystatechange should be fired per send. However, it's possible
// that some content still relies on this old behavior, so we're keeping it
// (behind a preference) for now. See bug 918719.
bool mSendExtraLoadingEvents;
RefPtr<XMLHttpRequestUpload> mUpload;
int64_t mUploadTransferred;
int64_t mUploadTotal;

View File

@ -5076,6 +5076,10 @@ pref("dom.voicemail.defaultServiceId", 0);
// Enable mapped array buffer by default.
pref("dom.mapped_arraybuffer.enabled", true);
// Whether to send more than one "loading" readystatechange during XHRs to
// simulate progress events for sites still not using modern progress events.
pref("dom.fire_extra_xhr_loading_readystatechanges", false);
// The tables used for Safebrowsing phishing and malware checks.
pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,test-malware-simple,test-unwanted-simple");

View File

@ -1,5 +0,0 @@
[event-readystatechange-loaded.htm]
type: testharness
[XMLHttpRequest: the LOADING state change should only happen once]
expected: FAIL