Bug 1060738 - Switch to using chromium's Thread/tasks in MediaManager. On Windows, use MessagePumpForNonMainUIThreads for the background media thread. r=jesup a=sylvestre
authorJim Mathies <jmathies@mozilla.com>
Fri, 12 Sep 2014 09:49:39 -0500
changeset 216721 1355bb2a2765
parent 216720 b6a5a3973477
child 216722 84daded3719c
push id3890
push userrjesup@wgate.com
push date2014-09-15 14:07 +0000
treeherdermozilla-beta@84daded3719c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, sylvestre
bugs1060738
milestone33.0
Bug 1060738 - Switch to using chromium's Thread/tasks in MediaManager. On Windows, use MessagePumpForNonMainUIThreads for the background media thread. r=jesup a=sylvestre
content/base/src/nsDocument.cpp
dom/media/MediaManager.cpp
dom/media/MediaManager.h
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -145,22 +145,16 @@
 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
 #include "nsJSUtils.h"
 #include "nsFrameLoader.h"
 #include "nsEscape.h"
 #include "nsObjectLoadingContent.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
-#ifdef MOZ_MEDIA_NAVIGATOR
-#include "mozilla/MediaManager.h"
-#endif // MOZ_MEDIA_NAVIGATOR
-#ifdef MOZ_WEBRTC
-#include "IPeerConnection.h"
-#endif // MOZ_WEBRTC
 
 #include "mozAutoDocUpdate.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMNavigationTiming.h"
 
 #include "nsSMILAnimationController.h"
@@ -224,16 +218,23 @@
 #include "nsIDocumentActivity.h"
 #include "nsIStructuredCloneContainer.h"
 #include "nsIMutableArray.h"
 #include "nsContentPermissionHelper.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "nsWindowMemoryReporter.h"
 #include "nsLocation.h"
 
+#ifdef MOZ_MEDIA_NAVIGATOR
+#include "mozilla/MediaManager.h"
+#endif // MOZ_MEDIA_NAVIGATOR
+#ifdef MOZ_WEBRTC
+#include "IPeerConnection.h"
+#endif // MOZ_WEBRTC
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
 static PRLogModuleInfo* gCspPRLog;
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -783,25 +783,23 @@ public:
 
     mListener->AudioConfig(aec_on, (uint32_t) aec,
                            agc_on, (uint32_t) agc,
                            noise_on, (uint32_t) noise,
                            playout_delay);
 
     // Dispatch to the media thread to ask it to start the sources,
     // 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()));
-    mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    // Pass ownership of trackunion to the MediaOperationTask
+    // to ensure it's kept alive until the MediaOperationTask runs (at least).
+    MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+      new MediaOperationTask(MEDIA_START, mListener, trackunion,
+                             tracksAvailableCallback,
+                             mAudioSource, mVideoSource, false, mWindowID,
+                             mError.forget()));
 
     // We won't need mError now.
     mError = nullptr;
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
@@ -982,20 +980,20 @@ static SourceSet *
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
  * ProcessGetUserMedia or ProcessGetUserMediaSnapshot is called, and the results
  * are sent back to the DOM.
  *
  * Do not run this on the main thread. The success and error callbacks *MUST*
  * be dispatched on the main thread!
  */
