Bug 1319744 - Ensure that progress events and corresponding LOADING readystatechanges fire as per spec. r=baku

This commit is contained in:
Thomas Wisniewski 2016-12-13 15:23:01 -05:00
parent cefb75a8ec
commit 54ad3187aa
5 changed files with 41 additions and 61 deletions

View File

@ -15,8 +15,8 @@ xhr.onloadend = function(e) {
xhr.onprogress = function(e) {
if (e.loaded > 0) {
progressFired = true;
xhr.abort();
}
xhr.abort();
};
onmessage = function(e) {

View File

@ -107,6 +107,7 @@ support-files =
script_createFile.js
worker_suspended.js
window_suspended.html
!/dom/base/test/file_bug945152.jar
!/dom/base/test/file_websocket_basic_wsh.py
!/dom/base/test/file_websocket_hello_wsh.py
!/dom/base/test/file_websocket_http_resource.txt

View File

@ -673,7 +673,7 @@ XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv)
// Use progress info to determine whether load is complete, but use
// mDataAvailable to ensure a slice is created based on the uncompressed
// data count.
if (mLoadTotal == mLoadTransferred) {
if (mState == State::done) {
mResponseBlob = mDOMBlob;
} else {
mResponseBlob = mDOMBlob->CreateSlice(0, mDataAvailable,
@ -688,7 +688,7 @@ XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv)
}
nsAutoCString contentType;
if (mLoadTotal == mLoadTransferred) {
if (mState == State::done) {
mChannel->GetContentType(contentType);
}
@ -1790,7 +1790,15 @@ XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
mDataAvailable += totalRead;
ChangeState(State::loading);
// Fire the first progress event/loading state change
if (mState != State::loading) {
ChangeState(State::loading);
if (!mFlagSynchronous) {
DispatchProgressEvent(this, ProgressEventType::progress,
mLoadTransferred, mLoadTotal);
}
mProgressSinceLastProgressEvent = false;
}
if (!mFlagSynchronous && !mProgressTimerIsActive) {
StartProgressEventTimer();
@ -2098,10 +2106,6 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
bool waitingForBlobCreation = false;
if (NS_SUCCEEDED(status) &&
(mResponseType == XMLHttpRequestResponseType::_empty ||
mResponseType == XMLHttpRequestResponseType::Text)) {
mLoadTotal = mResponseBody.Length();
} else if (NS_SUCCEEDED(status) &&
(mResponseType == XMLHttpRequestResponseType::Blob ||
mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
ErrorResult rv;
@ -2111,11 +2115,6 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
if (mDOMBlob) {
mResponseBlob = mDOMBlob;
mDOMBlob = nullptr;
mLoadTotal = mResponseBlob->GetSize(rv);
if (NS_WARN_IF(rv.Failed())) {
status = rv.StealNSResult();
}
} else {
// Smaller files may be written in cache map instead of separate files.
// Also, no-store response cannot be written in persistent cache.
@ -2126,8 +2125,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
// mBlobStorage can be null if the channel is non-file non-cacheable
// and if the response length is zero.
MaybeCreateBlobStorage();
mLoadTotal =
mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
waitingForBlobCreation = true;
} else {
// mBlobSet can be null if the channel is non-file non-cacheable
@ -2148,10 +2146,6 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
}
mResponseBlob = Blob::Create(GetOwner(), blobImpl);
mLoadTotal = mResponseBlob->GetSize(rv);
if (NS_WARN_IF(rv.Failed())) {
status = rv.StealNSResult();
}
}
}
@ -2163,8 +2157,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
// set the capacity down to the actual length, to realloc back
// down to the actual size
mLoadTotal = mArrayBufferBuilder.length();
if (!mArrayBufferBuilder.setCapacity(mLoadTotal)) {
if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
// this should never happen!
status = NS_ERROR_UNEXPECTED;
}
@ -2268,12 +2261,6 @@ XMLHttpRequestMainThread::ChangeStateToDone()
mTimeoutTimer->Cancel();
}
if (mLoadTransferred) {
mLoadTotal = mLoadTransferred;
} else {
mLoadTotal = -1;
}
// Per spec, fire the last download progress event, if any,
// before readystatechange=4/done. (Note that 0-sized responses
// will have not sent a progress event yet, so one must be sent here).
@ -3412,9 +3399,8 @@ XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext
StartProgressEventTimer();
}
} else {
mLoadTotal = lengthComputable ? aProgressMax : -1;
mLoadTotal = aProgressMax;
mLoadTransferred = aProgress;
// OnDataAvailable() handles mProgressSinceLastProgressEvent
// for the download phase.
}
@ -3631,6 +3617,7 @@ XMLHttpRequestMainThread::HandleProgressTimerCallback()
mUploadTransferred, mUploadTotal);
}
} else {
FireReadystatechangeEvent();
DispatchProgressEvent(this, ProgressEventType::progress,
mLoadTransferred, mLoadTotal);
}
@ -3809,12 +3796,6 @@ XMLHttpRequestMainThread::BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
mResponseBlob = aBlob;
mBlobStorage = nullptr;
ErrorResult rv;
mLoadTotal = mResponseBlob->GetSize(rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
ChangeStateToDone();
}

