Bug 926746 - Part 3: Fix the problem that JS objects could be released on non-mainthread. r=jesup
authorSteven Lee <slee@mozilla.com>
Thu, 05 Dec 2013 09:29:07 -0500
changeset 174682 e88d34a3c8d58ebc564f4e8a827faa75749dceff
parent 174681 6a829a0de92deb5a7b9263b1a48cde6ee6820c6f
child 174683 bcda0491338b26fc7b850eeb4345d94a50c2355e
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs926746
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 926746 - Part 3: Fix the problem that JS objects could be released on non-mainthread. r=jesup
dom/media/MediaManager.cpp
dom/media/MediaManager.h
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -137,84 +137,84 @@ static nsresult ValidateTrackConstraints
                                       aNormalized.mMandatory,
                                       aOutUnknownConstraint);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 ErrorCallbackRunnable::ErrorCallbackRunnable(
-  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback>& aError,
   const nsAString& aErrorMsg, uint64_t aWindowID)
   : mSuccess(aSuccess)
   , mError(aError)
   , mErrorMsg(aErrorMsg)
   , mWindowID(aWindowID)
+  , mManager(MediaManager::GetInstance()){
+}
+
+ErrorCallbackRunnable::ErrorCallbackRunnable(
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback>& aError,
+  const nsAString& aErrorMsg, uint64_t aWindowID)
+  : mError(aError)
+  , mErrorMsg(aErrorMsg)
+  , mWindowID(aWindowID)
   , mManager(MediaManager::GetInstance()) {
-  }
+}
 
 NS_IMETHODIMP
 ErrorCallbackRunnable::Run()
 {
   // Only run if the window is still active.
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
-
-  if (!(mManager->IsWindowStillActive(mWindowID))) {
-    return NS_OK;
+  if (mError.get() && mManager->IsWindowStillActive(mWindowID)) {
+    // This is safe since we're on main-thread, and the windowlist can only
+    // be invalidated from the main-thread (see OnNavigation)
+    mError->OnError(mErrorMsg);
   }
-  // This is safe since we're on main-thread, and the windowlist can only
-  // be invalidated from the main-thread (see OnNavigation)
-  error->OnError(mErrorMsg);
   return NS_OK;
 }
 
 /**
  * Invoke the "onSuccess" callback in content. The callback will take a
  * DOMBlob in the case of {picture:true}, and a MediaStream in the case of
  * {audio:true} or {video:true}. There is a constructor available for each
  * form. Do this only on the main thread.
  */
 class SuccessCallbackRunnable : public nsRunnable
 {
 public:
   SuccessCallbackRunnable(
-    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback>& aError,
     nsIDOMFile* aFile, uint64_t aWindowID)
     : mSuccess(aSuccess)
     , mError(aError)
     , mFile(aFile)
     , mWindowID(aWindowID)
     , mManager(MediaManager::GetInstance()) {}
 
   NS_IMETHOD
   Run()
   {
     // Only run if the window is still active.
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
-    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
-    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
-
-    if (!(mManager->IsWindowStillActive(mWindowID))) {
-      return NS_OK;
+    if (mSuccess.get() && mManager->IsWindowStillActive(mWindowID)) {
+      // This is safe since we're on main-thread, and the windowlist can only
+      // be invalidated from the main-thread (see OnNavigation)
+      mSuccess->OnSuccess(mFile);
     }
-    // This is safe since we're on main-thread, and the windowlist can only
-    // be invalidated from the main-thread (see OnNavigation)
-    success->OnSuccess(mFile);
     return NS_OK;
   }
 
 private:
-  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
-  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> mError;
   nsCOMPtr<nsIDOMFile> mFile;
   uint64_t mWindowID;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 /**
  * Invoke the GetUserMediaDevices success callback. Wrapped in a runnable
  * so that it may be called on the main thread. The error callback is also
@@ -454,18 +454,18 @@ public:
  * though that would complicate the constructors some.  Currently the
  * GetUserMedia spec does not allow for more than 2 streams to be obtained in
  * one call, to simplify handling of constraints.
  */
 class GetUserMediaStreamRunnable : public nsRunnable
 {
 public:
   GetUserMediaStreamRunnable(
-    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback>& aError,
     uint64_t aWindowID,
     GetUserMediaCallbackMediaStreamListener* aListener,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource)
     : mSuccess(aSuccess)
     , mError(aError)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
@@ -474,19 +474,19 @@ public:
     , mManager(MediaManager::GetInstance()) {}
 
   ~GetUserMediaStreamRunnable() {}
 
   class TracksAvailableCallback : public DOMMediaStream::OnTracksAvailableCallback
   {
   public:
     TracksAvailableCallback(MediaManager* aManager,
-                            nsIDOMGetUserMediaSuccessCallback* aSuccess,
-                            uint64_t aWindowID,
-                            DOMMediaStream* aStream)
+      nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
+      uint64_t aWindowID,
+      DOMMediaStream* aStream)
       : mWindowID(aWindowID), mSuccess(aSuccess), mManager(aManager),
         mStream(aStream) {}
     virtual void NotifyTracksAvailable(DOMMediaStream* aStream) MOZ_OVERRIDE
     {
       // We're in the main thread, so no worries here.
       if (!(mManager->IsWindowStillActive(mWindowID))) {
         return;
       }
@@ -496,17 +496,17 @@ public:
       aStream->SetLogicalStreamStartTime(aStream->GetStream()->GetCurrentTime());
 
       // This is safe since we're on main-thread, and the windowlist can only
       // be invalidated from the main-thread (see OnNavigation)
       LOG(("Returning success for getUserMedia()"));
       mSuccess->OnSuccess(aStream);
     }
     uint64_t mWindowID;
-    nsRefPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback> mSuccess;
     nsRefPtr<MediaManager> mManager;
     // Keep the DOMMediaStream alive until the NotifyTracksAvailable callback
     // has fired, otherwise we might immediately destroy the DOMMediaStream and
     // shut down the underlying MediaStream prematurely.
     // This creates a cycle which is broken when NotifyTracksAvailable
     // is fired (which will happen unless the browser shuts down,
     // since we only add this callback when we've successfully appended
     // the desired tracks in the MediaStreamGraph) or when
@@ -532,19 +532,18 @@ public:
     // Create a media stream.
     DOMMediaStream::TrackTypeHints hints =
       (mAudioSource ? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
       (mVideoSource ? DOMMediaStream::HINT_CONTENTS_VIDEO : 0);
 
     nsRefPtr<nsDOMUserMediaStream> trackunion =
       nsDOMUserMediaStream::CreateTrackUnionStream(window, hints);
     if (!trackunion) {
-      nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error = mError.forget();
       LOG(("Returning error for getUserMedia() - no stream"));
-      error->OnError(NS_LITERAL_STRING("NO_STREAM"));
+      mError->OnError(NS_LITERAL_STRING("NO_STREAM"));
       return NS_OK;
     }
 
     MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
     nsRefPtr<SourceMediaStream> stream = gm->CreateSourceStream(nullptr);
 
     // connect the source stream to the track union stream to avoid us blocking
     trackunion->GetStream()->AsProcessedStream()->SetAutofinish(true);
@@ -574,17 +573,17 @@ public:
     // because that can take a while.
     // Pass ownership of trackunion to the MediaOperationRunnable
     // to ensure it's kept alive until the MediaOperationRunnable runs (at least).
     nsIThread *mediaThread = MediaManager::GetThread();
     nsRefPtr<MediaOperationRunnable> runnable(
       new MediaOperationRunnable(MEDIA_START, mListener, trackunion,
                                  tracksAvailableCallback,
                                  mAudioSource, mVideoSource, false, mWindowID,
-                                 mError.forget()));
+                                 mError));
     mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 
 #ifdef MOZ_WEBRTC
     // Right now these configs are only of use if webrtc is available
     nsresult rv;
     nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
@@ -604,24 +603,22 @@ public:
 
         mListener->AudioConfig(aec_on, (uint32_t) aec,
                                agc_on, (uint32_t) agc,
                                noise_on, (uint32_t) noise);
       }
     }
 #endif
 
-    // We won't need mError now.
-    mError = nullptr;
     return NS_OK;
   }
 
 private:
