Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 89a0772

Browse files
committed
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 a=smaug
--HG-- extra : source : ea777ccca86cceeb53d2fc7252bea2a3bad3e405
1 parent 8aeb3f9 commit 89a0772

7 files changed

Lines changed: 93 additions & 6 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Send a payload that's over 32k in size, which for a plain response should be
2+
// large enough to ensure that OnDataAvailable is called more than once (and so
3+
// the XHR will be triggered to send more than one "loading" event if desired).
4+
5+
function handleRequest(request, response)
6+
{
7+
let data = (new Array(1 << 18)).join("A");
8+
response.processAsync();
9+
response.setHeader("Content-Type", "text/plain", false);
10+
response.setHeader("Content-Length", "" + data.length, false);
11+
response.write(data, data.length);
12+
response.finish();
13+
}

dom/tests/mochitest/bugs/mochitest.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ support-files =
77
bug346659-parent-echoer.html
88
bug346659-parent.html
99
bug458091_child.html
10+
bug918719.sjs
1011
child_bug260264.html
1112
devicemotion_inner.html
1213
devicemotion_outer.html
@@ -155,6 +156,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
155156
[test_bug857555.html]
156157
[test_bug862540.html]
157158
[test_bug876098.html]
159+
[test_bug918719.html]
158160
[test_bug927901.html]
159161
[test_devicemotion_multiple_listeners.html]
160162
skip-if = toolkit == 'android' #bug 775227
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<!--
4+
https://bugzilla.mozilla.org/show_bug.cgi?id=918719
5+
-->
6+
<head>
7+
<title>Test for Bug 918719</title>
8+
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
9+
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
10+
</head>
11+
<body>
12+
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=918719">Mozilla Bug 918719</a>
13+
<p id="display"></p>
14+
<div id="content" style="display: none">
15+
16+
</div>
17+
<pre id="test">
18+
<script class="testbody" type="text/javascript">
19+
20+
SimpleTest.waitForExplicitFinish();
21+
22+
function runTest() {
23+
return new Promise(function(resolve) {
24+
let loadingEventCount = 0,
25+
xhr = new XMLHttpRequest();
26+
xhr.open("GET", "bug918719.sjs");
27+
xhr.onreadystatechange = function() {
28+
if (xhr.readyState == 3) {
29+
loadingEventCount++;
30+
} else if (xhr.readyState == 4) {
31+
resolve(loadingEventCount);
32+
}
33+
}
34+
xhr.send();
35+
});
36+
}
37+
38+
function prefChangePromise(args) {
39+
return new Promise(function(resolve) {
40+
SpecialPowers.pushPrefEnv(args, resolve);
41+
});
42+
}
43+
44+
runTest().then(function(count) {
45+
ok(count === 1, "Only one loading readystatechange event should have been fired with the pref off.");
46+
}).then(function() {
47+
return prefChangePromise({"set": [["dom.fire_extra_xhr_loading_readystatechanges", true]]});
48+
}).then(function() {
49+
return runTest();
50+
}).then(function(count) {
51+
// Note this case is tricky to test, as there is no hard guarantee that the
52+
// server will send the data in more than one chunk, even with the trick
53+
// we're using in the sjs. As such this case *may* intermittently fail.
54+
ok(count > 1, "Multiple loading readystatechange events should have been fired with the pref on.");
55+
SimpleTest.finish();
56+
});
57+
58+
</script>
59+
</pre>
60+
</body>
61+
</html>
62+

dom/xhr/XMLHttpRequestMainThread.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ XMLHttpRequestMainThread::XMLHttpRequestMainThread()
169169
mFlagSyncLooping(false), mFlagBackgroundRequest(false),
170170
mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
171171
mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
172+
mSendExtraLoadingEvents(Preferences::GetBool("dom.fire_extra_xhr_loading_readystatechanges", true)),
172173
mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
173174
mProgressSinceLastProgressEvent(false),
174175
mRequestSentTime(0), mTimeoutMilliseconds(0),
@@ -1719,7 +1720,9 @@ XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
17191720

17201721
mDataAvailable += totalRead;
17211722

1722-
ChangeState(State::loading);
1723+
if (mState == State::headers_received || mSendExtraLoadingEvents) {
1724+
ChangeState(State::loading);
1725+
}
17231726

17241727
if (!mFlagSynchronous && !mProgressTimerIsActive) {
17251728
StartProgressEventTimer();

dom/xhr/XMLHttpRequestMainThread.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,14 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
696696
// late, and ensure the XHR only handles one in-flight request at once.
697697
bool mFlagSend;
698698

699+
// Before ProgressEvents were a thing, multiple readystatechange events were
700+
// fired during the loading state to give sites a way to monitor XHR progress.
701+
// The XHR spec now has proper progress events and dictates that only one
702+
// "loading" readystatechange should be fired per send. However, it's possible
703+
// that some content still relies on this old behavior, so we're keeping it
704+
// (behind a preference) for now. See bug 918719.
705+
bool mSendExtraLoadingEvents;
706+
699707
RefPtr<XMLHttpRequestUpload> mUpload;
700708
int64_t mUploadTransferred;
701709
int64_t mUploadTotal;

modules/libpref/init/all.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5080,6 +5080,10 @@ pref("dom.voicemail.defaultServiceId", 0);
50805080
// Enable mapped array buffer by default.
50815081
pref("dom.mapped_arraybuffer.enabled", true);
50825082

5083+
// Whether to send more than one "loading" readystatechange during XHRs to
5084+
// simulate progress events for sites still not using modern progress events.
5085+
pref("dom.fire_extra_xhr_loading_readystatechanges", false);
5086+
50835087
// The tables used for Safebrowsing phishing and malware checks.
50845088
pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,test-malware-simple,test-unwanted-simple");
50855089

testing/web-platform/meta/XMLHttpRequest/event-readystatechange-loaded.htm.ini

Lines changed: 0 additions & 5 deletions
This file was deleted.

0 commit comments

Comments
 (0)