Bug 678057 - 'Workers: overrideMimeType() removed from XHR, breaks web content, diverges from main-thread XHR'. r=sicking.
authorBen Turner <bent.mozilla@gmail.com>
Sat, 13 Aug 2011 12:43:51 -0700
changeset 75286 c5550a4ff624136d7f765fe8875571eb7472fc8a
parent 75285 9bcd224e78566cbf782c732322a4ff0b60c8379e
child 75287 c0b90308926a9107a3a9732e696d7a0e554a6e9e
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewerssicking
bugs678057
milestone8.0a1
Bug 678057 - 'Workers: overrideMimeType() removed from XHR, breaks web content, diverges from main-thread XHR'. r=sicking.
dom/workers/XMLHttpRequest.cpp
dom/workers/XMLHttpRequestPrivate.cpp
dom/workers/XMLHttpRequestPrivate.h
dom/workers/test/xhrAbort_worker.js
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -254,17 +254,16 @@ class XMLHttpRequest
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
   static JSPropertySpec sStaticProperties[];
 
   enum SLOT {
     SLOT_channel = 0,
     SLOT_responseXML,
     SLOT_responseText,
-    SLOT_mozResponseArrayBuffer,
     SLOT_status,
     SLOT_statusText,
     SLOT_readyState,
     SLOT_multipart,
     SLOT_mozBackgroundRequest,
     SLOT_withCredentials,
     SLOT_upload,
 
@@ -384,18 +383,16 @@ private:
     }
 
     jsval emptyString = JS_GetEmptyStringValue(aCx);
     jsval zero = INT_TO_JSVAL(0);
 
     if (!JS_SetReservedSlot(aCx, obj, SLOT_channel, JSVAL_NULL) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_responseXML, JSVAL_NULL) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_responseText, emptyString) ||
-        !JS_SetReservedSlot(aCx, obj, SLOT_mozResponseArrayBuffer,
-                            JSVAL_NULL) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_status, zero) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_statusText, emptyString) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_readyState, zero) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_multipart, JSVAL_FALSE) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_mozBackgroundRequest, JSVAL_FALSE) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_withCredentials, JSVAL_FALSE) ||
         !JS_SetReservedSlot(aCx, obj, SLOT_upload, JSVAL_NULL)) {
       return false;
@@ -727,16 +724,35 @@ private:
     JSString* header, *value;
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "SS", &header,
                              &value)) {
       return false;
     }
 
     return priv->SetRequestHeader(aCx, header, value);
   }