-  nsRefPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
-  nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaEngineSource> mAudioSource;
   nsRefPtr<MediaEngineSource> mVideoSource;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 /**
@@ -739,45 +736,45 @@ static SourceSet *
  * Do not run this on the main thread. The success and error callbacks *MUST*
  * be dispatched on the main thread!
  */
 class GetUserMediaRunnable : public nsRunnable
 {
 public:
   GetUserMediaRunnable(
     const MediaStreamConstraintsInternal& aConstraints,
-    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    nsIDOMGetUserMediaSuccessCallback* aSuccess,
+    nsIDOMGetUserMediaErrorCallback* aError,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs)
     : mConstraints(aConstraints)
-    , mSuccess(aSuccess)
-    , mError(aError)
+    , mSuccess(new nsMainThreadPtrHolder<nsIDOMGetUserMediaSuccessCallback>(aSuccess))
+    , mError(new nsMainThreadPtrHolder<nsIDOMGetUserMediaErrorCallback>(aError))
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mPrefs(aPrefs)
     , mDeviceChosen(false)
     , mBackendChosen(false)
     , mManager(MediaManager::GetInstance())
   {}
 
   /**
    * The caller can also choose to provide their own backend instead of
    * using the one provided by MediaManager::GetBackend.
    */
   GetUserMediaRunnable(
     const MediaStreamConstraintsInternal& aConstraints,
-    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    nsIDOMGetUserMediaSuccessCallback* aSuccess,
+    nsIDOMGetUserMediaErrorCallback* aError,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs,
     MediaEngine* aBackend)
     : mConstraints(aConstraints)
-    , mSuccess(aSuccess)
-    , mError(aError)
+    , mSuccess(new nsMainThreadPtrHolder<nsIDOMGetUserMediaSuccessCallback>(aSuccess))
+    , mError(new nsMainThreadPtrHolder<nsIDOMGetUserMediaErrorCallback>(aError))
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mPrefs(aPrefs)
     , mDeviceChosen(false)
     , mBackendChosen(true)
     , mBackend(aBackend)
     , mManager(MediaManager::GetInstance())
   {}
@@ -840,18 +837,17 @@ public:
 
       // Should happen *after* error runs for consistency, but may not matter
       nsRefPtr<MediaManager> manager(MediaManager::GetInstance());
       manager->RemoveFromWindowList(mWindowID, mListener);
     } else {
       // This will re-check the window being alive on main-thread
       // Note: we must remove the listener on MainThread as well
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mSuccess, mError, aErrorMsg, mWindowID
-      ));
+        mSuccess, mError, aErrorMsg, mWindowID));
 
       // MUST happen after ErrorCallbackRunnable Run()s, as it checks the active window list
       NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
     }
 
     return NS_OK;
   }
 
