Bug 458324 - 'Workers: Don't attach upload listeners to real XHR object until the user does (to avoid XS security behavior changes)'. r+sr=sicking.
authorBen Turner <bent.mozilla@gmail.com>
Tue, 14 Oct 2008 11:16:37 -0700
changeset 20476 240adb221de097d99d827a53d463cf1e2de76aa4
parent 20475 5f4b2c8f89cc83c26995a6e25d5bfbcf0a91aaf4
child 20477 e38f0e77d4375c5a3f35d8278ae42d53ae6d20d2
push id2894
push userbturner@mozilla.com
push dateTue, 14 Oct 2008 18:19:37 +0000
treeherderautoland@a043e83d291f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs458324
milestone1.9.1b2pre
Bug 458324 - 'Workers: Don't attach upload listeners to real XHR object until the user does (to avoid XS security behavior changes)'. r+sr=sicking.
dom/src/threads/nsDOMWorkerXHRProxy.cpp
dom/src/threads/nsDOMWorkerXHRProxy.h
--- a/dom/src/threads/nsDOMWorkerXHRProxy.cpp
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.cpp
@@ -436,24 +436,55 @@ public:
 
 private:
   nsCOMPtr<nsIDOMEventListener> mInner;
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDOMWorkerXHRWrappedListener,
                               nsIDOMEventListener)
 