View File

@ -1,5 +0,0 @@
[firing-events-http-no-content-length.html]
type: testharness
[ProgressEvent: firing events for HTTP with no Content-Length]
expected: FAIL

View File

@ -22,53 +22,56 @@
}
function getNextEvent(arr) {
var eventStr = arr.shift();
var event = { str: arr.shift() };
// we can only handle strings, numbers (readystates) and undefined
if (eventStr === undefined) {
if (event.str === undefined) {
return event;
}
if (typeof eventStr !== "string") {
if (Number.isInteger(eventStr)) {
eventStr = "readystatechange(" + eventStr + ")";
if (typeof event.str !== "string") {
if (Number.isInteger(event.str)) {
event.state = event.str;
event.str = "readystatechange(" + event.str + ")";
} else {
throw "Test error: unexpected event type " + eventStr;
throw "Test error: unexpected event type " + event.str;
}
}
// parse out the general type, loaded and total values
var type = eventStr.type = eventStr.split("(")[0].split(".").pop();
eventStr.mayFollowOptionalProgressEvents = type == "progress" ||
type == "load" || type == "abort" || type == "error";
var loadedAndTotal = eventStr.match(/\((\d)+,(\d)+/);
var type = event.type = event.str.split("(")[0].split(".").pop();
var loadedAndTotal = event.str.match(/.*\((\d+),(\d+),(true|false)\)/);
if (loadedAndTotal) {
eventStr.loaded = parseInt(loadedAndTotal[0]);
eventStr.total = parseInt(loadedAndTotal[1]);
event.loaded = parseInt(loadedAndTotal[1]);
event.total = parseInt(loadedAndTotal[2]);
event.lengthComputable = loadedAndTotal[3] == "true";
}
return eventStr;
return event;
}
global.assert_xhr_event_order_matches = function(expected) {
var recorded = recorded_xhr_events;
var lastRecordedLoaded = -1;
while(expected.length && recorded.length) {
var currentExpected = getNextEvent(expected),
currentRecorded = getNextEvent(recorded);
// skip to the last progress event if we've hit one
while (recorded.length && currentRecorded.type == "progress") {
assert_greater(currentRecorded.loaded, lastRecordedLoaded,
"progress event 'loaded' values must only increase");
// skip to the last progress event if we've hit one (note the next
// event after a progress event should be a LOADING readystatechange,
// if there are multiple progress events in a row).
while (recorded.length && currentRecorded.type == "progress" &&
parseInt(recorded) === 3) {
assert_greater_than(currentRecorded.loaded, lastRecordedLoaded,
"progress event 'loaded' values must only increase");
lastRecordedLoaded = currentRecorded.loaded;
currentRecorded = getNextEvent(recorded);
}
if (currentRecorded.type == "loadstart") {
if (currentRecorded.type == "loadend") {
recordedProgressCount = 0;
lastRecordedLoaded = -1;
}
assert_equals(currentRecorded, currentExpected);
assert_equals(currentRecorded.str, currentExpected.str);
}
if (recorded.length) {
throw "\nUnexpected extra events: " + recorded.join(", ");