@@ -912,32 +908,30 @@ public:
   void
   ProcessGetUserMedia(MediaEngineSource* aAudioSource, MediaEngineSource* aVideoSource)
   {
     nsresult rv;
     if (aAudioSource) {
       rv = aAudioSource->Allocate(mPrefs);
       if (NS_FAILED(rv)) {
         LOG(("Failed to allocate audiosource %d",rv));
-        NS_DispatchToMainThread(new ErrorCallbackRunnable(
-                                  mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
-                                                          ));
+        NS_DispatchToMainThread(new ErrorCallbackRunnable(mSuccess, mError,
+          NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID));
         return;
       }
     }
     if (aVideoSource) {
       rv = aVideoSource->Allocate(mPrefs);
       if (NS_FAILED(rv)) {
         LOG(("Failed to allocate videosource %d\n",rv));
         if (aAudioSource) {
           aAudioSource->Deallocate();
         }
-        NS_DispatchToMainThread(new ErrorCallbackRunnable(
-          mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
-                                                          ));
+        NS_DispatchToMainThread(new ErrorCallbackRunnable(mSuccess, mError,
+          NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID));
         return;
       }
     }
 
     NS_DispatchToMainThread(new GetUserMediaStreamRunnable(
       mSuccess, mError, mWindowID, mListener, aAudioSource, aVideoSource
     ));
     return;
@@ -947,19 +941,18 @@ public:
    * Allocates a video device, takes a snapshot and returns a DOMFile via
    * a SuccessRunnable or an error via the ErrorRunnable. Off the main thread.
    */
   void
   ProcessGetUserMediaSnapshot(MediaEngineSource* aSource, int aDuration)
   {
     nsresult rv = aSource->Allocate(mPrefs);
     if (NS_FAILED(rv)) {
-      NS_DispatchToMainThread(new ErrorCallbackRunnable(
-        mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
-      ));
+      NS_DispatchToMainThread(new ErrorCallbackRunnable(mSuccess, mError,
+        NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID));
       return;
     }
 
     /**
      * Display picture capture UI here before calling Snapshot() - Bug 748835.
      */
     nsCOMPtr<nsIDOMFile> file;
     aSource->Snapshot(aDuration, getter_AddRefs(file));
@@ -969,18 +962,18 @@ public:
       mSuccess, mError, file, mWindowID
     ));
     return;
   }
 
 private:
   MediaStreamConstraintsInternal mConstraints;
 