+class nsDOMWorkerXHRAttachUploadListenersRunnable : public nsRunnable
+{
+public:
+  nsDOMWorkerXHRAttachUploadListenersRunnable(nsDOMWorkerXHRProxy* aProxy)
+  : mProxy(aProxy) {
+    NS_ASSERTION(aProxy, "Null pointer!");
+  }
+
+  NS_IMETHOD Run() {
+    if (!mProxy->mOwnedByXHR) {
+      NS_ASSERTION(mProxy->mWantUploadListeners, "Inconsistent state!");
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIDOMEventTarget> upload(do_QueryInterface(mProxy->mUpload));
+    NS_ASSERTION(upload, "This shouldn't fail!");
+
+    nsAutoString eventName;
+    for (PRUint32 index = 0; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
+      eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
+      upload->AddEventListener(eventName, mProxy, PR_FALSE);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<nsDOMWorkerXHRProxy> mProxy;
+};
+
 nsDOMWorkerXHRProxy::nsDOMWorkerXHRProxy(nsDOMWorkerXHR* aWorkerXHR)
 : mWorkerXHR(aWorkerXHR),
   mXHR(nsnull),
   mConcreteXHR(nsnull),
   mUpload(nsnull),
   mSyncEventQueue(nsnull),
   mChannelID(-1),
   mOwnedByXHR(PR_FALSE),
+  mWantUploadListeners(PR_FALSE),
   mCanceled(PR_FALSE)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(MAX_XHR_LISTENER_TYPE >= MAX_UPLOAD_LISTENER_TYPE,
                "Upload should support a subset of XHR's event types!");
 }
 
 nsDOMWorkerXHRProxy::~nsDOMWorkerXHRProxy()
@@ -616,34 +647,36 @@ nsDOMWorkerXHRProxy::FlipOwnership()
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Flip!
   mOwnedByXHR = !mOwnedByXHR;
 
   nsCOMPtr<nsIDOMEventTarget> xhrTarget(do_QueryInterface(mXHR));
   NS_ASSERTION(xhrTarget, "This shouldn't fail!");
 
-  nsCOMPtr<nsIDOMEventTarget> uploadTarget(do_QueryInterface(mUpload));
-  NS_ASSERTION(uploadTarget, "This shouldn't fail!");
-
   // If mWorkerXHR has no outstanding refs from JS then we are about to die.
   // Hold an extra ref here to make sure that we live through this call.
   nsRefPtr<nsDOMWorkerXHRProxy> kungFuDeathGrip(this);
 
   EventListenerFunction function = mOwnedByXHR ?
                                    &nsIDOMEventTarget::AddEventListener :
                                    &nsIDOMEventTarget::RemoveEventListener;
 
   nsAutoString eventName;
   PRUint32 index = 0;
 
-  for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
-    eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
-    (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
-    (uploadTarget.get()->*function)(eventName, this, PR_FALSE);
+  if (mWantUploadListeners) {
+    nsCOMPtr<nsIDOMEventTarget> uploadTarget(do_QueryInterface(mUpload));
+    NS_ASSERTION(uploadTarget, "This shouldn't fail!");
+
+    for (; index < MAX_UPLOAD_LISTENER_TYPE; index++) {
+      eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
+      (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
+      (uploadTarget.get()->*function)(eventName, this, PR_FALSE);
+    }
   }
 
   for (; index < MAX_XHR_LISTENER_TYPE; index++) {
     eventName.AssignASCII(nsDOMWorkerXHREventTarget::sListenerTypes[index]);
     (xhrTarget.get()->*function)(eventName, this, PR_FALSE);
   }
 
   if (mOwnedByXHR) {
@@ -659,62 +692,84 @@ nsDOMWorkerXHRProxy::FlipOwnership()
 nsresult
 nsDOMWorkerXHRProxy::AddEventListener(PRUint32 aType,
                                       nsIDOMEventListener* aListener,
                                       PRBool aOnXListener,
                                       PRBool aUploadListener)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
-  if (mCanceled) {
-    return NS_ERROR_ABORT;
-  }
-
   if ((aUploadListener && aType >= MAX_UPLOAD_LISTENER_TYPE) ||
       aType >= MAX_XHR_LISTENER_TYPE) {
     // Silently fail on junk events.
     return NS_OK;
   }
 
   ListenerArray& listeners = aUploadListener ? mUploadListeners[aType] :
                                                mXHRListeners[aType];
   WrappedListener& onXListener = aUploadListener ? mUploadOnXListeners[aType] :
                                                    mXHROnXListeners[aType];
 
-  nsAutoLock lock(mWorkerXHR->Lock());
+  {
+    nsAutoLock lock(mWorkerXHR->Lock());
+
+    if (mCanceled) {
+      return NS_ERROR_ABORT;
+    }
 
 #ifdef DEBUG
-  if (!aListener) {
-    NS_ASSERTION(aOnXListener, "Shouldn't pass a null listener!");
-  }
+    if (!aListener) {
+      NS_ASSERTION(aOnXListener, "Shouldn't pass a null listener!");
+    }
 #endif
 
-  if (aOnXListener) {
-    // Remove the old one from the array if it exists.
-    if (onXListener) {
+    if (aOnXListener) {
+      // Remove the old one from the array if it exists.
+      if (onXListener) {
 #ifdef DEBUG
-      PRBool removed =
+        PRBool removed =
 #endif
-      listeners.RemoveElement(onXListener);
-      NS_ASSERTION(removed, "Should still be in the array!");
+        listeners.RemoveElement(onXListener);
+        NS_ASSERTION(removed, "Should still be in the array!");
+      }
+
+      if (!aListener) {
+        onXListener = nsnull;
+        return NS_OK;
+      }
+
+      onXListener = new nsDOMWorkerXHRWrappedListener(aListener);
+      NS_ENSURE_TRUE(onXListener, NS_ERROR_OUT_OF_MEMORY);
+
+      aListener = onXListener;
     }
 
-    if (!aListener) {
-      onXListener = nsnull;
-      return NS_OK;
-    }
-
-    onXListener = new nsDOMWorkerXHRWrappedListener(aListener);
-    NS_ENSURE_TRUE(onXListener, NS_ERROR_OUT_OF_MEMORY);
-
-    aListener = onXListener;
+    Listener* added = listeners.AppendElement(aListener);
+    NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
   }
 
-  Listener* added = listeners.AppendElement(aListener);
-  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
+  // If this is the first time we're setting an upload listener then we have to
+  // hit the main thread to attach the upload listeners.
+  if (aUploadListener && aListener && !mWantUploadListeners) {
+    mWantUploadListeners = PR_TRUE;
+
+    nsRefPtr<nsDOMWorkerXHRAttachUploadListenersRunnable> attachRunnable =
+      new nsDOMWorkerXHRAttachUploadListenersRunnable(this);
+    NS_ENSURE_TRUE(attachRunnable, NS_ERROR_OUT_OF_MEMORY);
+
+    nsRefPtr<nsResultReturningRunnable> runnable =
+      new nsResultReturningRunnable(mMainThread, attachRunnable,
+                                    mWorkerXHR->mWorker);
+    NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+    nsresult rv = runnable->Dispatch();
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
 
   return NS_OK;
 }
 
 nsresult
 nsDOMWorkerXHRProxy::RemoveEventListener(PRUint32 aType,
                                          nsIDOMEventListener* aListener,
                                          PRBool aUploadListener)
--- a/dom/src/threads/nsDOMWorkerXHRProxy.h
+++ b/dom/src/threads/nsDOMWorkerXHRProxy.h
@@ -59,17 +59,17 @@ class nsIXMLHttpRequest;
 class nsDOMWorkerXHREvent;
 class nsDOMWorkerXHRWrappedListener;
 class nsXMLHttpRequest;
 
 class nsDOMWorkerXHRProxy : public nsRunnable,
                             public nsIDOMEventListener,
                             public nsIRequestObserver
 {
-
+  friend class nsDOMWorkerXHRAttachUploadListenersRunnable;
   friend class nsDOMWorkerXHREvent;
   friend class nsDOMWorkerXHR;
   friend class nsDOMWorkerXHRUpload;
 
   typedef nsCOMPtr<nsIDOMEventListener> Listener;
   typedef nsTArray<Listener> ListenerArray;
 
   typedef nsRefPtr<nsDOMWorkerXHRWrappedListener> WrappedListener;
@@ -174,12 +174,13 @@ protected:
 
   SyncEventQueue* mSyncEventQueue;
 
   PRInt32 mChannelID;
 
   // Whether or not this object is owned by the real XHR object.
   PRPackedBool mOwnedByXHR;
 
+  PRPackedBool mWantUploadListeners;
   PRPackedBool mCanceled;
 };
 
 #endif /* __NSDOMWORKERXHRPROXY_H__ */