+
+  static JSBool
+  OverrideMimeType(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    XMLHttpRequestPrivate* priv =
+      GetInstancePrivate(aCx, obj, sFunctions[7].name);
+    if (!priv) {
+      return false;
+    }
+
+    JSString* mimeType;
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "S", &mimeType)) {
+      return false;
+    }
+
+    return priv->OverrideMimeType(aCx, mimeType);
+  }
 };
 
 JSClass XMLHttpRequest::sClass = {
   "XMLHttpRequest",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
   NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL
@@ -746,17 +762,16 @@ JSPropertySpec XMLHttpRequest::sProperti
 
 #define GENERIC_READONLY_PROPERTY(_name) \
   { #_name, SLOT_##_name, PROPERTY_FLAGS, GetProperty, \
     js_GetterOnlyPropertyStub },
 
   GENERIC_READONLY_PROPERTY(channel)
   GENERIC_READONLY_PROPERTY(responseXML)
   GENERIC_READONLY_PROPERTY(responseText)
-  GENERIC_READONLY_PROPERTY(mozResponseArrayBuffer)
   GENERIC_READONLY_PROPERTY(status)
   GENERIC_READONLY_PROPERTY(statusText)
   GENERIC_READONLY_PROPERTY(readyState)
 
   { "multipart", 7, PROPERTY_FLAGS, GetProperty, SetMultipart },
   { "mozBackgroundRequest", 8, PROPERTY_FLAGS, GetProperty,
     SetMozBackgroundRequest },
   { "withCredentials", 9, PROPERTY_FLAGS, GetProperty, SetWithCredentials },
@@ -785,16 +800,17 @@ JSPropertySpec XMLHttpRequest::sProperti
 JSFunctionSpec XMLHttpRequest::sFunctions[] = {
   JS_FN("abort", Abort, 0, FUNCTION_FLAGS),
   JS_FN("getAllResponseHeaders", GetAllResponseHeaders, 0, FUNCTION_FLAGS),
   JS_FN("getResponseHeader", GetResponseHeader, 1, FUNCTION_FLAGS),
   JS_FN("open", Open, 2, FUNCTION_FLAGS),
   JS_FN("send", Send, 0, FUNCTION_FLAGS),
   JS_FN("sendAsBinary", SendAsBinary, 1, FUNCTION_FLAGS),
   JS_FN("setRequestHeader", SetRequestHeader, 2, FUNCTION_FLAGS),
+  JS_FN("overrideMimeType", OverrideMimeType, 1, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 JSPropertySpec XMLHttpRequest::sStaticProperties[] = {
   { "UNSENT", UNSENT, CONSTANT_FLAGS, GetConstant, NULL },
   { "OPENED", OPENED, CONSTANT_FLAGS, GetConstant, NULL },
   { "HEADERS_RECEIVED", HEADERS_RECEIVED, CONSTANT_FLAGS, GetConstant, NULL },
   { "LOADING", LOADING, CONSTANT_FLAGS, GetConstant, NULL },
--- a/dom/workers/XMLHttpRequestPrivate.cpp
+++ b/dom/workers/XMLHttpRequestPrivate.cpp
@@ -1051,16 +1051,34 @@ public:
   intN
   MainThreadRun()
   {
     nsresult rv = mProxy->mXHR->SetRequestHeader(mHeader, mValue);
     return GetDOMExceptionCodeFromResult(rv);
   }
 };
 
+class OverrideMimeTypeRunnable : public WorkerThreadProxySyncRunnable
+{
+  nsCString mMimeType;
+
+public:
+  OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
+                           const nsCString& aMimeType)
+  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMimeType(aMimeType)
+  { }
+
+  intN
+  MainThreadRun()
+  {
+    nsresult rv = mProxy->mXHR->OverrideMimeType(mMimeType);
+    return GetDOMExceptionCodeFromResult(rv);
+  }
+};
+
 } // anonymous namespace
 
 void
 Proxy::Teardown()
 {
   AssertIsOnMainThread();
 
   if (mXHR) {
@@ -1620,16 +1638,46 @@ XMLHttpRequestPrivate::SetRequestHeader(
   nsRefPtr<SetRequestHeaderRunnable> runnable =
     new SetRequestHeaderRunnable(mWorkerPrivate, mProxy,
                                  NS_ConvertUTF16toUTF8(header),
                                  NS_ConvertUTF16toUTF8(value));
   return runnable->Dispatch(aCx);
 }
 
 bool
+XMLHttpRequestPrivate::OverrideMimeType(JSContext* aCx, JSString* aMimeType)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  if (mCanceled) {
+    return false;
+  }
+
+  // We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
+  // can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
+  // non-racy way until the XHR state machine actually runs on this thread
+  // (bug 671047). For now we're going to let this work only if the Send()
+  // method has not been called.
+  if (!mProxy || SendInProgress()) {
+    ThrowDOMExceptionForCode(aCx, INVALID_STATE_ERR);
+    return false;
+  }
+
+  nsDependentJSString mimeType;
+  if (!mimeType.init(aCx, aMimeType)) {
+    return false;
+  }
+
+  nsRefPtr<OverrideMimeTypeRunnable> runnable =
+    new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, 
+                                 NS_ConvertUTF16toUTF8(mimeType));
+  return runnable->Dispatch(aCx);
+}
+
+bool
 XMLHttpRequestPrivate::MaybeDispatchPrematureAbortEvents(JSContext* aCx,
                                                          bool aFromOpen)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   NS_ASSERTION(mProxy, "Must have a proxy here!");
 
   xhr::StateData state = {
     JSVAL_VOID, JSVAL_VOID, JSVAL_VOID, INT_TO_JSVAL(4),
--- a/dom/workers/XMLHttpRequestPrivate.h
+++ b/dom/workers/XMLHttpRequestPrivate.h
@@ -136,28 +136,37 @@ public:
   Send(JSContext* aCx, bool aHasBody, jsval aBody);
 
   bool
   SendAsBinary(JSContext* aCx, JSString* aBody);
 
   bool
   SetRequestHeader(JSContext* aCx, JSString* aHeader, JSString* aValue);
 
+  bool
+  OverrideMimeType(JSContext* aCx, JSString* aMimeType);
+
 private:
   void
   ReleaseProxy();
 
   bool
   RootJSObject(JSContext* aCx);
 
   bool
   MaybeDispatchPrematureAbortEvents(JSContext* aCx, bool aFromOpen);
 
   bool
   DispatchPrematureAbortEvent(JSContext* aCx, JSObject* aTarget,
                               PRUint64 aEventType, bool aUploadTarget);
+
+  bool
+  SendInProgress() const
+  {
+    return mJSObjectRootCount != 0;
+  }
 };
 
 }  // namespace xhr
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_xmlhttprequestprivate_h__
--- a/dom/workers/test/xhrAbort_worker.js
+++ b/dom/workers/test/xhrAbort_worker.js
@@ -70,16 +70,17 @@ function runTest() {
   xhr.onreadystatechange = function(event) {
     pushEvent(event);
     if (++count == 3) {
       xhr.abort();
     }
   };
 
   xhr.open("GET", "testXHR.txt");
+  xhr.overrideMimeType("text/plain");
   xhr.send(null);
 }
 
 function messageListener(event) {
   switch (event.data) {
     case "start":
       runTest();
       break;