-  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
-  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> mError;
   uint64_t mWindowID;
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsRefPtr<MediaDevice> mAudioDevice;
   nsRefPtr<MediaDevice> mVideoDevice;
   MediaEnginePrefs mPrefs;
 
   bool mDeviceChosen;
   bool mBackendChosen;
@@ -1314,22 +1307,22 @@ MediaManager::GetUserMedia(JSContext* aC
   }
 
   /**
    * Pass runnables along to GetUserMediaRunnable so it can add the
    * MediaStreamListener to the runnable list.
    */
   if (c.mFake) {
     // Fake stream from default backend.
-    gUMRunnable = new GetUserMediaRunnable(c, onSuccess.forget(),
-      onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
+    gUMRunnable = new GetUserMediaRunnable(c, onSuccess,
+      onError, windowID, listener, mPrefs, new MediaEngineDefault());
   } else {
     // Stream from default device from WebRTC backend.
-    gUMRunnable = new GetUserMediaRunnable(c, onSuccess.forget(),
-      onError.forget(), windowID, listener, mPrefs);
+    gUMRunnable = new GetUserMediaRunnable(c, onSuccess,
+      onError, windowID, listener, mPrefs);
   }
 
 #ifdef MOZ_B2G_CAMERA
   if (mCameraManager == nullptr) {
     mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
@@ -1784,17 +1777,17 @@ GetUserMediaCallbackMediaStreamListener:
   nsRefPtr<MediaOperationRunnable> runnable;
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
   runnable = new MediaOperationRunnable(MEDIA_STOP,
                                         this, nullptr, nullptr,
                                         mAudioSource, mVideoSource,
-                                        mFinished, mWindowID, nullptr);
+                                        mFinished, mWindowID);
   mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
   mFinished = true;
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -9,16 +9,17 @@
 
 #include "nsHashKeys.h"
 #include "nsGlobalWindow.h"
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsObserverService.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
+#include "nsProxyRelease.h"
 
 #include "nsPIDOMWindow.h"
 #include "nsIDOMNavigatorUserMedia.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "prlog.h"
@@ -208,17 +209,17 @@ class GetUserMediaNotificationEvent: pub
                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID)
     : mListener(aListener) , mStatus(aStatus) , mIsAudio(aIsAudio)
     , mIsVideo(aIsVideo), mWindowID(aWindowID) {}
 
     GetUserMediaNotificationEvent(GetUserMediaStatus aStatus,
                                   already_AddRefed<DOMMediaStream> aStream,
                                   DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
                                   bool aIsAudio, bool aIsVideo, uint64_t aWindowID,
-                                  already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
+                                  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> aError)
     : mStream(aStream), mOnTracksAvailableCallback(aOnTracksAvailableCallback),
       mStatus(aStatus), mIsAudio(aIsAudio), mIsVideo(aIsVideo), mWindowID(aWindowID),
       mError(aError) {}
     virtual ~GetUserMediaNotificationEvent()
     {
 
     }
 