-class GetUserMediaRunnable : public nsRunnable
+class GetUserMediaTask : public Task
 {
 public:
-  GetUserMediaRunnable(
+  GetUserMediaTask(
     const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
@@ -1006,17 +1004,17 @@ public:
     , mBackend(nullptr)
     , mManager(MediaManager::GetInstance())
   {}
 
   /**
    * The caller can also choose to provide their own backend instead of
    * using the one provided by MediaManager::GetBackend.
    */
-  GetUserMediaRunnable(
+  GetUserMediaTask(
     const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
     MediaEnginePrefs &aPrefs,
     MediaEngine* aBackend)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
@@ -1024,69 +1022,68 @@ public:
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mPrefs(aPrefs)
     , mDeviceChosen(false)
     , mBackend(aBackend)
     , mManager(MediaManager::GetInstance())
   {}
 
-  ~GetUserMediaRunnable() {
+  ~GetUserMediaTask() {
   }
 
   void
   Fail(const nsAString& aMessage) {
     nsRefPtr<ErrorCallbackRunnable> runnable =
       new ErrorCallbackRunnable(mSuccess, mError, aMessage, mWindowID);
     // These should be empty now
     MOZ_ASSERT(!mSuccess);
     MOZ_ASSERT(!mError);
 
     NS_DispatchToMainThread(runnable);
   }
 
-  NS_IMETHOD
+  void
   Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
     MOZ_ASSERT(mSuccess);
     MOZ_ASSERT(mError);
 
     MediaEngine* backend = mBackend;
     // Was a backend provided?
     if (!backend) {
       backend = mManager->GetBackend(mWindowID);
     }
 
     // Was a device provided?
     if (!mDeviceChosen) {
       nsresult rv = SelectDevice(backend);
       if (rv != NS_OK) {
-        return rv;
+        return;
       }
     }
 
     // It is an error if audio or video are requested along with picture.
     if (mConstraints.mPicture &&
         (IsOn(mConstraints.mAudio) || IsOn(mConstraints.mVideo))) {
       Fail(NS_LITERAL_STRING("NOT_SUPPORTED_ERR"));
-      return NS_OK;
+      return;
     }
 
     if (mConstraints.mPicture) {
       ProcessGetUserMediaSnapshot(mVideoDevice->GetSource(), 0);
-      return NS_OK;
+      return;
     }
 
     // There's a bug in the permission code that can leave us with mAudio but no audio device
     ProcessGetUserMedia(((IsOn(mConstraints.mAudio) && mAudioDevice) ?
                          mAudioDevice->GetSource() : nullptr),
                         ((IsOn(mConstraints.mVideo) && mVideoDevice) ?
                          mVideoDevice->GetSource() : nullptr));
-    return NS_OK;
   }
 
   nsresult
   Denied(const nsAString& aErrorMsg)
   {
     MOZ_ASSERT(mSuccess);
     MOZ_ASSERT(mError);
 
@@ -1266,40 +1263,62 @@ private:
   MediaEnginePrefs mPrefs;
 
   bool mDeviceChosen;
 
   RefPtr<MediaEngine> mBackend;
   nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
+#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
+class GetUserMediaRunnableWrapper : public nsRunnable
+{
+public:
+  // This object must take ownership of task
+  GetUserMediaRunnableWrapper(GetUserMediaTask* task) :
+    mTask(task) {
+  }
+
+  ~GetUserMediaRunnableWrapper() {
+  }
+
+  NS_IMETHOD Run() {
+    mTask->Run();
+    return NS_OK;
+  }
+
+private:
+  nsAutoPtr<GetUserMediaTask> mTask;
+};
+#endif
+
 /**
- * Similar to GetUserMediaRunnable, but used for the chrome-only
+ * Similar to GetUserMediaTask, but used for the chrome-only
  * GetUserMediaDevices function. Enumerates a list of audio & video devices,
  * wraps them up in nsIMediaDevice objects and returns it to the success
  * callback.
  */
-class GetUserMediaDevicesRunnable : public nsRunnable
+class GetUserMediaDevicesTask : public Task
 {
 public:
-  GetUserMediaDevicesRunnable(
+  GetUserMediaDevicesTask(
     const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
     uint64_t aWindowId, nsACString& aAudioLoopbackDev,
     nsACString& aVideoLoopbackDev)
     : mConstraints(aConstraints)
     , mSuccess(aSuccess)
     , mError(aError)
     , mManager(MediaManager::GetInstance())
     , mWindowId(aWindowId)
     , mLoopbackAudioDevice(aAudioLoopbackDev)
     , mLoopbackVideoDevice(aVideoLoopbackDev) {}
 
-  NS_IMETHOD
+  void // NS_IMETHOD
   Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
     nsRefPtr<MediaEngine> backend;
     if (mConstraints.mFake)
       backend = new MediaEngineDefault();
     else
@@ -1321,17 +1340,16 @@ public:
       final->MoveElementsFrom(*s);
     }
 
     NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
                                                               mSuccess, mError,
                                                               final.forget()));
     // DeviceSuccessCallbackRunnable should have taken these.
     MOZ_ASSERT(!mSuccess && !mError);
-    return NS_OK;
   }
 
 private:
   MediaStreamConstraints mConstraints;
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
   nsRefPtr<MediaManager> mManager;
   uint64_t mWindowId;
@@ -1369,22 +1387,33 @@ NS_IMPL_ISUPPORTS(MediaManager, nsIMedia
 /* static */ StaticRefPtr<MediaManager> MediaManager::sSingleton;
 
 // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
 // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
 // from MediaManager thread.
 /* static */  MediaManager*
 MediaManager::Get() {
   if (!sSingleton) {
+    NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
+
     sSingleton = new MediaManager();
 
-    NS_NewNamedThread("MediaManager", getter_AddRefs(sSingleton->mMediaThread));
+    sSingleton->mMediaThread = new base::Thread("MediaManager");
+    base::Thread::Options options;
+#if defined(_WIN32)
+    options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD;
+#else
+    options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINTHREAD;
+#endif
+    if (!sSingleton->mMediaThread->StartWithOptions(options)) {
+      MOZ_CRASH();
+    }
+
     LOG(("New Media thread for gum"));
 
-    NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
       obs->AddObserver(sSingleton, "xpcom-shutdown", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
       obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
       obs->AddObserver(sSingleton, "phone-state-changed", false);
     }
@@ -1403,16 +1432,25 @@ MediaManager::Get() {
 /* static */ already_AddRefed<MediaManager>
 MediaManager::GetInstance()
 {
   // so we can have non-refcounted getters
   nsRefPtr<MediaManager> service = MediaManager::Get();
   return service.forget();
 }
 
+/* static */
+MessageLoop*
+MediaManager::GetMessageLoop()
+{
+  NS_ASSERTION(Get(), "MediaManager singleton?");
+  NS_ASSERTION(Get()->mMediaThread, "No thread yet");
+  return Get()->mMediaThread->message_loop();
+}
+
 /* static */ nsresult
 MediaManager::NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
                                           const nsString& aMsg,
                                           const bool& aIsAudio,
                                           const bool& aIsVideo)
 {
   NS_ENSURE_ARG(aWindow);
 
@@ -1538,22 +1576,19 @@ MediaManager::GetUserMedia(bool aPrivile
   // This is safe since we're on main-thread, and the windowlist can only
   // be invalidated from the main-thread (see OnNavigation)
   StreamListeners* listeners = GetActiveWindows()->Get(windowID);
   if (!listeners) {
     listeners = new StreamListeners;
     GetActiveWindows()->Put(windowID, listeners);
   }
 
-  // Ensure there's a thread for gum to proxy to off main thread
-  nsIThread *mediaThread = MediaManager::GetThread();
-
   // Create a disabled listener to act as a placeholder
   GetUserMediaCallbackMediaStreamListener* listener =
-    new GetUserMediaCallbackMediaStreamListener(mediaThread, windowID);
+    new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID);
 
   // No need for locking because we always do this in the main thread.
   listeners->AppendElement(listener);
 
   // Developer preference for turning off permission check.
   if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
     aPrivileged = true;
   }
@@ -1578,63 +1613,64 @@ MediaManager::GetUserMedia(bool aPrivile
           n.mFacingMode.Construct(tc.mOptional.Value()[i].mFacingMode.Value());
           tc.mAdvanced.Value().AppendElement(n);
         }
       }
     }
   }
 #endif
 
-  // Pass callbacks and MediaStreamListener along to GetUserMediaRunnable.
-  nsRefPtr<GetUserMediaRunnable> runnable;
+  // Pass callbacks and MediaStreamListener along to GetUserMediaTask.
+  nsAutoPtr<GetUserMediaTask> task;
   if (c.mFake) {
     // Fake stream from default backend.
-    runnable = new GetUserMediaRunnable(c, onSuccess.forget(),
+    task = new GetUserMediaTask(c, onSuccess.forget(),
       onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
   } else {
     // Stream from default device from WebRTC backend.
-    runnable = new GetUserMediaRunnable(c, onSuccess.forget(),
+    task = new GetUserMediaTask(c, onSuccess.forget(),
       onError.forget(), windowID, listener, mPrefs);
   }
 
   nsIURI* docURI = aWindow->GetDocumentURI();
 
   if (c.mVideo.IsMediaTrackConstraints()) {
     auto& tc = c.mVideo.GetAsMediaTrackConstraints();
     // deny screensharing request if support is disabled
     if (tc.mMediaSource != dom::MediaSourceEnum::Camera) {
       if (!Preferences::GetBool("media.getusermedia.screensharing.enabled", false)) {
-        return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
+        return task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
       }
       /* Deny screensharing if the requesting document is not from a host
        on the whitelist. */
       // Block screen/window sharing on Mac OSX 10.6 and WinXP until proved that they work
       if (
 #if defined(XP_MACOSX)
           !nsCocoaFeatures::OnLionOrLater() ||
 #endif
 #if defined (XP_WIN)
           !IsVistaOrLater() ||
 #endif
           (!aPrivileged && !HostHasPermission(*docURI))) {
-        return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
+        return task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
       }
     }
   }
 
 #ifdef MOZ_B2G_CAMERA
   if (mCameraManager == nullptr) {
     mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
   if (c.mPicture) {
     // ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
-    NS_DispatchToMainThread(runnable);
+    // Note, GetUserMediaRunnableWrapper takes ownership of task.
+    NS_DispatchToMainThread(new GetUserMediaRunnableWrapper(task.forget()));
     return NS_OK;
   }
 #endif
 
   bool isLoop = false;
   nsCOMPtr<nsIURI> loopURI;
   nsresult rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation");
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1643,17 +1679,17 @@ MediaManager::GetUserMedia(bool aPrivile
 
   if (isLoop) {
     aPrivileged = true;
   }
 
   // XXX No full support for picture in Desktop yet (needs proper UI)
   if (aPrivileged ||
       (c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) {
-    mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
   } else {
     bool isHTTPS = false;
     if (docURI) {
       docURI->SchemeIs("https", &isHTTPS);
     }
 
     // Check if this site has persistent permissions.
     nsresult rv;
@@ -1672,37 +1708,37 @@ MediaManager::GetUserMedia(bool aPrivile
     if (IsOn(c.mVideo)) {
       rv = permManager->TestExactPermissionFromPrincipal(
         aWindow->GetExtantDoc()->NodePrincipal(), "camera", &videoPerm);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if ((!IsOn(c.mAudio) || audioPerm == nsIPermissionManager::DENY_ACTION) &&
         (!IsOn(c.mVideo) || videoPerm == nsIPermissionManager::DENY_ACTION)) {
-      return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
+      return task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
     }
 
-    // Ask for user permission, and dispatch runnable (or not) when a response
+    // Ask for user permission, and dispatch task (or not) when a response
     // is received via an observer notification. Each call is paired with its
-    // runnable by a GUID.
+    // task by a GUID.
     nsCOMPtr<nsIUUIDGenerator> uuidgen =
       do_GetService("@mozilla.org/uuid-generator;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Generate a call ID.
     nsID id;
     rv = uuidgen->GenerateUUIDInPlace(&id);
     NS_ENSURE_SUCCESS(rv, rv);
 
     char buffer[NSID_LENGTH];
     id.ToProvidedString(buffer);
     NS_ConvertUTF8toUTF16 callID(buffer);
 
-    // Store the current unarmed runnable w/callbacks.
-    mActiveCallbacks.Put(callID, runnable);
+    // Store the current unarmed task w/callbacks.
+    mActiveCallbacks.Put(callID, task.forget());
 
     // Add a WindowID cross-reference so OnNavigation can tear things down
     nsTArray<nsString>* array;
     if (!mCallIds.Get(windowID, &array)) {
       array = new nsTArray<nsString>();
       array->AppendElement(callID);
       mCallIds.Put(windowID, array);
     } else {
@@ -1737,22 +1773,22 @@ MediaManager::GetUserMediaDevices(nsPIDO
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
 
   // Check if the preference for using loopback devices is enabled.
   nsAdoptingCString loopbackAudioDevice =
     Preferences::GetCString("media.audio_loopback_dev");
   nsAdoptingCString loopbackVideoDevice =
     Preferences::GetCString("media.video_loopback_dev");
 
-  nsCOMPtr<nsIRunnable> gUMDRunnable = new GetUserMediaDevicesRunnable(
-    aConstraints, onSuccess.forget(), onError.forget(),
-    (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
-    loopbackAudioDevice, loopbackVideoDevice);
+  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+    new GetUserMediaDevicesTask(
+      aConstraints, onSuccess.forget(), onError.forget(),
+      (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
+      loopbackAudioDevice, loopbackVideoDevice));
 
-  mMediaThread->Dispatch(gUMDRunnable, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 MediaEngine*
 MediaManager::GetBackend(uint64_t aWindowId)
 {
   // Plugin backends as appropriate. The default engine also currently
   // includes picture support for Android.
@@ -1924,86 +1960,84 @@ MediaManager::Observe(nsISupports* aSubj
       MutexAutoLock lock(mMutex);
       GetActiveWindows()->Clear();
       mActiveCallbacks.Clear();
       mCallIds.Clear();
       LOG(("Releasing MediaManager singleton and thread"));
       // Note: won't be released immediately as the Observer has a ref to us
       sSingleton = nullptr;
       if (mMediaThread) {
-        mMediaThread->Shutdown();
-        mMediaThread = nullptr;
+        mMediaThread->Stop();
       }
       mBackend = nullptr;
     }
 
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
-    nsRefPtr<GetUserMediaRunnable> runnable;
-    if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) {
+    nsAutoPtr<GetUserMediaTask> task;
+    mActiveCallbacks.RemoveAndForget(key, task);
+    if (!task) {
       return NS_OK;
     }
-    mActiveCallbacks.Remove(key);
 
     if (aSubject) {
       // A particular device or devices were chosen by the user.
       // NOTE: does not allow setting a device to null; assumes nullptr
       nsCOMPtr<nsISupportsArray> array(do_QueryInterface(aSubject));
       MOZ_ASSERT(array);
       uint32_t len = 0;
       array->Count(&len);
       MOZ_ASSERT(len);
       if (!len) {
         // neither audio nor video were selected
-        runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
+        task->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
         return NS_OK;
       }
       for (uint32_t i = 0; i < len; i++) {
         nsCOMPtr<nsISupports> supports;
         array->GetElementAt(i,getter_AddRefs(supports));
         nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supports));
         MOZ_ASSERT(device); // shouldn't be returning anything else...
         if (device) {
           nsString type;
           device->GetType(type);
           if (type.EqualsLiteral("video")) {
-            runnable->SetVideoDevice(static_cast<VideoDevice*>(device.get()));
+            task->SetVideoDevice(static_cast<VideoDevice*>(device.get()));
           } else if (type.EqualsLiteral("audio")) {
-            runnable->SetAudioDevice(static_cast<AudioDevice*>(device.get()));
+            task->SetAudioDevice(static_cast<AudioDevice*>(device.get()));
           } else {
             NS_WARNING("Unknown device type in getUserMedia");
           }
         }
       }
     }
 
     // Reuse the same thread to save memory.
-    mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
     nsString errorMessage(NS_LITERAL_STRING("PERMISSION_DENIED"));
 
     if (aSubject) {
       nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
       MOZ_ASSERT(msg);
       msg->GetData(errorMessage);
       if (errorMessage.IsEmpty())
         errorMessage.AssignLiteral(MOZ_UTF16("UNKNOWN_ERROR"));
     }
 
     nsString key(aData);
-    nsRefPtr<GetUserMediaRunnable> runnable;
-    if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) {
-      return NS_OK;
+    nsAutoPtr<GetUserMediaTask> task;
+    mActiveCallbacks.RemoveAndForget(key, task);
+    if (task) {
+      task->Denied(errorMessage);
     }
-    mActiveCallbacks.Remove(key);
-    runnable->Denied(errorMessage);
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:revoke")) {
     nsresult rv;
     // may be windowid or screen:windowid
     nsDependentString data(aData);
     if (Substring(data, 0, strlen("screen:")).EqualsLiteral("screen:")) {
       uint64_t windowID = PromiseFlatString(Substring(data, strlen("screen:"))).ToInteger64(&rv);
@@ -2242,49 +2276,63 @@ MediaManager::StopMediaStreams()
     array->GetElementAt(i, getter_AddRefs(window));
     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(window));
     if (win) {
       OnNavigation(win->WindowID());
     }
   }
 }
 
+void
+GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
+              uint32_t aEcho,
+              bool aAgcOn, uint32_t aAGC,
+              bool aNoiseOn, uint32_t aNoise,
+              int32_t aPlayoutDelay)
+{
+  if (mAudioSource) {
+#ifdef MOZ_WEBRTC
+    mMediaThread->message_loop()->PostTask(FROM_HERE,
+      NewRunnableMethod(mAudioSource.get(), &MediaEngineSource::Config,
+                        aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
+                        aNoise, aPlayoutDelay));
+#endif
+  }
+}
+
 // Can be invoked from EITHER MainThread or MSG thread
 void
 GetUserMediaCallbackMediaStreamListener::Invalidate()
 {
-
-  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);
-  mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+    new MediaOperationTask(MEDIA_STOP,
+                           this, nullptr, nullptr,
+                           mAudioSource, mVideoSource,
+                           mFinished, mWindowID, nullptr));
 }
 
 // Doesn't kill audio
 // XXX refactor to combine with Invalidate()?
 void
 GetUserMediaCallbackMediaStreamListener::StopScreenWindowSharing()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   if (mVideoSource && !mStopped &&
       (mVideoSource->GetMediaSource() == MediaSourceType::Screen ||
        mVideoSource->GetMediaSource() == MediaSourceType::Window)) {
     // Stop the whole stream if there's no audio; just the video track if we have both
-    nsRefPtr<MediaOperationRunnable> runnable(
-      new MediaOperationRunnable(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP,
-                                 this, nullptr, nullptr,
-                                 nullptr, mVideoSource,
-                                 mFinished, mWindowID, nullptr));
-    mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+    MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+      new MediaOperationTask(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP,
+                             this, nullptr, nullptr,
+                             nullptr, mVideoSource,
+                             mFinished, mWindowID, nullptr));
   }
 }
 
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
@@ -2293,22 +2341,21 @@ GetUserMediaCallbackMediaStreamListener:
   NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, this));
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
                                                                bool aHasListeners)
 {
-  nsRefPtr<MediaOperationRunnable> runnable;
-  runnable = new MediaOperationRunnable(MEDIA_DIRECT_LISTENERS,
-                                        this, nullptr, nullptr,
-                                        mAudioSource, mVideoSource,
-                                        aHasListeners, mWindowID, nullptr);
-  mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+    new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
+                           this, nullptr, nullptr,
+                           mAudioSource, mVideoSource,
+                           aHasListeners, mWindowID, nullptr));
 }
 
 // Called from the MediaStreamGraph thread
 // this can be in response to our own RemoveListener() (via ::Remove()), or
 // because the DOM GC'd the DOMLocalMediaStream/etc we're attached to.
 void
 GetUserMediaCallbackMediaStreamListener::NotifyRemoved(MediaStreamGraph* aGraph)
 {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -25,16 +25,20 @@
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "prlog.h"
 #include "DOMMediaStream.h"
 
 #ifdef MOZ_WEBRTC
 #include "mtransport/runnable_utils.h"
 #endif
 
+// Note, these suck in Windows headers, unfortunately.
+#include "base/thread.h"
+#include "base/task.h"
+
 #ifdef MOZ_WIDGET_GONK
 #include "DOMCameraManager.h"
 #endif
 
 namespace mozilla {
 namespace dom {
 struct MediaStreamConstraints;
 class NavigatorUserMediaSuccessCallback;
@@ -52,17 +56,17 @@ extern PRLogModuleInfo* GetMediaManagerL
  * This class is an implementation of MediaStreamListener. This is used
  * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
  * are assigned and deassigned in content.
  */
 class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
 {
 public:
   // Create in an inactive state
-  GetUserMediaCallbackMediaStreamListener(nsIThread *aThread,
+  GetUserMediaCallbackMediaStreamListener(base::Thread *aThread,
     uint64_t aWindowID)
     : mMediaThread(aThread)
     , mWindowID(aWindowID)
     , mStopped(false)
     , mFinished(false)
     , mLock("mozilla::GUMCMSL")
     , mRemoved(false) {}
 
@@ -131,37 +135,25 @@ public:
            mVideoSource->GetMediaSource() == MediaSourceType::Window;
   }
 
   void SetStopped()
   {
     mStopped = true;
   }
 
-  // implement in .cpp to avoid circular dependency with MediaOperationRunnable
+  // implement in .cpp to avoid circular dependency with MediaOperationTask
   // Can be invoked from EITHER MainThread or MSG thread
   void Invalidate();
 
   void
   AudioConfig(bool aEchoOn, uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
-              int32_t aPlayoutDelay)
-  {
-    if (mAudioSource) {
-#ifdef MOZ_WEBRTC
-      // Right now these configs are only of use if webrtc is available
-      RUN_ON_THREAD(mMediaThread,
-                    WrapRunnable(nsRefPtr<MediaEngineSource>(mAudioSource), // threadsafe
-                                 &MediaEngineSource::Config,
-                                 aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn, aNoise, aPlayoutDelay),
-                    NS_DISPATCH_NORMAL);
-#endif
-    }
-  }
+              int32_t aPlayoutDelay);
 
   void
   Remove()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
     // allow calling even if inactive (!mStream) for easier cleanup
     // Caller holds strong reference to us, so no death grip required
     MutexAutoLock lock(mLock); // protect access to mRemoved
@@ -217,17 +209,17 @@ public:
   virtual void
   NotifyRemoved(MediaStreamGraph* aGraph);
 
   virtual void
   NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
 
 private:
   // Set at construction
-  nsCOMPtr<nsIThread> mMediaThread;
+  base::Thread* mMediaThread;
   uint64_t mWindowID;
 
   bool mStopped; // MainThread only
 
   // Set at Activate on MainThread
 
   // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
   // No locking needed as they're only addrefed except on the MediaManager thread
@@ -286,17 +278,17 @@ class GetUserMediaNotificationEvent: pub
 typedef enum {
   MEDIA_START,
   MEDIA_STOP,
   MEDIA_STOP_TRACK,
   MEDIA_DIRECT_LISTENERS
 } MediaOperation;
 
 class MediaManager;
-class GetUserMediaRunnable;
+class GetUserMediaTask;
 
 /**
  * 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
 {
@@ -327,21 +319,21 @@ public:
 private:
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
 };
 
 // Generic class for running long media operations like Start off the main
 // thread, and then (because nsDOMMediaStreams aren't threadsafe),
 // ProxyReleases mStream since it's cycle collected.
-class MediaOperationRunnable : public nsRunnable
+class MediaOperationTask : public Task
 {
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
-  MediaOperationRunnable(MediaOperation aType,
+  MediaOperationTask(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     DOMMediaStream* aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource,
     bool aBool,
     uint64_t aWindowID,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
@@ -351,67 +343,69 @@ public:
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mListener(aListener)
     , mBool(aBool)
     , mWindowID(aWindowID)
     , mError(aError)
   {}
 
-  ~MediaOperationRunnable()
+  ~MediaOperationTask()
   {
     // MediaStreams can be released on any thread.
   }
 
-  nsresult returnAndCallbackError(nsresult rv, const char* errorLog)
+  void
+  ReturnCallbackError(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));
     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success;
     NS_DispatchToMainThread(new ErrorCallbackRunnable(success, mError,
       log, mWindowID));
-    return NS_OK;
   }
 
-  NS_IMETHOD
-  Run() MOZ_OVERRIDE
+  void
+  Run()
   {
     SourceMediaStream *source = mListener->GetSourceStream();
     // No locking between these is required as all the callbacks for the
     // same MediaStream will occur on the same thread.
     if (!source) // means the stream was never Activated()
-      return NS_OK;
+      return;
 
     switch (mType) {
       case MEDIA_START:
         {
           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
           nsresult rv;
 
           source->SetPullEnabled(true);
 
           DOMMediaStream::TrackTypeHints expectedTracks = 0;
           if (mAudioSource) {
             rv = mAudioSource->Start(source, kAudioTrack);
             if (NS_SUCCEEDED(rv)) {
               expectedTracks |= DOMMediaStream::HINT_CONTENTS_AUDIO;
             } else {
-              return returnAndCallbackError(rv, "Starting audio failed");
+              ReturnCallbackError(rv, "Starting audio failed");
+              return;
             }
           }
           if (mVideoSource) {
             rv = mVideoSource->Start(source, kVideoTrack);
             if (NS_SUCCEEDED(rv)) {
               expectedTracks |= DOMMediaStream::HINT_CONTENTS_VIDEO;
             } else {
-              return returnAndCallbackError(rv, "Starting video failed");
+              ReturnCallbackError(rv, "Starting video failed");
+              return;
             }
           }
 
           mOnTracksAvailableCallback->SetExpectedTracks(expectedTracks);
 
           MM_LOG(("started all sources"));
           // Forward mOnTracksAvailableCallback to GetUserMediaNotificationEvent,
           // because mOnTracksAvailableCallback needs to be added to mStream
@@ -468,17 +462,16 @@ public:
           }
         }
         break;
 
       default:
         MOZ_ASSERT(false,"invalid MediaManager operation");
         break;
     }
-    return NS_OK;
   }
 
 private:
   MediaOperation mType;
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
@@ -532,26 +525,23 @@ class MediaManager MOZ_FINAL : public ns
 {
 public:
   static already_AddRefed<MediaManager> GetInstance();
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
   static MediaManager* Get();
+  static MessageLoop* GetMessageLoop();
 
   static bool Exists()
   {
     return !!sSingleton;
   }
 
-  static nsIThread* GetThread() {
-    return Get()->mMediaThread;
-  }
-
   static nsresult NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
                                               const nsString& aMsg,
                                               const bool& aIsAudio,
                                               const bool& aIsVideo);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIMEDIAMANAGERSERVICE
@@ -608,20 +598,20 @@ private:
 
   void StopScreensharing(uint64_t aWindowID);
   void StopScreensharing(nsPIDOMWindow *aWindow);
 
   void StopMediaStreams();
 
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
-  nsRefPtrHashtable<nsStringHashKey, GetUserMediaRunnable> mActiveCallbacks;
+  nsClassHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
   // Always exists
-  nsCOMPtr<nsIThread> mMediaThread;
+  nsAutoPtr<base::Thread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
 #ifdef MOZ_B2G_CAMERA