Bug 1311798 - Align XMLHttpRequest.abort() with the spec. r=baku
☠☠ backed out by cf7e3bcf119a ☠ ☠
authorThomas Wisniewski <twisniewski@mozilla.com>
Sun, 20 Nov 2016 09:32:00 -0500
changeset 323561 3421c306d8cf844b149594517340275173a5460f
parent 323560 c788e6d12d93cf74519858f92656119218596a38
child 323562 cf7e3bcf119a09e85bf221de52b9e84016c0e0d1
push id21
push usermaklebus@msu.edu
push dateThu, 01 Dec 2016 06:22:08 +0000
reviewersbaku
bugs1311798
milestone53.0a1
Bug 1311798 - Align XMLHttpRequest.abort() with the spec. r=baku
dom/xhr/XMLHttpRequestMainThread.cpp
dom/xhr/XMLHttpRequestMainThread.h
dom/xhr/XMLHttpRequestWorker.cpp
dom/xhr/XMLHttpRequestWorker.h
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -1059,20 +1059,85 @@ XMLHttpRequestMainThread::CloseRequestWi
   if (mFlagAborted) {
     ChangeState(State::unsent, false);  // IE seems to do it
   }
 
   mFlagSyncLooping = false;
 }
 
 void
-XMLHttpRequestMainThread::Abort(ErrorResult& arv)
+XMLHttpRequestMainThread::RequestErrorSteps(const ProgressEventType aEventType,
+                                            const nsresult aOptionalException,
+                                            ErrorResult& aRv)
+{
+  // Step 1
+  mState = State::done;
+
+  StopProgressEventTimer();
+
+  // Step 2
+  mFlagSend = false;
+
+  // Step 3
+  ResetResponse();
+
+  // If we're in the destructor, don't risk dispatching an event.
+  if (mFlagDeleted) {
+    mFlagSyncLooping = false;
+    return;
+  }
+
+  // Step 4
+  if (mFlagSynchronous && NS_FAILED(aOptionalException)) {
+    aRv.Throw(aOptionalException);
+    return;
+  }
+
+  // Step 5
+  FireReadystatechangeEvent();
+
+  // Step 6
+  if (mUpload && !mUploadComplete) {
+
+    // Step 6-1
+    mUploadComplete = true;
+
+    // Step 6-2
+    if (mFlagHadUploadListenersOnSend) {
+
+      // Steps 6-3, 6-4 (loadend is fired for us)
+      DispatchProgressEvent(mUpload, aEventType, 0, -1);
+    }
+  }
+
+  // Steps 7 and 8 (loadend is fired for us)
+  DispatchProgressEvent(this, aEventType, 0, -1);
+}
+
+void
+XMLHttpRequestMainThread::Abort(ErrorResult& aRv)
 {
   mFlagAborted = true;
-  CloseRequestWithError(ProgressEventType::abort);
+
+  // Step 1
+  CloseRequest();
+
+  // Step 2
+  if ((mState == State::opened && mFlagSend) ||
+       mState == State::headers_received ||
+       mState == State::loading) {
+    RequestErrorSteps(ProgressEventType::abort, NS_OK, aRv);
+  }
+
+  // Step 3
+  if (mState == State::done) {
+    ChangeState(State::unsent, false); // no ReadystateChange event
+  }
+
+  mFlagSyncLooping = false;
 }
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::SlowAbort()
 {
   Abort();
   return NS_OK;
 }
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -391,16 +391,21 @@ public:
   Send(JSContext* aCx, nsIInputStream* aStream, ErrorResult& aRv) override
   {
     NS_ASSERTION(aStream, "Null should go to string version");
     RequestBody<nsIInputStream> body(aStream);
     aRv = SendInternal(&body);
   }
 
   void
+  RequestErrorSteps(const ProgressEventType aEventType,
+                    const nsresult aOptionalException,
+                    ErrorResult& aRv);
+
+  void
   Abort() {
     ErrorResult rv;
     Abort(rv);
     MOZ_ASSERT(!rv.Failed());
   }
 
   virtual void
   Abort(ErrorResult& aRv) override;
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -1664,17 +1664,19 @@ XMLHttpRequestWorker::MaybePin(ErrorResu
 void
 XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mProxy);
 
   // Only send readystatechange event when state changed.
   bool isStateChanged = false;
-  if (mStateData.mReadyState != 4) {
+  if ((mStateData.mReadyState == 1 && mStateData.mFlagSend) ||
+      mStateData.mReadyState == 2 ||
+      mStateData.mReadyState == 3) {
     isStateChanged = true;
     mStateData.mReadyState = 4;
   }
 
   if (mProxy->mSeenUploadLoadStart) {
     MOZ_ASSERT(mUpload);
 
     DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true,
@@ -1806,16 +1808,18 @@ XMLHttpRequestWorker::SendInternal(SendR
     syncLoopTarget = autoSyncLoop->EventTarget();
   }
 
   mProxy->mOuterChannelId++;
 
   aRunnable->SetSyncLoopTarget(syncLoopTarget);
   aRunnable->SetHaveUploadListeners(hasUploadListeners);
 
+  mStateData.mFlagSend = true;
+
   aRunnable->Dispatch(aRv);
   if (aRv.Failed()) {
     // Dispatch() may have spun the event loop and we may have already unrooted.
     // If so we don't want autoUnpin to try again.
     if (!mRooted) {
       autoUnpin.Clear();
     }
     return;
@@ -1832,16 +1836,17 @@ XMLHttpRequestWorker::SendInternal(SendR
   // Don't clobber an existing exception that we may have thrown on aRv
   // already... though can there really be one?  In any case, it seems to me
   // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call
   // for it will come from ProxyCompleteRunnable and that always passes true for
   // the second arg.
   if (!autoSyncLoop->Run() && !aRv.Failed()) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
+  mStateData.mFlagSend = false;
 }
 
 bool
 XMLHttpRequestWorker::Notify(Status aStatus)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (aStatus >= Canceling && !mCanceled) {
--- a/dom/xhr/XMLHttpRequestWorker.h
+++ b/dom/xhr/XMLHttpRequestWorker.h
@@ -28,25 +28,26 @@ class XMLHttpRequestWorker final : publi
 public:
   struct StateData
   {
     XMLHttpRequestStringSnapshot mResponseText;
     nsString mResponseURL;
     uint32_t mStatus;
     nsCString mStatusText;
     uint16_t mReadyState;
+    bool mFlagSend;
     JS::Heap<JS::Value> mResponse;
     nsresult mResponseTextResult;
     nsresult mStatusResult;
     nsresult mResponseResult;
 
     StateData()
-    : mStatus(0), mReadyState(0), mResponse(JS::UndefinedValue()),
-      mResponseTextResult(NS_OK), mStatusResult(NS_OK),
-      mResponseResult(NS_OK)
+    : mStatus(0), mReadyState(0), mFlagSend(false),
+      mResponse(JS::UndefinedValue()), mResponseTextResult(NS_OK),
+      mStatusResult(NS_OK), mResponseResult(NS_OK)
     { }
 
     void trace(JSTracer* trc);
   };
 
 private:
   RefPtr<XMLHttpRequestUpload> mUpload;
   workers::WorkerPrivate* mWorkerPrivate;