@@ -227,17 +228,17 @@ class GetUserMediaNotificationEvent: pub
   protected:
     nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
     nsRefPtr<DOMMediaStream> mStream;
     nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
     GetUserMediaStatus mStatus;
     bool mIsAudio;
     bool mIsVideo;
     uint64_t mWindowID;
-    nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef enum {
   MEDIA_START,
   MEDIA_STOP
 } MediaOperation;
 
 class MediaManager;
@@ -246,23 +247,26 @@ class MediaManager;
  * Send an error back to content. The error is the form a string.
  * Do this only on the main thread. The success callback is also passed here
  * so it can be released correctly.
  */
 class ErrorCallbackRunnable : public nsRunnable
 {
 public:
   ErrorCallbackRunnable(
-    already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback>& aError,
+    const nsAString& aErrorMsg, uint64_t aWindowID);
+  ErrorCallbackRunnable(
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback>& aError,
     const nsAString& aErrorMsg, uint64_t aWindowID);
   NS_IMETHOD Run();
 private:
-  already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
-  already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaSuccessCallback> mSuccess;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> mError;
   const nsString mErrorMsg;
   uint64_t mWindowID;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
 class ReleaseMediaOperationResource : public nsRunnable
 {
 public:
@@ -286,43 +290,61 @@ public:
   MediaOperationRunnable(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     DOMMediaStream* aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource,
     bool aNeedsFinish,
     uint64_t aWindowID,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
+    nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> aError)
     : mType(aType)
     , mStream(aStream)
     , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mListener(aListener)
     , mFinish(aNeedsFinish)
     , mWindowID(aWindowID)
     , mError(aError)
   {}
 
+  MediaOperationRunnable(MediaOperation aType,
+    GetUserMediaCallbackMediaStreamListener* aListener,
+    DOMMediaStream* aStream,
+    DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
+    MediaEngineSource* aAudioSource,
+    MediaEngineSource* aVideoSource,
+    bool aNeedsFinish,
+    uint64_t aWindowID)
+    : mType(aType)
+    , mStream(aStream)
+    , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
+    , mAudioSource(aAudioSource)
+    , mVideoSource(aVideoSource)
+    , mListener(aListener)
+    , mFinish(aNeedsFinish)
+    , mWindowID(aWindowID)
+    , mError(nullptr)
+  {}
+
   ~MediaOperationRunnable()
   {
     // MediaStreams can be released on any thread.
   }
 
   nsresult returnAndCallbackError(nsresult rv, const char* errorLog)
   {
     MM_LOG(("%s , rv=%d", errorLog, rv));
     NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
           mOnTracksAvailableCallback.forget()));
     nsString log;
 
     log.AssignASCII(errorLog, strlen(errorLog));
-    NS_DispatchToMainThread(new ErrorCallbackRunnable(nullptr, mError.forget(),
-      log, mWindowID));
+    NS_DispatchToMainThread(new ErrorCallbackRunnable(mError, log, mWindowID));
     return NS_OK;
   }
 
   NS_IMETHOD
   Run() MOZ_OVERRIDE
   {
     SourceMediaStream *source = mListener->GetSourceStream();
     // No locking between these is required as all the callbacks for the
@@ -363,17 +385,17 @@ public:
           // because mOnTracksAvailableCallback needs to be added to mStream
           // on the main thread.
           nsRefPtr<GetUserMediaNotificationEvent> event =
             new GetUserMediaNotificationEvent(GetUserMediaNotificationEvent::STARTING,
                                               mStream.forget(),
                                               mOnTracksAvailableCallback.forget(),
                                               mAudioSource != nullptr,
                                               mVideoSource != nullptr,
-                                              mWindowID, mError.forget());
+                                              mWindowID, mError);
           NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
         }
         break;
 
       case MEDIA_STOP:
         {
           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
           if (mAudioSource) {
@@ -410,17 +432,17 @@ private:
   MediaOperation mType;
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
   bool mFinish;
   uint64_t mWindowID;
-  nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
+  nsMainThreadPtrHandle<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
 {
 public: