Bug 1173255 - Cleanup MediaManager e10s code in prep for deviceId constraint. r=jesup
authorJan-Ivar Bruaroey <jib@mozilla.com>
Thu, 18 Jun 2015 11:46:36 -0400
changeset 280550 53063473eb54c1b4cd701be4d54eead966a51603
parent 280549 eca22a9aca5762dcbe1729c0b1cdf7620600fe64
child 280551 4c42035bc7f8945ca10f799925ec54d25dd7d154
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1173255
milestone41.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 1173255 - Cleanup MediaManager e10s code in prep for deviceId constraint. r=jesup
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/PeerConnection.js
dom/media/nsIDOMNavigatorUserMedia.idl
dom/media/systemservices/MediaChild.cpp
dom/media/systemservices/MediaChild.h
dom/media/systemservices/MediaParent.cpp
dom/media/systemservices/MediaParent.h
dom/media/systemservices/MediaUtils.h
dom/media/systemservices/PMedia.ipdl
dom/media/tests/mochitest/test_getUserMedia_constraints.html
dom/media/webrtc/MediaEngine.h
dom/media/webrtc/MediaEngineCameraVideoSource.cpp
dom/media/webrtc/MediaEngineCameraVideoSource.h
dom/media/webrtc/MediaEngineDefault.cpp
dom/media/webrtc/MediaEngineDefault.h
dom/media/webrtc/MediaEngineGonkVideoSource.cpp
dom/media/webrtc/MediaEngineTabVideoSource.cpp
dom/media/webrtc/MediaEngineTabVideoSource.h
dom/media/webrtc/MediaEngineWebRTC.h
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
dom/media/webrtc/MediaEngineWebRTCVideo.cpp
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -47,16 +47,17 @@
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/PCompositorChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/widget/WidgetMessageUtils.h"
+#include "mozilla/media/MediaChild.h"
 
 #if defined(MOZ_CONTENT_SANDBOX)
 #if defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #elif defined(XP_LINUX)
 #include "mozilla/Sandbox.h"
 #include "mozilla/SandboxInfo.h"
@@ -199,16 +200,17 @@ using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::cellbroadcast;
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::icc;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
+using namespace mozilla::media;
 using namespace mozilla::embedding;
 using namespace mozilla::gmp;
 using namespace mozilla::hal_sandbox;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::psm;
@@ -1745,16 +1747,28 @@ ContentChild::SendPVoicemailConstructor(
 
 bool
 ContentChild::DeallocPVoicemailChild(PVoicemailChild* aActor)
 {
     static_cast<VoicemailIPCService*>(aActor)->Release();
     return true;
 }
 
+media::PMediaChild*
+ContentChild::AllocPMediaChild()
+{
+  return media::AllocPMediaChild();
+}
+
+bool
+ContentChild::DeallocPMediaChild(media::PMediaChild *aActor)
+{
+  return media::DeallocPMediaChild(aActor);
+}
+
 PStorageChild*
 ContentChild::AllocPStorageChild()
 {
     NS_NOTREACHED("We should never be manually allocating PStorageChild actors");
     return nullptr;
 }
 
 bool
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -257,16 +257,19 @@ public:
 
     virtual PTelephonyChild* AllocPTelephonyChild() override;
     virtual bool DeallocPTelephonyChild(PTelephonyChild*) override;
 
     virtual PVoicemailChild* AllocPVoicemailChild() override;
     PVoicemailChild* SendPVoicemailConstructor(PVoicemailChild* aActor);
     virtual bool DeallocPVoicemailChild(PVoicemailChild*) override;
 
+    virtual PMediaChild* AllocPMediaChild() override;
+    virtual bool DeallocPMediaChild(PMediaChild* aActor) override;
+
     virtual PStorageChild* AllocPStorageChild() override;
     virtual bool DeallocPStorageChild(PStorageChild* aActor) override;
 
     virtual PBluetoothChild* AllocPBluetoothChild() override;
     virtual bool DeallocPBluetoothChild(PBluetoothChild* aActor) override;
 
     virtual PFMRadioChild* AllocPFMRadioChild() override;
     virtual bool DeallocPFMRadioChild(PFMRadioChild* aActor) override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -69,16 +69,17 @@
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/LookAndFeel.h"
+#include "mozilla/media/MediaParent.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #ifdef MOZ_ENABLE_PROFILER_SPS
 #include "mozilla/ProfileGatherer.h"
 #endif
@@ -253,16 +254,17 @@ using namespace mozilla::dom::cellbroadc
 using namespace mozilla::dom::devicestorage;
 using namespace mozilla::dom::icc;
 using namespace mozilla::dom::indexedDB;
 using namespace mozilla::dom::power;
 using namespace mozilla::dom::mobileconnection;
 using namespace mozilla::dom::mobilemessage;
 using namespace mozilla::dom::telephony;
 using namespace mozilla::dom::voicemail;
+using namespace mozilla::media;
 using namespace mozilla::embedding;
 using namespace mozilla::gmp;
 using namespace mozilla::hal;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::net;
 using namespace mozilla::jsipc;
 using namespace mozilla::psm;
@@ -3802,16 +3804,28 @@ ContentParent::RecvPVoicemailConstructor
 
 bool
 ContentParent::DeallocPVoicemailParent(PVoicemailParent* aActor)
 {
     static_cast<VoicemailParent*>(aActor)->Release();
     return true;
 }
 
+media::PMediaParent*
+ContentParent::AllocPMediaParent()
+{
+  return media::AllocPMediaParent();
+}
+
+bool
+ContentParent::DeallocPMediaParent(media::PMediaParent *aActor)
+{
+  return media::DeallocPMediaParent(aActor);
+}
+
 PStorageParent*
 ContentParent::AllocPStorageParent()
 {
     return new DOMStorageDBParent();
 }
 
 bool
 ContentParent::DeallocPStorageParent(PStorageParent* aActor)
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -641,16 +641,19 @@ private:
 
     virtual PTelephonyParent* AllocPTelephonyParent() override;
     virtual bool DeallocPTelephonyParent(PTelephonyParent*) override;
 
     virtual PVoicemailParent* AllocPVoicemailParent() override;
     virtual bool RecvPVoicemailConstructor(PVoicemailParent* aActor) override;
     virtual bool DeallocPVoicemailParent(PVoicemailParent* aActor) override;
 
+    virtual PMediaParent* AllocPMediaParent() override;
+    virtual bool DeallocPMediaParent(PMediaParent* aActor) override;
+
     virtual bool DeallocPStorageParent(PStorageParent* aActor) override;
 
     virtual PBluetoothParent* AllocPBluetoothParent() override;
     virtual bool DeallocPBluetoothParent(PBluetoothParent* aActor) override;
     virtual bool RecvPBluetoothConstructor(PBluetoothParent* aActor) override;
 
     virtual PFMRadioParent* AllocPFMRadioParent() override;
     virtual bool DeallocPFMRadioParent(PFMRadioParent* aActor) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -20,16 +20,17 @@ include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PIcc;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
+include protocol PMedia;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
 include protocol PNecko;
 // FIXME This is pretty ridiculous, but we have to keep the order of the
 //       following 4 includes, or the parser is confused about PGMPContent
 //       bridging PContent and PGMP. As soon as it registers the bridge between
 //       PContent and PPluginModule it seems to think that PContent's parent and
 //       child live in the same process!
@@ -424,16 +425,17 @@ prio(normal upto urgent) sync protocol P
     manages PDeviceStorageRequest;
     manages PFileSystemRequest;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
     manages PIcc;
+    manages PMedia;
     manages PMemoryReportRequest;
     manages PMobileConnection;
     manages PNecko;
     manages POfflineCacheUpdate;
     manages PPrinting;
     manages PScreenManager;
     manages PSms;
     manages PSpeechSynthesis;
@@ -759,16 +761,18 @@ parent:
     PSpeechSynthesis();
 
     prio(urgent) async PStorage();
 
     PTelephony();
 
     PVoicemail();
 
+    PMedia();
+
     PBluetooth();
 
     PFMRadio();
 
     PAsmJSCacheEntry(OpenMode openMode, WriteParams write, Principal principal);
 
     PWebrtcGlobal();
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -118,16 +118,19 @@ using dom::MediaStreamConstraints;
 using dom::MediaTrackConstraintSet;
 using dom::MediaTrackConstraints;
 using dom::MediaStreamError;
 using dom::GetUserMediaRequest;
 using dom::Sequence;
 using dom::OwningBooleanOrMediaTrackConstraints;
 using dom::SupportedAudioConstraints;
 using dom::SupportedVideoConstraints;
+using media::Pledge;
+using media::NewRunnableFrom;
+using media::NewTaskFrom;
 
 static Atomic<bool> sInShutdown;
 
 static bool
 HostInDomain(const nsCString &aHost, const nsCString &aPattern)
 {
   int32_t patternOffset = 0;
   int32_t hostOffset = 0;
@@ -262,160 +265,16 @@ private:
 
   nsCOMPtr<SuccessCallbackType> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
   nsRefPtr<MediaMgrError> mError;
   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
- * passed so it can be released correctly.
- */
-class DeviceSuccessCallbackRunnable : public nsRunnable
-{
-public:
-  DeviceSuccessCallbackRunnable(
-    uint64_t aWindowID,
-    nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback>& aOnSuccess,
-    nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
-    nsTArray<nsRefPtr<MediaDevice>>* aDevices,
-    bool aIsGum)
-    : mDevices(aDevices)
-    , mWindowID(aWindowID)
-    , mIsGum(aIsGum)
-    , mManager(MediaManager::GetInstance())
-  {
-    mOnSuccess.swap(aOnSuccess);
-    mOnFailure.swap(aOnFailure);
-  }
-
-  ~DeviceSuccessCallbackRunnable()
-  {
-    if (!NS_IsMainThread()) {
-      // This can happen if the main thread processes the runnable before
-      // GetUserMediaDevicesTask::Run returns.
-      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-
-      NS_ProxyRelease(mainThread, mOnSuccess);
-      NS_ProxyRelease(mainThread, mOnFailure);
-    }
-  }
-
-  nsresult
-  AnonymizeId(nsAString& aId, const nsACString& aOriginKey)
-  {
-    nsresult rv;
-    nsCOMPtr<nsIKeyObjectFactory> factory =
-      do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    nsCString rawKey;
-    rv = Base64Decode(aOriginKey, rawKey);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    nsCOMPtr<nsIKeyObject> key;
-    rv = factory->KeyFromString(nsIKeyObject::HMAC, rawKey, getter_AddRefs(key));
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    nsCOMPtr<nsICryptoHMAC> hasher =
-      do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    rv = hasher->Init(nsICryptoHMAC::SHA256, key);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    NS_ConvertUTF16toUTF8 id(aId);
-    rv = hasher->Update(reinterpret_cast<const uint8_t*> (id.get()), id.Length());
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    nsCString mac;
-    rv = hasher->Finish(true, mac);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    aId = NS_ConvertUTF8toUTF16(mac);
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Run()
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-
-    // Only run if window is still on our active list.
-    if (!mManager->IsWindowStillActive(mWindowID)) {
-      return NS_OK;
-    }
-
-    nsCOMPtr<nsIWritableVariant> devices =
-      do_CreateInstance("@mozilla.org/variant;1");
-
-    size_t len = mDevices->Length();
-
-    if (!len) {
-      if (mIsGum) { // gUM fails on 0 devices whereas enumerateDevices doesn't.
-        // XXX
-        // We should in the future return an empty array, and dynamically add
-        // devices to the dropdowns if things are hotplugged while the
-        // requester is up.
-        nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
-        if (window) {
-          nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
-              NS_LITERAL_STRING("NotFoundError"));
-          mOnFailure->OnError(error);
-        }
-        return NS_OK;
-      }
-      devices->SetAsEmptyArray(); // SetAsArray() fails on zero length arrays.
-    } else {
-      nsTArray<nsIMediaDevice*> tmp(len);
-      for (auto& device : *mDevices) {
-        if (!mOriginKey.IsEmpty()) {
-          nsString id;
-          device->GetId(id);
-          AnonymizeId(id, mOriginKey);
-          device->SetId(id);
-        }
-        tmp.AppendElement(device);
-      }
-      nsresult rv = devices->SetAsArray(nsIDataType::VTYPE_INTERFACE,
-                                        &NS_GET_IID(nsIMediaDevice),
-                                        mDevices->Length(),
-                                        const_cast<void*>(
-                                          static_cast<const void*>(tmp.Elements())
-                                        ));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-    mOnSuccess->OnSuccess(devices);
-    return NS_OK;
-  }
-
-  nsCString mOriginKey;
-private:
-  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
-  nsAutoPtr<nsTArray<nsRefPtr<MediaDevice>>> mDevices;
-  uint64_t mWindowID;
-  bool mIsGum;
-  nsRefPtr<MediaManager> mManager;
-};
-
 // Handle removing GetUserMediaCallbackMediaStreamListener from main thread
 class GetUserMediaListenerRemove: public nsRunnable
 {
 public:
   GetUserMediaListenerRemove(uint64_t aWindowID,
     GetUserMediaCallbackMediaStreamListener *aListener)
     : mWindowID(aWindowID)
     , mListener(aListener) {}
@@ -435,125 +294,49 @@ protected:
 };
 
 /**
  * nsIMediaDevice implementation.
  */
 NS_IMPL_ISUPPORTS(MediaDevice, nsIMediaDevice)
 
 MediaDevice::MediaDevice(MediaEngineSource* aSource)
-  : mHasFacingMode(false)
-  , mSource(aSource) {
+  : mSource(aSource) {
   mSource->GetName(mName);
-  mSource->GetUUID(mID);
+  nsCString id;
+  mSource->GetUUID(id);
+  CopyUTF8toUTF16(id, mID);
 }
 
 VideoDevice::VideoDevice(MediaEngineVideoSource* aSource)
   : MediaDevice(aSource) {
-#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
-  if (mName.EqualsLiteral("back")) {
-    mHasFacingMode = true;
-    mFacingMode = dom::VideoFacingModeEnum::Environment;
-  } else if (mName.EqualsLiteral("front")) {
-    mHasFacingMode = true;
-    mFacingMode = dom::VideoFacingModeEnum::User;
-  }
-#endif // MOZ_B2G_CAMERA
-#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
-  // Names are generated. Example: "Camera 0, Facing back, Orientation 90"
-  //
-  // See media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/
-  // webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
-
-  if (mName.Find(NS_LITERAL_STRING("Facing back")) != kNotFound) {
-    mHasFacingMode = true;
-    mFacingMode = dom::VideoFacingModeEnum::Environment;
-  } else if (mName.Find(NS_LITERAL_STRING("Facing front")) != kNotFound) {
-    mHasFacingMode = true;
-    mFacingMode = dom::VideoFacingModeEnum::User;
-  }
-#endif // ANDROID
-#ifdef XP_MACOSX
-  // Kludge to test user-facing cameras on OSX.
-  if (mName.Find(NS_LITERAL_STRING("Face")) != -1) {
-    mHasFacingMode = true;
-    mFacingMode = dom::VideoFacingModeEnum::User;
-  }
-#endif
   mMediaSource = aSource->GetMediaSource();
 }
 
 /**
  * Helper functions that implement the constraints algorithm from
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
  */
 
 // Reminder: add handling for new constraints both here and in GetSources below!
 
 uint32_t
 VideoDevice::GetBestFitnessDistance(
     const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
 {
-  // TODO: Minimal kludge to fix plain and ideal facingMode regression, for
-  // smooth landing and uplift. Proper cleanup is forthcoming (1037389).
-  uint64_t distance = 0;
-
   // Interrogate device-inherent properties first.
-  for (size_t i = 0; i < aConstraintSets.Length(); i++) {
-    auto& c = *aConstraintSets[i];
-    if (!c.mFacingMode.IsConstrainDOMStringParameters() ||
-        c.mFacingMode.GetAsConstrainDOMStringParameters().mIdeal.WasPassed() ||
-        c.mFacingMode.GetAsConstrainDOMStringParameters().mExact.WasPassed()) {
-      nsString deviceFacingMode;
-      GetFacingMode(deviceFacingMode);
-      if (c.mFacingMode.IsString()) {
-        if (c.mFacingMode.GetAsString() != deviceFacingMode) {
-          if (i == 0) {
-            distance = 1000;
-          }
-        }
-      } else if (c.mFacingMode.IsStringSequence()) {
-        if (!c.mFacingMode.GetAsStringSequence().Contains(deviceFacingMode)) {
-          if (i == 0) {
-            distance = 1000;
-          }
-        }
-      } else if (c.mFacingMode.GetAsConstrainDOMStringParameters().mExact.WasPassed()) {
-        auto& exact = c.mFacingMode.GetAsConstrainDOMStringParameters().mExact.Value();
-        if (exact.IsString()) {
-          if (exact.GetAsString() != deviceFacingMode) {
-            return UINT32_MAX;
-          }
-        } else if (!exact.GetAsStringSequence().Contains(deviceFacingMode)) {
-          return UINT32_MAX;
-        }
-      } else if (c.mFacingMode.GetAsConstrainDOMStringParameters().mIdeal.WasPassed()) {
-        auto& ideal = c.mFacingMode.GetAsConstrainDOMStringParameters().mIdeal.Value();
-        if (ideal.IsString()) {
-          if (ideal.GetAsString() != deviceFacingMode) {
-            if (i == 0) {
-              distance = 1000;
-            }
-          }
-        } else if (!ideal.GetAsStringSequence().Contains(deviceFacingMode)) {
-          if (i == 0) {
-            distance = 1000;
-          }
-        }
-      }
-    }
+  for (const auto& constraint : aConstraintSets) {
     nsString s;
     GetMediaSource(s);
-    if (s != c.mMediaSource) {
+    if (s != constraint->mMediaSource) {
       return UINT32_MAX;
     }
   }
   // Forward request to underlying object to interrogate per-mode capabilities.
-  distance += uint64_t(GetSource()->GetBestFitnessDistance(aConstraintSets));
-  return uint32_t(std::min(distance, uint64_t(UINT32_MAX)));
+  return GetSource()->GetBestFitnessDistance(aConstraintSets);
 }
 
 AudioDevice::AudioDevice(MediaEngineAudioSource* aSource)
   : MediaDevice(aSource) {}
 
 uint32_t
 AudioDevice::GetBestFitnessDistance(
     const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
@@ -598,28 +381,16 @@ MediaDevice::GetId(nsAString& aID)
 
 void
 MediaDevice::SetId(const nsAString& aID)
 {
   mID.Assign(aID);
 }
 
 NS_IMETHODIMP
-MediaDevice::GetFacingMode(nsAString& aFacingMode)
-{
-  if (mHasFacingMode) {
-    aFacingMode.Assign(NS_ConvertUTF8toUTF16(
-        dom::VideoFacingModeEnumValues::strings[uint32_t(mFacingMode)].value));
-  } else {
-    aFacingMode.Truncate(0);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 MediaDevice::GetMediaSource(nsAString& aMediaSource)
 {
   if (mMediaSource == dom::MediaSourceEnum::Microphone) {
     aMediaSource.Assign(NS_LITERAL_STRING("microphone"));
   } else if (mMediaSource == dom::MediaSourceEnum::Window) { // this will go away
     aMediaSource.Assign(NS_LITERAL_STRING("window"));
   } else { // all the rest are shared
     aMediaSource.Assign(NS_ConvertUTF8toUTF16(
@@ -1434,145 +1205,95 @@ public:
     return NS_OK;
   }
 
 private:
   nsAutoPtr<GetUserMediaTask> mTask;
 };
 #endif
 
-class SanitizeDeviceIdsTask : public Task
-{
-public:
-  explicit SanitizeDeviceIdsTask(int64_t aSinceWhen)
-  : mSinceWhen(aSinceWhen) {}
+// TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
+// error: 'this' was not captured for this lambda function
 
-  void // NS_IMETHOD
-  Run()
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    nsRefPtr<media::ChildPledge<bool>> p =
-        mozilla::media::SanitizeOriginKeys(mSinceWhen); // we fire and forget
-  }
-private:
-  int64_t mSinceWhen;
-};
+static auto& MediaManager_GetInstance = MediaManager::GetInstance;
+static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
+static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
 
 /**
- * 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.
- *
- * All code in this class runs on the MediaManager thread.
+ * EnumerateRawDevices - Enumerate a list of audio & video devices that
+ * satisfy passed-in constraints. List contains raw id's.
  */
-class GetUserMediaDevicesTask : public Task
+
+already_AddRefed<MediaManager::PledgeSourceSet>
+MediaManager::EnumerateRawDevices(uint64_t aWindowId,
+                                  const MediaStreamConstraints& aConstraints)
 {
-public:
-  GetUserMediaDevicesTask(
-    const MediaStreamConstraints& aConstraints,
-    already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aOnSuccess,
-    already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
-    uint64_t aWindowId, nsACString& aAudioLoopbackDev,
-    nsACString& aVideoLoopbackDev, bool aPrivileged, const nsACString& aOrigin,
-    bool aInPrivateBrowsing, bool aUseFakeDevices)
-    : mConstraints(aConstraints)
-    , mOnSuccess(aOnSuccess)
-    , mOnFailure(aOnFailure)
-    , mManager(MediaManager::GetInstance())
-    , mWindowId(aWindowId)
-    , mLoopbackAudioDevice(aAudioLoopbackDev)
-    , mLoopbackVideoDevice(aVideoLoopbackDev)
-    , mPrivileged(aPrivileged)
-    , mOrigin(aOrigin)
-    , mInPrivateBrowsing(aInPrivateBrowsing)
-    , mUseFakeDevices(aUseFakeDevices) {}
+  MOZ_ASSERT(NS_IsMainThread());
+  nsRefPtr<PledgeSourceSet> p = new PledgeSourceSet();
+  uint32_t id = mOutstandingPledges.Append(*p);
+
+  // Check if the preference for using audio/video loopback devices is
+  // enabled. This is currently used for automated media tests only.
+  auto audioLoopDev = Preferences::GetCString("media.audio_loopback_dev");
+  auto videoLoopDev = Preferences::GetCString("media.video_loopback_dev");
+  bool fake = Preferences::GetBool("media.navigator.streams.fake", false);
 
-  void // NS_IMETHOD
-  Run()
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
+  MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, aConstraints, aWindowId,
+                                                 audioLoopDev,  videoLoopDev,
+                                                 fake]() mutable {
     nsRefPtr<MediaEngine> backend;
-    if (mConstraints.mFake || mUseFakeDevices)
-      backend = new MediaEngineDefault(mConstraints.mFakeTracks);
-    else
-      backend = mManager->GetBackend(mWindowId);
-
-    typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
+    if (aConstraints.mFake || fake) {
+      backend = new MediaEngineDefault(aConstraints.mFakeTracks);
+    } else {
+      nsRefPtr<MediaManager> manager = MediaManager_GetInstance();
+      backend = manager->GetBackend(aWindowId);
+    }
 
     ScopedDeletePtr<SourceSet> result(new SourceSet);
-    if (IsOn(mConstraints.mVideo)) {
+    if (IsOn(aConstraints.mVideo)) {
       nsTArray<nsRefPtr<VideoDevice>> sources;
-      GetSources(backend, GetInvariant(mConstraints.mVideo),
-                 &MediaEngine::EnumerateVideoDevices, sources,
-                 mLoopbackVideoDevice.get());
+      GetSources(backend, GetInvariant(aConstraints.mVideo),
+                 &MediaEngine::EnumerateVideoDevices, sources, videoLoopDev);
       for (auto& source : sources) {
         result->AppendElement(source);
       }
     }
-    if (IsOn(mConstraints.mAudio)) {
+    if (IsOn(aConstraints.mAudio)) {
       nsTArray<nsRefPtr<AudioDevice>> sources;
-      GetSources(backend, GetInvariant(mConstraints.mAudio),
-                 &MediaEngine::EnumerateAudioDevices, sources,
-                 mLoopbackAudioDevice.get());
+      GetSources(backend, GetInvariant(aConstraints.mAudio),
+                 &MediaEngine::EnumerateAudioDevices, sources, audioLoopDev);
       for (auto& source : sources) {
         result->AppendElement(source);
       }
     }
-    nsRefPtr<DeviceSuccessCallbackRunnable> runnable =
-        new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure,
-                                          result.forget(), mPrivileged);
-    if (mPrivileged) {
-      NS_DispatchToMainThread(runnable);
-    } else {
-      // Get persistent origin-unique uuid to anonymize deviceIds back on main.
-      //
-      // GetOriginKey is an async API that returns a pledge (as promise-like
-      // pattern). We use .Then() to pass in a lambda to run back on this
-      // thread once GetOriginKey resolves asynchronously . The "runnable"
-      // pointer is "captured" (passed by value) into the lambda.
-      nsRefPtr<media::ChildPledge<nsCString>> p =
-          media::GetOriginKey(mOrigin, mInPrivateBrowsing);
-      p->Then([runnable](nsCString result) mutable {
-        runnable->mOriginKey = result;
-        NS_DispatchToMainThread(runnable);
-      });
-    }
-    // One of the Runnables have taken these.
-    MOZ_ASSERT(!mOnSuccess && !mOnFailure);
-  }
-
-private:
-  MediaStreamConstraints mConstraints;
-  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
-  nsRefPtr<MediaManager> mManager;
-  uint64_t mWindowId;
-  const nsString mCallId;
-  // Audio & Video loopback devices to be used based on
-  // the preference settings. This is currently used for
-  // automated media tests only.
-  nsCString mLoopbackAudioDevice;
-  nsCString mLoopbackVideoDevice;
-  bool mPrivileged;
-  nsCString mOrigin;
-  bool mInPrivateBrowsing;
-  bool mUseFakeDevices;
-};
+    SourceSet* handoff = result.forget();
+    NS_DispatchToMainThread(NewRunnableFrom([id, handoff]() mutable {
+      ScopedDeletePtr<SourceSet> result(handoff);
+      nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
+      if (!mgr) {
+        return NS_OK;
+      }
+      nsRefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
+      if (p) {
+        p->Resolve(result.forget());
+      }
+      return NS_OK;
+    }));
+  }));
+  return p.forget();
+}
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mMutex("mozilla::MediaManager")
   , mBackend(nullptr) {
   mPrefs.mWidth  = 0; // adaptive default
   mPrefs.mHeight = 0; // adaptive default
   mPrefs.mFPS    = MediaEngine::DEFAULT_VIDEO_FPS;
   mPrefs.mMinFPS = MediaEngine::DEFAULT_VIDEO_MIN_FPS;
-
   nsresult rv;
   nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
     if (branch) {
       GetPrefs(branch, nullptr);
     }
   }
@@ -1649,16 +1370,25 @@ MediaManager::GetIfExists() {
 /* static */ already_AddRefed<MediaManager>
 MediaManager::GetInstance()
 {
   // so we can have non-refcounted getters
   nsRefPtr<MediaManager> service = MediaManager::Get();
   return service.forget();
 }
 
+media::Parent<media::NonE10s>*
+MediaManager::GetNonE10sParent()
+{
+  if (!mNonE10sParent) {
+    mNonE10sParent = new media::Parent<media::NonE10s>(true);
+  }
+  return mNonE10sParent;
+}
+
 /* static */
 void
 MediaManager::PostTask(const tracked_objects::Location& from_here, Task* task)
 {
   if (sInShutdown) {
     // Can't safely delete task here since it may have items with specific
     // thread-release requirements.
     return;
@@ -1731,32 +1461,30 @@ MediaManager::NotifyRecordingStatusChang
 }
 
 /**
  * The entry point for this file. A call from Navigator::mozGetUserMedia
  * will end up here. MediaManager is a singleton that is responsible
  * for handling all incoming getUserMedia calls from every window.
  */
 nsresult
-MediaManager::GetUserMedia(
-  nsPIDOMWindow* aWindow, const MediaStreamConstraints& aConstraints,
-  nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
-  nsIDOMGetUserMediaErrorCallback* aOnFailure)
+MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
+                           const MediaStreamConstraints& aConstraints,
+                           nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
+                           nsIDOMGetUserMediaErrorCallback* aOnFailure)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-
-  NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE(aOnFailure, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aOnFailure);
+  MOZ_ASSERT(aOnSuccess);
+  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess(aOnSuccess);
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
 
   bool privileged = nsContentUtils::IsCallerChrome();
 
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess(aOnSuccess);
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
-
   MediaStreamConstraints c(aConstraints); // copy
 
   static bool created = false;
   if (!created) {
     // Force MediaManager to startup before we try to access it from other threads
     // Hack: should init singleton earlier unless it's expensive (mem or CPU)
     (void) MediaManager::Get();
 #ifdef MOZ_B2G
@@ -1976,72 +1704,223 @@ MediaManager::GetUserMedia(
 
 #ifdef MOZ_WEBRTC
   EnableWebRtcLog();
 #endif
 
   return NS_OK;
 }
 
-nsresult
-MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
-  const MediaStreamConstraints& aConstraints,
-  nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
-  nsIDOMGetUserMediaErrorCallback* aOnFailure,
-  uint64_t aInnerWindowID,
-  bool aPrivileged)
+/* static */ void
+MediaManager::AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey)
+{
+  if (!aOriginKey.IsEmpty()) {
+    for (auto& device : aDevices) {
+      nsString id;
+      device->GetId(id);
+      AnonymizeId(id, aOriginKey);
+      device->SetId(id);
+    }
+  }
+}
+
+/* static */ nsresult
+MediaManager::AnonymizeId(nsAString& aId, const nsACString& aOriginKey)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv;
+  nsCOMPtr<nsIKeyObjectFactory> factory =
+    do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  nsCString rawKey;
+  rv = Base64Decode(aOriginKey, rawKey);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  nsCOMPtr<nsIKeyObject> key;
+  rv = factory->KeyFromString(nsIKeyObject::HMAC, rawKey, getter_AddRefs(key));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  NS_ENSURE_TRUE(aOnFailure, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_TRUE(!sInShutdown, NS_ERROR_FAILURE);
+  nsCOMPtr<nsICryptoHMAC> hasher =
+    do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  rv = hasher->Init(nsICryptoHMAC::SHA256, key);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  NS_ConvertUTF16toUTF8 id(aId);
+  rv = hasher->Update(reinterpret_cast<const uint8_t*> (id.get()), id.Length());
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+  nsCString mac;
+  rv = hasher->Finish(true, mac);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
+  aId = NS_ConvertUTF8toUTF16(mac);
+  return NS_OK;
+}
 
-  // 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");
-  bool useFakeStreams =
-    Preferences::GetBool("media.navigator.streams.fake", false);
+/* static */
+already_AddRefed<nsIWritableVariant>
+MediaManager::ToJSArray(SourceSet& aDevices)
+{
+  nsCOMPtr<nsIWritableVariant> var = do_CreateInstance("@mozilla.org/variant;1");
+  size_t len = aDevices.Length();
+  if (len) {
+    nsTArray<nsIMediaDevice*> tmp(len);
+    for (auto& device : aDevices) {
+      tmp.AppendElement(device);
+    }
+    auto* elements = static_cast<const void*>(tmp.Elements());
+    nsresult rv = var->SetAsArray(nsIDataType::VTYPE_INTERFACE,
+                                  &NS_GET_IID(nsIMediaDevice), len,
+                                  const_cast<void*>(elements));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+  } else {
+    var->SetAsEmptyArray(); // because SetAsArray() fails on zero length arrays.
+  }
+  return var.forget();
+}
+
+already_AddRefed<MediaManager::PledgeSourceSet>
+MediaManager::EnumerateDevicesImpl(uint64_t aWindowId,
+                                   const MediaStreamConstraints& aConstraints)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // This function returns a pledge, a promise-like object with the future result
+  nsRefPtr<PledgeSourceSet> pledge = new PledgeSourceSet();
+  uint32_t id = mOutstandingPledges.Append(*pledge);
+
+  // To get a device list anonymized for a particular origin, we must:
+  // 1. Get an origin-key (for either regular or private browsing)
+  // 2. Get the raw devices list
+  // 3. Anonymize the raw list with the origin-key.
 
   nsCString origin;
-  nsPrincipal::GetOriginForURI(aWindow->GetDocumentURI(), origin);
-  bool inPrivateBrowsing;
+  bool privateBrowsing;
   {
-    nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
+    nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
+        (nsGlobalWindow::GetInnerWindowWithId(aWindowId));
+    nsPrincipal::GetOriginForURI(window->GetDocumentURI(), origin);
+
+    nsCOMPtr<nsIDocument> doc = window->GetDoc();
     nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
-    inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
+    privateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
   }
-  MediaManager::PostTask(FROM_HERE,
-    new GetUserMediaDevicesTask(
-      aConstraints, onSuccess.forget(), onFailure.forget(),
-      (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
-      loopbackAudioDevice, loopbackVideoDevice, aPrivileged, origin,
-      inPrivateBrowsing, useFakeStreams));
+  // GetOriginKey is an async API that returns a pledge (a promise-like
+  // pattern). We use .Then() to pass in a lambda to run back on this same
+  // thread later once GetOriginKey resolves. Needed variables are "captured"
+  // (passed by value) safely into the lambda.
+
+  nsRefPtr<Pledge<nsCString>> p = media::GetOriginKey(origin, privateBrowsing);
+  p->Then([id, aWindowId, aConstraints](const nsCString& aOriginKey) mutable {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
+
+    nsRefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId, aConstraints);
+    p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
+      ScopedDeletePtr<SourceSet> devices(aDevices); // secondary result
 
-  return NS_OK;
+      // Only run if window is still on our active list.
+      nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
+      if (!mgr) {
+        return NS_OK;
+      }
+      nsRefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
+      if (!p || !mgr->IsWindowStillActive(aWindowId)) {
+        return NS_OK;
+      }
+      MediaManager_AnonymizeDevices(*devices, aOriginKey);
+      p->Resolve(devices.forget());
+      return NS_OK;
+    });
+  });
+  return pledge.forget();
 }
 
 nsresult
 MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
                                nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
                                nsIDOMGetUserMediaErrorCallback* aOnFailure)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  MOZ_ASSERT(NS_IsMainThread());
+  NS_ENSURE_TRUE(!sInShutdown, NS_ERROR_FAILURE);
+  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
+  uint64_t windowId = aWindow->WindowID();
+
+  AddWindowID(windowId);
 
   MediaStreamConstraints c;
   c.mVideo.SetAsBoolean() = true;
   c.mAudio.SetAsBoolean() = true;
 
-  AddWindowID(aWindow->WindowID());
-  return GetUserMediaDevices(aWindow, c, aOnSuccess, aOnFailure, 0, false);
+  nsRefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowId, c);
+  p->Then([onSuccess](SourceSet*& aDevices) mutable {
+    ScopedDeletePtr<SourceSet> devices(aDevices); // grab result
+    nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
+    onSuccess->OnSuccess(array);
+  }, [onFailure](MediaStreamError& reason) mutable {
+    onFailure->OnError(&reason);
+  });
+  return NS_OK;
+}
+
+/*
+ * GetUserMediaDevices - called by the UI-part of getUserMedia from chrome JS.
+ */
+
+nsresult
+MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
+                                  const MediaStreamConstraints& aConstraints,
+                                  nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
+                                  nsIDOMGetUserMediaErrorCallback* aOnFailure,
+                                  uint64_t aWindowId)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
+  if (!aWindowId) {
+    aWindowId = aWindow->WindowID();
+  }
+
+  nsRefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(aWindowId, aConstraints);
+  p->Then([aWindowId, onSuccess, onFailure](SourceSet*& aDevices) mutable {
+    ScopedDeletePtr<SourceSet> devices(aDevices); // grab result
+
+    if (devices->Length()) {
+      nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
+      onSuccess->OnSuccess(array);
+    } else {
+      nsRefPtr<nsGlobalWindow> window = nsGlobalWindow::GetInnerWindowWithId(aWindowId);
+      if (!window) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      nsRefPtr<MediaStreamError> reason =
+          new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
+      onFailure->OnError(reason);
+    }
+    return NS_OK;
+  }, [onFailure](MediaStreamError& reason) mutable {
+    onFailure->OnError(&reason);
+  });
+  return NS_OK;
 }
 
 MediaEngine*
 MediaManager::GetBackend(uint64_t aWindowId)
 {
   // Plugin backends as appropriate. The default engine also currently
   // includes picture support for Android.
   // This IS called off main-thread.
@@ -2545,17 +2424,17 @@ MediaManager::MediaCaptureWindowState(ns
 }
 
 NS_IMETHODIMP
 MediaManager::SanitizeDeviceIds(int64_t aSinceWhen)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   LOG(("%s: sinceWhen = %llu", __FUNCTION__, aSinceWhen));
 
-  MediaManager::PostTask(FROM_HERE, new SanitizeDeviceIdsTask(aSinceWhen));
+  media::SanitizeOriginKeys(aSinceWhen); // we fire and forget
   return NS_OK;
 }
 
 static void
 StopScreensharingCallback(MediaManager *aThis,
                           uint64_t aWindowID,
                           StreamListeners *aListeners,
                           void *aData)
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -22,16 +22,18 @@
 #include "nsIDOMNavigatorUserMedia.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/MediaStreamError.h"
+#include "mozilla/media/MediaChild.h"
+#include "mozilla/media/MediaParent.h"
 #include "mozilla/Logging.h"
 #include "DOMMediaStream.h"
 
 #ifdef MOZ_WEBRTC
 #include "mtransport/runnable_utils.h"
 #endif
 
 // Note, these suck in Windows headers, unfortunately.
@@ -463,18 +465,16 @@ public:
   NS_DECL_NSIMEDIADEVICE
 
   void SetId(const nsAString& aID);
 protected:
   virtual ~MediaDevice() {}
   explicit MediaDevice(MediaEngineSource* aSource);
   nsString mName;
   nsString mID;
-  bool mHasFacingMode;
-  dom::VideoFacingModeEnum mFacingMode;
   dom::MediaSourceEnum mMediaSource;
   nsRefPtr<MediaEngineSource> mSource;
 };
 
 class VideoDevice : public MediaDevice
 {
 public:
   typedef MediaEngineVideoSource Source;
@@ -529,16 +529,17 @@ public:
                                               const nsString& aMsg,
                                               const bool& aIsAudio,
                                               const bool& aIsVideo);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIMEDIAMANAGERSERVICE
 
+  media::Parent<media::NonE10s>* GetNonE10sParent();
   MediaEngine* GetBackend(uint64_t aWindowId = 0);
   StreamListeners *GetWindowListeners(uint64_t aWindowId) {
     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
 
     return mActiveWindows.Get(aWindowId);
   }
   void RemoveWindowID(uint64_t aWindowId);
   bool IsWindowStillActive(uint64_t aWindowId) {
@@ -550,33 +551,47 @@ public:
 
   nsresult GetUserMedia(
     nsPIDOMWindow* aWindow,
     const dom::MediaStreamConstraints& aConstraints,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
 
   nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
-    const dom::MediaStreamConstraints& aConstraints,
-    nsIGetUserMediaDevicesSuccessCallback* onSuccess,
-    nsIDOMGetUserMediaErrorCallback* onError,
-    uint64_t aInnerWindowID = 0,
-    bool aPrivileged = true);
+                               const dom::MediaStreamConstraints& aConstraints,
+                               nsIGetUserMediaDevicesSuccessCallback* onSuccess,
+                               nsIDOMGetUserMediaErrorCallback* onError,
+                               uint64_t aInnerWindowID = 0);
 
   nsresult EnumerateDevices(nsPIDOMWindow* aWindow,
                             nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
                             nsIDOMGetUserMediaErrorCallback* aOnFailure);
 
   nsresult EnumerateDevices(nsPIDOMWindow* aWindow, dom::Promise& aPromise);
   void OnNavigation(uint64_t aWindowID);
   bool IsWindowActivelyCapturing(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
 private:
+  typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
+  typedef media::Pledge<SourceSet*, dom::MediaStreamError> PledgeSourceSet;
+
+  static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
+public: // TODO: make private once we upgrade to GCC 4.8+ on linux.
+  static void AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey);
+  static already_AddRefed<nsIWritableVariant> ToJSArray(SourceSet& aDevices);
+private:
+  already_AddRefed<PledgeSourceSet>
+  EnumerateRawDevices(uint64_t aWindowId,
+                      const dom::MediaStreamConstraints& aConstraints);
+  already_AddRefed<PledgeSourceSet>
+  EnumerateDevicesImpl(uint64_t aWindowId,
+                       const dom::MediaStreamConstraints& aConstraints);
+
   StreamListeners* AddWindowID(uint64_t aWindowId);
   WindowTable *GetActiveWindows() {
     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
     return &mActiveWindows;
   }
 
   void GetPref(nsIPrefBranch *aBranch, const char *aPref,
                const char *aData, int32_t *aVal);
@@ -605,16 +620,20 @@ private:
   nsAutoPtr<base::Thread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
+  media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   nsRefPtr<nsDOMCameraManager> mCameraManager;
 #endif
+public:
+  media::CoatCheck<media::Pledge<nsCString>> mGetOriginKeyPledges;
+  ScopedDeletePtr<media::Parent<media::NonE10s>> mNonE10sParent;
 };
 
 } // namespace mozilla
 
 #endif // MOZILLA_MEDIAMANAGER_H
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -999,16 +999,17 @@ RTCPeerConnection.prototype = {
       dict.maxRetransmits = dict.maxRetransmitNum;
       this.logWarning("Deprecated RTCDataChannelInit dictionary entry maxRetransmitNum used!", null, 0);
     }
     if (dict.outOfOrderAllowed != undefined) {
       dict.ordered = !dict.outOfOrderAllowed; // the meaning is swapped with
                                               // the name change
       this.logWarning("Deprecated RTCDataChannelInit dictionary entry outOfOrderAllowed used!", null, 0);
     }
+
     if (dict.preset != undefined) {
       dict.negotiated = dict.preset;
       this.logWarning("Deprecated RTCDataChannelInit dictionary entry preset used!", null, 0);
     }
     if (dict.stream != undefined) {
       dict.id = dict.stream;
       this.logWarning("Deprecated RTCDataChannelInit dictionary entry stream used!", null, 0);
     }
--- a/dom/media/nsIDOMNavigatorUserMedia.idl
+++ b/dom/media/nsIDOMNavigatorUserMedia.idl
@@ -1,22 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIVariant.idl"
 
-[scriptable, builtinclass, uuid(bbfebbc6-76eb-407c-b77d-363a69bc5c30)]
+[scriptable, builtinclass, uuid(cebcefca-2de1-460d-b253-d0582c50b40f)]
 interface nsIMediaDevice : nsISupports
 {
   readonly attribute DOMString type;
   readonly attribute DOMString name;
   readonly attribute DOMString id;
-  readonly attribute DOMString facingMode;
   readonly attribute DOMString mediaSource;
 };
 
 [scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)]
 interface nsIGetUserMediaDevicesSuccessCallback : nsISupports
 {
   void onSuccess(in nsIVariant devices);
 };
--- a/dom/media/systemservices/MediaChild.cpp
+++ b/dom/media/systemservices/MediaChild.cpp
@@ -1,154 +1,116 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaChild.h"
+#include "MediaParent.h"
 
-#include "mozilla/ipc/BackgroundChild.h"
-#include "mozilla/ipc/PBackgroundChild.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/MediaManager.h"
 #include "mozilla/Logging.h"
 #include "nsQueryObject.h"
 
 #undef LOG
 PRLogModuleInfo *gMediaChildLog;
 #define LOG(args) MOZ_LOG(gMediaChildLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 namespace media {
 
-static Child* sChild;
-
-template<typename ValueType> void
-ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor)
-{
-  if (!sChild) {
-    // Create PMedia by sending a message to the parent
-    sChild = static_cast<Child*>(aActor->SendPMediaConstructor());
-  }
-  Run(sChild);
-}
-
-template<typename ValueType> void
-ChildPledge<ValueType>::ActorFailed()
-{
-  Pledge<ValueType>::Reject(NS_ERROR_UNEXPECTED);
-}
-
-template<typename ValueType> NS_IMPL_ADDREF(ChildPledge<ValueType>)
-template<typename ValueType> NS_IMPL_RELEASE(ChildPledge<ValueType>)
-template<typename ValueType> NS_INTERFACE_MAP_BEGIN(ChildPledge<ValueType>)
-NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
-NS_INTERFACE_MAP_END
-
-already_AddRefed<ChildPledge<nsCString>>
+already_AddRefed<Pledge<nsCString>>
 GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing)
 {
-  class Pledge : public ChildPledge<nsCString>
-  {
-  public:
-    explicit Pledge(const nsCString& aOrigin, bool aPrivateBrowsing)
-    : mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {}
-  private:
-    ~Pledge() {}
-    void Run(PMediaChild* aChild)
-    {
-      Child* child = static_cast<Child*>(aChild);
+  nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
+  MOZ_ASSERT(mgr);
+
+  nsRefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
+  uint32_t id = mgr->mGetOriginKeyPledges.Append(*p);
 
-      uint32_t id = child->AddRequestPledge(*this);
-      child->SendGetOriginKey(id, mOrigin, mPrivateBrowsing);
-    }
-    const nsCString mOrigin;
-    const bool mPrivateBrowsing;
-  };
-
-  nsRefPtr<ChildPledge<nsCString>> p = new Pledge(aOrigin, aPrivateBrowsing);
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
-  bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
-  MOZ_RELEASE_ASSERT(ok);
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    mgr->GetNonE10sParent()->RecvGetOriginKey(id, aOrigin, aPrivateBrowsing);
+  } else {
+    Child::Get()->SendGetOriginKey(id, aOrigin, aPrivateBrowsing);
+  }
   return p.forget();
 }
 
-already_AddRefed<ChildPledge<bool>>
+void
 SanitizeOriginKeys(const uint64_t& aSinceWhen)
 {
-  class Pledge : public ChildPledge<bool>
-  {
-  public:
-    explicit Pledge(const uint64_t& aSinceWhen) : mSinceWhen(aSinceWhen) {}
-  private:
-    void Run(PMediaChild* aMedia)
-    {
-      aMedia->SendSanitizeOriginKeys(mSinceWhen);
-      mValue = true;
-      LOG(("SanitizeOriginKeys since %llu", mSinceWhen));
-      Resolve();
-    }
-    const uint64_t mSinceWhen;
-  };
+  LOG(("SanitizeOriginKeys since %llu", aSinceWhen));
 
-  nsRefPtr<ChildPledge<bool>> p = new Pledge(aSinceWhen);
-  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
-  bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
-  MOZ_RELEASE_ASSERT(ok);
-  return p.forget();
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    // Avoid opening MediaManager in this case, since this is called by
+    // sanitize.js when cookies are cleared, which can happen on startup.
+    ScopedDeletePtr<Parent<NonE10s>> tmpParent(new Parent<NonE10s>(true));
+    tmpParent->RecvSanitizeOriginKeys(aSinceWhen);
+  } else {
+    Child::Get()->SendSanitizeOriginKeys(aSinceWhen);
+  }
+}
+
+static Child* sChild;
+
+Child* Child::Get()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content);
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!sChild) {
+    sChild = static_cast<Child*>(dom::ContentChild::GetSingleton()->SendPMediaConstructor());
+  }
+  return sChild;
 }
 
 Child::Child()
+  : mActorDestroyed(false)
 {
   if (!gMediaChildLog) {
     gMediaChildLog = PR_NewLogModule("MediaChild");
   }
   LOG(("media::Child: %p", this));
   MOZ_COUNT_CTOR(Child);
 }
 
 Child::~Child()
 {
   LOG(("~media::Child: %p", this));
   sChild = nullptr;
   MOZ_COUNT_DTOR(Child);
 }
 
-uint32_t
-Child::AddRequestPledge(ChildPledge<nsCString>& aPledge)
+void Child::ActorDestroy(ActorDestroyReason aWhy)
 {
-  return mRequestPledges.Append(aPledge);
-}
-
-already_AddRefed<ChildPledge<nsCString>>
-Child::RemoveRequestPledge(uint32_t aRequestId)
-{
-  return mRequestPledges.Remove(aRequestId);
+  mActorDestroyed = true;
 }
 
 bool
 Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey)
 {
-  nsRefPtr<ChildPledge<nsCString>> pledge = RemoveRequestPledge(aRequestId);
+  nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
+  if (!mgr) {
+    return false;
+  }
+  nsRefPtr<Pledge<nsCString>> pledge = mgr->mGetOriginKeyPledges.Remove(aRequestId);
   if (pledge) {
     pledge->Resolve(aKey);
   }
   return true;
 }
 
 PMediaChild*
 AllocPMediaChild()
 {
-  Child* obj = new Child();
-  obj->AddRef();
-  return obj;
+  return new Child();
 }
 
 bool
 DeallocPMediaChild(media::PMediaChild *aActor)
 {
-  static_cast<Child*>(aActor)->Release();
+  delete static_cast<Child*>(aActor);
   return true;
 }
 
 }
 }
--- a/dom/media/systemservices/MediaChild.h
+++ b/dom/media/systemservices/MediaChild.h
@@ -5,67 +5,50 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_MediaChild_h
 #define mozilla_MediaChild_h
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/media/PMediaChild.h"
 #include "mozilla/media/PMediaParent.h"
-#include "nsIIPCBackgroundChildCreateCallback.h"
 #include "MediaUtils.h"
 
 namespace mozilla {
 namespace media {
 
 // media::Child implements proxying to the chrome process for some media-related
 // functions, for the moment just:
 //
 // GetOriginKey() - get a cookie-like persisted unique key for a given origin.
 // SanitizeOriginKeys() - reset persisted unique keys.
 
 // GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
 // (promise-like objects) with the future value. Use pledge.Then(func) to access.
 
-class Child;
-
-template<typename ValueType>
-class ChildPledge : public Pledge<ValueType>,
-                    public nsIIPCBackgroundChildCreateCallback
-{
-  friend Child;
-  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
-  NS_DECL_ISUPPORTS
-public:
-  explicit ChildPledge() {};
-protected:
-  virtual ~ChildPledge() {}
-  virtual void Run(PMediaChild* aMedia) = 0;
-};
-
-already_AddRefed<ChildPledge<nsCString>>
+already_AddRefed<Pledge<nsCString>>
 GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing);
 
-already_AddRefed<ChildPledge<bool>>
+void
 SanitizeOriginKeys(const uint64_t& aSinceWhen);
 
 class Child : public PMediaChild
 {
-  NS_INLINE_DECL_REFCOUNTING(Child)
 public:
+  static Child* Get();
+
   Child();
 
   bool RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey);
 
-  uint32_t AddRequestPledge(ChildPledge<nsCString>& aPledge);
-  already_AddRefed<ChildPledge<nsCString>> RemoveRequestPledge(uint32_t aRequestId);
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  virtual ~Child();
 private:
-  virtual ~Child();
 
-  CoatCheck<ChildPledge<nsCString>> mRequestPledges;
+  bool mActorDestroyed;
 };
 
 PMediaChild* AllocPMediaChild();
 bool DeallocPMediaChild(PMediaChild *aActor);
 
 } // namespace media
 } // namespace mozilla
 
--- a/dom/media/systemservices/MediaParent.cpp
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -27,23 +27,23 @@ PRLogModuleInfo *gMediaParentLog;
 // deviceIds to be unique per origin, to avoid them being supercookies.
 
 #define ORIGINKEYS_FILE "enumerate_devices.txt"
 #define ORIGINKEYS_VERSION "1"
 
 namespace mozilla {
 namespace media {
 
-static StaticMutex gMutex;
-static ParentSingleton* sParentSingleton = nullptr;
+static Parent<PMediaParent>* sIPCServingParent;
 
-class ParentSingleton : public nsISupports
+static OriginKeyStore* sOriginKeyStore = nullptr;
+
+class OriginKeyStore : public nsISupports
 {
   NS_DECL_THREADSAFE_ISUPPORTS
-
   class OriginKey
   {
   public:
     static const size_t DecodedLength = 18;
     static const size_t EncodedLength = DecodedLength * 4 / 3;
 
     OriginKey(const nsACString& aKey, int64_t aSecondsStamp)
     : mKey(aKey)
@@ -319,200 +319,220 @@ class ParentSingleton : public nsISuppor
         Load();
       }
     }
   private:
     nsCOMPtr<nsIFile> mProfileDir;
   };
 
 private:
-  virtual ~ParentSingleton()
+  virtual ~OriginKeyStore()
   {
-    sParentSingleton = nullptr;
+    sOriginKeyStore = nullptr;
     LOG((__FUNCTION__));
   }
 
 public:
-  static ParentSingleton* Get()
+  static OriginKeyStore* Get()
   {
-    // Protect creation of singleton and access from multiple Background threads.
-    //
-    // Multiple Background threads happen because sanitize.js calls us from the
-    // chrome process and gets a thread separate from the one servicing ipc from
-    // the content process.
-
-    StaticMutexAutoLock lock(gMutex);
-    if (!sParentSingleton) {
-      sParentSingleton = new ParentSingleton();
+    MOZ_ASSERT(NS_IsMainThread());
+    if (!sOriginKeyStore) {
+      sOriginKeyStore = new OriginKeyStore();
     }
-    return sParentSingleton;
+    return sOriginKeyStore;
   }
 
   // Only accessed on StreamTS thread
   OriginKeysLoader mOriginKeys;
   OriginKeysTable mPrivateBrowsingOriginKeys;
-
-  // Only accessed on return thread
-  CoatCheck<Pledge<nsCString>> mOutstandingPledges;
 };
 
-NS_IMPL_ISUPPORTS0(ParentSingleton)
+NS_IMPL_ISUPPORTS0(OriginKeyStore)
+
+template<> /* static */
+Parent<PMediaParent>* Parent<PMediaParent>::GetSingleton()
+{
+  return sIPCServingParent;
+}
 
-bool
-Parent::RecvGetOriginKey(const uint32_t& aRequestId,
+template<> /* static */
+Parent<NonE10s>* Parent<NonE10s>::GetSingleton()
+{
+  nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
+  if (!mgr) {
+    return nullptr;
+  }
+  return mgr->GetNonE10sParent();
+}
+
+// TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
+// error: 'this' was not captured for this lambda function
+
+template<class Super> static
+Parent<Super>* GccGetSingleton() { return Parent<Super>::GetSingleton(); };
+
+
+template<class Super> bool
+Parent<Super>::RecvGetOriginKey(const uint32_t& aRequestId,
                          const nsCString& aOrigin,
                          const bool& aPrivateBrowsing)
 {
-  // TODO: Replace all this when moving MediaParent to PContent soon (1037389)
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // First, get profile dir.
 
-  nsRefPtr<ParentSingleton> singleton(mSingleton);
-  nsCOMPtr<nsIThread> returnThread = NS_GetCurrentThread();
-  nsRefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
-  nsresult rv;
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIFile> profileDir;
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                       getter_AddRefs(profileDir));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
 
-  // First, over to main thread to get profile dir.
+  // Then over to stream-transport thread to do the actual file io.
+  // Stash a pledge to hold the answer and get an id for this request.
 
-  // Pledges are non-threadsafe by design, so check them and pass an id instead.
-  uint32_t id = singleton->mOutstandingPledges.Append(*p);
+  nsRefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
+  uint32_t id = mOutstandingPledges.Append(*p);
+
+  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(sts);
+  nsRefPtr<OriginKeyStore> store(mOriginKeyStore);
+  bool sameProcess = mSameProcess;
 
-  rv = NS_DispatchToMainThread(NewRunnableFrom([id, returnThread,
-                                                singleton, aOrigin,
-                                                aPrivateBrowsing]() -> nsresult {
-    MOZ_ASSERT(NS_IsMainThread());
-    nsCOMPtr<nsIFile> profileDir;
-    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                                         getter_AddRefs(profileDir));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+  rv = sts->Dispatch(NewRunnableFrom([id, profileDir, store, sameProcess,
+                                      aOrigin, aPrivateBrowsing]() -> nsresult {
+    MOZ_ASSERT(!NS_IsMainThread());
+    store->mOriginKeys.SetProfileDir(profileDir);
+    nsCString result;
+    if (aPrivateBrowsing) {
+      store->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, result);
+    } else {
+      store->mOriginKeys.GetOriginKey(aOrigin, result);
     }
 
-    // Then from there over to stream-transport thread to do the actual file io.
-
-    nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-    MOZ_ASSERT(sts);
-    rv = sts->Dispatch(NewRunnableFrom([profileDir, id, returnThread, singleton,
-                                        aOrigin, aPrivateBrowsing]() -> nsresult {
-      MOZ_ASSERT(!NS_IsMainThread());
-      singleton->mOriginKeys.SetProfileDir(profileDir);
-      nsCString result;
-      if (aPrivateBrowsing) {
-        singleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, result);
-      } else {
-        singleton->mOriginKeys.GetOriginKey(aOrigin, result);
+    // Pass result back to main thread.
+    nsresult rv;
+    rv = NS_DispatchToMainThread(NewRunnableFrom([id, store, sameProcess,
+                                                  result]() -> nsresult {
+      Parent* parent = GccGetSingleton<Super>(); // GetSingleton();
+      if (!parent) {
+        return NS_OK;
       }
-
-      // Pass result back to original thread.
-      nsresult rv;
-      rv = returnThread->Dispatch(NewRunnableFrom([id, singleton,
-                                                   result]() -> nsresult {
-        nsRefPtr<Pledge<nsCString>> p = singleton->mOutstandingPledges.Remove(id);
-        if (!p) {
-          return NS_ERROR_UNEXPECTED;
-        }
-        p->Resolve(result);
-        return NS_OK;
-      }), NS_DISPATCH_NORMAL);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
+      nsRefPtr<Pledge<nsCString>> p = parent->mOutstandingPledges.Remove(id);
+      if (!p) {
+        return NS_ERROR_UNEXPECTED;
       }
+      p->Resolve(result);
       return NS_OK;
     }), NS_DISPATCH_NORMAL);
+
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return NS_OK;
-  }));
+  }), NS_DISPATCH_NORMAL);
+
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
-  nsRefPtr<media::Parent> keepAlive(this);
-  p->Then([this, keepAlive, aRequestId](const nsCString& aKey) mutable {
-    if (!mDestroyed) {
-      unused << SendGetOriginKeyResponse(aRequestId, aKey);
+  p->Then([aRequestId, sameProcess](const nsCString& aKey) mutable {
+    if (!sameProcess) {
+      if (!sIPCServingParent) {
+        return NS_OK;
+      }
+      unused << sIPCServingParent->SendGetOriginKeyResponse(aRequestId, aKey);
+    } else {
+      nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
+      if (!mgr) {
+        return NS_OK;
+      }
+      nsRefPtr<Pledge<nsCString>> pledge =
+          mgr->mGetOriginKeyPledges.Remove(aRequestId);
+      if (pledge) {
+        pledge->Resolve(aKey);
+      }
     }
     return NS_OK;
   });
   return true;
 }
 
-bool
-Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen)
+template<class Super> bool
+Parent<Super>::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen)
 {
-  nsRefPtr<ParentSingleton> singleton(mSingleton);
-
-  // First, over to main to get profile dir.
-  nsresult rv;
-
-  rv = NS_DispatchToMainThread(NewRunnableFrom([singleton,
-                                                aSinceWhen]() -> nsresult {
-    MOZ_ASSERT(NS_IsMainThread());
-    nsCOMPtr<nsIFile> profileDir;
-    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+  MOZ_ASSERT(NS_IsMainThread());
+  nsCOMPtr<nsIFile> profileDir;
+  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                          getter_AddRefs(profileDir));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    // Then from there over to stream-transport thread to do the file io.
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+  // Over to stream-transport thread to do the file io.
 
-    nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
-    MOZ_ASSERT(sts);
-    rv = sts->Dispatch(NewRunnableFrom([profileDir, singleton, aSinceWhen]() -> nsresult {
-      MOZ_ASSERT(!NS_IsMainThread());
-      singleton->mOriginKeys.SetProfileDir(profileDir);
-      singleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
-      singleton->mOriginKeys.Clear(aSinceWhen);
-      return NS_OK;
-    }), NS_DISPATCH_NORMAL);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
+  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(sts);
+  nsRefPtr<OriginKeyStore> store(mOriginKeyStore);
+
+  rv = sts->Dispatch(NewRunnableFrom([profileDir, store, aSinceWhen]() -> nsresult {
+    MOZ_ASSERT(!NS_IsMainThread());
+    store->mOriginKeys.SetProfileDir(profileDir);
+    store->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
+    store->mOriginKeys.Clear(aSinceWhen);
     return NS_OK;
-  }));
+  }), NS_DISPATCH_NORMAL);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
   return true;
 }
 
-void
-Parent::ActorDestroy(ActorDestroyReason aWhy)
+template<class Super> void
+Parent<Super>::ActorDestroy(ActorDestroyReason aWhy)
 {
   // No more IPC from here
   mDestroyed = true;
   LOG((__FUNCTION__));
 }
 
-Parent::Parent()
-  : mSingleton(ParentSingleton::Get())
+template<class Super>
+Parent<Super>::Parent(bool aSameProcess)
+  : mOriginKeyStore(OriginKeyStore::Get())
   , mDestroyed(false)
+  , mSameProcess(aSameProcess)
 {
   if (!gMediaParentLog)
     gMediaParentLog = PR_NewLogModule("MediaParent");
   LOG(("media::Parent: %p", this));
 
   MOZ_COUNT_CTOR(Parent);
 }
 
-Parent::~Parent()
+template<class Super>
+Parent<Super>::~Parent()
 {
   LOG(("~media::Parent: %p", this));
 
   MOZ_COUNT_DTOR(Parent);
 }
 
 PMediaParent*
 AllocPMediaParent()
 {
-  Parent* obj = new Parent();
-  obj->AddRef();
-  return obj;
+  MOZ_ASSERT(!sIPCServingParent);
+  sIPCServingParent = new Parent<PMediaParent>();
+  return sIPCServingParent;
 }
 
 bool
 DeallocPMediaParent(media::PMediaParent *aActor)
 {
-  static_cast<Parent*>(aActor)->Release();
+  MOZ_ASSERT(sIPCServingParent == static_cast<Parent<PMediaParent>*>(aActor));
+  delete sIPCServingParent;
   return true;
 }
 
 }
 }
+
+// Instantiate templates to satisfy linker
+template class mozilla::media::Parent<mozilla::media::NonE10s>;
--- a/dom/media/systemservices/MediaParent.h
+++ b/dom/media/systemservices/MediaParent.h
@@ -11,35 +11,58 @@
 
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/media/PMediaParent.h"
 
 namespace mozilla {
 namespace media {
 
 // media::Parent implements the chrome-process side of ipc for media::Child APIs
+// A "SameProcess" version may also be created to service non-e10s calls.
 
-class ParentSingleton;
+class OriginKeyStore;
 
-class Parent : public PMediaParent
+class NonE10s
 {
-  NS_INLINE_DECL_REFCOUNTING(Parent)
+  typedef mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason
+      ActorDestroyReason;
+protected:
+  virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
+                                const nsCString& aOrigin,
+                                const bool& aPrivateBrowsing) = 0;
+  virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) = 0;
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) = 0;
+};
+
+// Super = PMediaParent or NonE10s
+
+template<class Super>
+class Parent : public Super
+{
+  typedef mozilla::ipc::IProtocolManager<mozilla::ipc::IProtocol>::ActorDestroyReason
+      ActorDestroyReason;
 public:
+  static Parent* GetSingleton();
+
   virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
                                 const nsCString& aOrigin,
                                 const bool& aPrivateBrowsing) override;
   virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override;
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
-  Parent();
-private:
+  explicit Parent(bool aSameProcess = false);
   virtual ~Parent();
+private:
 
-  nsRefPtr<ParentSingleton> mSingleton;
+  nsRefPtr<OriginKeyStore> mOriginKeyStore;
   bool mDestroyed;
+  bool mSameProcess;
+
+  CoatCheck<Pledge<nsCString>> mOutstandingPledges;
 };
 
 PMediaParent* AllocPMediaParent();
 bool DeallocPMediaParent(PMediaParent *aActor);
 
 } // namespace media
 } // namespace mozilla
 
--- a/dom/media/systemservices/MediaUtils.h
+++ b/dom/media/systemservices/MediaUtils.h
@@ -35,110 +35,212 @@ namespace media {
  *   nsRefPtr<media::Pledge<Foo>> p = GetFooAsynchronously(); // returns a pledge
  *   p->Then([](const Foo& foo) {
  *     // use foo here (same thread. Need not be thread-safe!)
  *   });
  *
  * See media::CoatCheck below for an example of GetFooAsynchronously().
  */
 
-template<typename ValueType>
-class Pledge
+class PledgeBase
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(PledgeBase);
+protected:
+  virtual ~PledgeBase() {};
+};
+
+template<typename ValueType, typename ErrorType = nsresult>
+class Pledge : public PledgeBase
 {
   // TODO: Remove workaround once mozilla allows std::function from <functional>
   // wo/std::function support, do template + virtual trick to accept lambdas
   class FunctorsBase
   {
   public:
     FunctorsBase() {}
-    virtual void Succeed(const ValueType& result) = 0;
-    virtual void Fail(nsresult rv) = 0;
+    virtual void Succeed(ValueType& result) = 0;
+    virtual void Fail(ErrorType& error) = 0;
     virtual ~FunctorsBase() {};
   };
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(Pledge);
-  explicit Pledge() : mDone(false), mResult(NS_OK) {}
+  explicit Pledge() : mDone(false), mError(nullptr) {}
+  Pledge(const Pledge& aOther) = delete;
+  Pledge& operator = (const Pledge&) = delete;
 
   template<typename OnSuccessType>
   void Then(OnSuccessType aOnSuccess)
   {
-    Then(aOnSuccess, [](nsresult){});
+    Then(aOnSuccess, [](ErrorType&){});
   }
 
   template<typename OnSuccessType, typename OnFailureType>
   void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
   {
-    class F : public FunctorsBase
+    class Functors : public FunctorsBase
     {
     public:
-      F(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
+      Functors(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
         : mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
 
-      void Succeed(const ValueType& result)
+      void Succeed(ValueType& result)
       {
         mOnSuccess(result);
       }
-      void Fail(nsresult rv)
+      void Fail(ErrorType& error)
       {
-        mOnFailure(rv);
+        mOnFailure(error);
       };
 
       OnSuccessType mOnSuccess;
       OnFailureType mOnFailure;
     };
-    mFunctors = new F(aOnSuccess, aOnFailure);
+    mFunctors = new Functors(aOnSuccess, aOnFailure);
 
     if (mDone) {
-      if (mResult == NS_OK) {
+      if (!mError) {
         mFunctors->Succeed(mValue);
       } else {
-        mFunctors->Fail(mResult);
+        mFunctors->Fail(*mError);
       }
     }
   }
 
   void Resolve(const ValueType& aValue)
   {
     mValue = aValue;
     Resolve();
   }
 protected:
   void Resolve()
   {
     if (!mDone) {
       mDone = true;
-      MOZ_ASSERT(mResult == NS_OK);
+      MOZ_ASSERT(!mError);
       if (mFunctors) {
         mFunctors->Succeed(mValue);
       }
     }
   }
 
-  void Reject(nsresult rv)
+  void Reject(ErrorType rv)
   {
     if (!mDone) {
       mDone = true;
-      mResult = rv;
+      mError = rv;
       if (mFunctors) {
-        mFunctors->Fail(mResult);
+        mFunctors->Fail(mError);
       }
     }
   }
 
   ValueType mValue;
-protected:
+private:
   ~Pledge() {};
   bool mDone;
-  nsresult mResult;
+  nsRefPtr<ErrorType> mError;
+  ScopedDeletePtr<FunctorsBase> mFunctors;
+};
+
+template<typename ValueType>
+class Pledge<ValueType, nsresult>  : public PledgeBase
+{
+  // TODO: Remove workaround once mozilla allows std::function from <functional>
+  // wo/std::function support, do template + virtual trick to accept lambdas
+  class FunctorsBase
+  {
+  public:
+    FunctorsBase() {}
+    virtual void Succeed(ValueType& result) = 0;
+    virtual void Fail(nsresult error) = 0;
+    virtual ~FunctorsBase() {};
+  };
+
+public:
+  explicit Pledge() : mDone(false), mError(NS_OK) {}
+  Pledge(const Pledge& aOther) = delete;
+  Pledge& operator = (const Pledge&) = delete;
+
+  template<typename OnSuccessType>
+  void Then(OnSuccessType aOnSuccess)
+  {
+    Then(aOnSuccess, [](nsresult){});
+  }
+
+  template<typename OnSuccessType, typename OnFailureType>
+  void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
+  {
+    class Functors : public FunctorsBase
+    {
+    public:
+      Functors(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
+        : mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
+
+      void Succeed(ValueType& result)
+      {
+        mOnSuccess(result);
+      }
+      void Fail(nsresult rv)
+      {
+        mOnFailure(rv);
+      };
+
+      OnSuccessType mOnSuccess;
+      OnFailureType mOnFailure;
+    };
+    mFunctors = new Functors(aOnSuccess, aOnFailure);
+
+    if (mDone) {
+      if (mError == NS_OK) {
+        mFunctors->Succeed(mValue);
+      } else {
+        mFunctors->Fail(mError);
+      }
+    }
+  }
+
+  void Resolve(const ValueType& aValue)
+  {
+    mValue = aValue;
+    Resolve();
+  }
+protected:
+  void Resolve()
+  {
+    if (!mDone) {
+      mDone = true;
+      MOZ_ASSERT(mError == NS_OK);
+      if (mFunctors) {
+        mFunctors->Succeed(mValue);
+      }
+    }
+  }
+
+  void Reject(nsresult error)
+  {
+    if (!mDone) {
+      mDone = true;
+      mError = error;
+      if (mFunctors) {
+        mFunctors->Fail(mError);
+      }
+    }
+  }
+
+  ValueType mValue;
 private:
-  nsAutoPtr<FunctorsBase> mFunctors;
+  ~Pledge() {};
+  bool mDone;
+  nsresult mError;
+  ScopedDeletePtr<FunctorsBase> mFunctors;
 };
 
 /* media::NewRunnableFrom() - Create an nsRunnable from a lambda.
+ * media::NewTaskFrom()     - Create a Task from a lambda.
  *
  * Passing variables (closures) to an async function is clunky with nsRunnable:
  *
  *   void Foo()
  *   {
  *     class FooRunnable : public nsRunnable
  *     {
  *     public:
@@ -187,16 +289,37 @@ private:
 
 template<typename OnRunType>
 LambdaRunnable<OnRunType>*
 NewRunnableFrom(OnRunType aOnRun)
 {
   return new LambdaRunnable<OnRunType>(aOnRun);
 }
 
+template<typename OnRunType>
+class LambdaTask : public Task
+{
+public:
+  explicit LambdaTask(OnRunType& aOnRun) : mOnRun(aOnRun) {}
+private:
+  void
+  Run()
+  {
+    return mOnRun();
+  }
+  OnRunType mOnRun;
+};
+
+template<typename OnRunType>
+LambdaTask<OnRunType>*
+NewTaskFrom(OnRunType aOnRun)
+{
+  return new LambdaTask<OnRunType>(aOnRun);
+}
+
 /* media::CoatCheck - There and back again. Park an object in exchange for an id.
  *
  * A common problem with calling asynchronous functions that do work on other
  * threads or processes is how to pass in a heap object for use once the
  * function completes, without requiring that object to have threadsafe
  * refcounting, contain mutexes, be marshaled, or leak if things fail
  * (or worse, intermittent use-after-free because of lifetime issues).
  *
@@ -208,40 +331,40 @@ NewRunnableFrom(OnRunType aOnRun)
  *   class FooDoer
  *   {
  *     CoatCheck<Foo> mOutstandingFoos;
  *
  *   public:
  *     void DoFoo()
  *     {
  *       nsRefPtr<Foo> foo = new Foo();
- *       uint32_t requestId = mOutstandingFoos.Append(foo);
+ *       uint32_t requestId = mOutstandingFoos.Append(*foo);
  *       sChild->SendFoo(requestId);
  *     }
  *
  *     void RecvFooResponse(uint32_t requestId)
  *     {
  *       nsRefPtr<Foo> foo = mOutstandingFoos.Remove(requestId);
  *       if (foo) {
  *         // use foo
  *       }
  *     }
  *   };
  *
  * If you read media::Pledge earlier, here's how this is useful for pledges:
  *
  *   class FooGetter
  *   {
- *     CoatCheck<Foo> mOutstandingPledges;
+ *     CoatCheck<Pledge<Foo>> mOutstandingPledges;
  *
  *   public:
  *     already_addRefed<Pledge<Foo>> GetFooAsynchronously()
  *     {
  *       nsRefPtr<Pledge<Foo>> p = new Pledge<Foo>();
- *       uint32_t requestId = mOutstandingPledges.Append(p);
+ *       uint32_t requestId = mOutstandingPledges.Append(*p);
  *       sChild->SendFoo(requestId);
  *       return p.forget();
  *     }
  *
  *     void RecvFooResponse(uint32_t requestId, const Foo& fooResult)
  *     {
  *       nsRefPtr<Foo> p = mOutstandingPledges.Remove(requestId);
  *       if (p) {
--- a/dom/media/systemservices/PMedia.ipdl
+++ b/dom/media/systemservices/PMedia.ipdl
@@ -1,21 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
-include protocol PBackground;
 
 namespace mozilla {
 namespace media {
 
 protocol PMedia
 {
-  manager PBackground;
+  manager PContent;
 
 parent:
   /**
    * Requests a persistent unique secret key for each origin.
    * Has no expiry, but is cleared by age along with cookies.
    * This is needed by mediaDevices.enumerateDevices() to produce persistent
    * deviceIds that wont work cross-origin.
    */
--- a/dom/media/tests/mochitest/test_getUserMedia_constraints.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_constraints.html
@@ -18,24 +18,16 @@ var tests = [
   { message: "unknown required constraint on video ignored",
     constraints: { video: { somethingUnknown: { exact: 0 } },
                    fake: true },
     error: null },
   { message: "unknown required constraint on audio ignored",
     constraints: { audio: { somethingUnknown: { exact: 0 } },
                    fake: true },
     error: null },
-  { message: "video overconstrained by facingMode fails",
-    constraints: { video: { facingMode:{ exact: 'left' } },
-                   fake: true },
-    error: "NotFoundError" },
-  { message: "video overconstrained by facingMode array fails",
-    constraints: { video: { facingMode:{ exact: ['left', 'right'] } },
-                   fake: true },
-    error: "NotFoundError" },
   { message: "audio overconstrained by facingMode ignored",
     constraints: { audio: { facingMode: { exact: 'left' } },
                    fake: true },
     error: null },
   { message: "full screensharing requires permission",
     constraints: { video: { mediaSource: 'screen' } },
     error: "PermissionDeniedError" },
   { message: "application screensharing requires permission",
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -83,18 +83,18 @@ public:
 
   virtual ~MediaEngineSource() {}
 
   virtual void Shutdown() = 0;
 
   /* Populate the human readable name of this device in the nsAString */
   virtual void GetName(nsAString&) = 0;
 
-  /* Populate the UUID of this device in the nsAString */
-  virtual void GetUUID(nsAString&) = 0;
+  /* Populate the UUID of this device in the nsACString */
+  virtual void GetUUID(nsACString&) = 0;
 
   /* Release the device back to the system. */
   virtual nsresult Deallocate() = 0;
 
   /* Start the device and add the track to the provided SourceMediaStream, with
    * the provided TrackID. You may start appending data to the track
    * immediately after. */
   virtual nsresult Start(SourceMediaStream*, TrackID) = 0;
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
@@ -4,21 +4,17 @@
 
 #include "MediaEngineCameraVideoSource.h"
 
 #include <limits>
 
 namespace mozilla {
 
 using namespace mozilla::gfx;
-using dom::OwningLongOrConstrainLongRange;
-using dom::ConstrainLongRange;
-using dom::OwningDoubleOrConstrainDoubleRange;
-using dom::ConstrainDoubleRange;
-using dom::MediaTrackConstraintSet;
+using namespace mozilla::dom;
 
 extern PRLogModuleInfo* GetMediaManagerLog();
 #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 #define LOGFRAME(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Verbose, msg)
 
 // guts for appending data to the MSG track
 bool MediaEngineCameraVideoSource::AppendToTrack(SourceMediaStream* aSource,
                                                  layers::Image* aImage,
@@ -57,69 +53,125 @@ MediaEngineCameraVideoSource::GetCapabil
 
 // The full algorithm for all cameras. Sources that don't list capabilities
 // need to fake it and hardcode some by populating mHardcodedCapabilities above.
 
 // Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
 
 template<class ValueType, class ConstrainRange>
 /* static */ uint32_t
-MediaEngineCameraVideoSource::FitnessDistance(ValueType n,
+MediaEngineCameraVideoSource::FitnessDistance(ValueType aN,
                                               const ConstrainRange& aRange)
 {
-  if ((aRange.mExact.WasPassed() && aRange.mExact.Value() != n) ||
-      (aRange.mMin.WasPassed() && aRange.mMin.Value() > n) ||
-      (aRange.mMax.WasPassed() && aRange.mMax.Value() < n)) {
+  if ((aRange.mExact.WasPassed() && aRange.mExact.Value() != aN) ||
+      (aRange.mMin.WasPassed() && aRange.mMin.Value() > aN) ||
+      (aRange.mMax.WasPassed() && aRange.mMax.Value() < aN)) {
     return UINT32_MAX;
   }
-  if (!aRange.mIdeal.WasPassed() || n == aRange.mIdeal.Value()) {
+  if (!aRange.mIdeal.WasPassed() || aN == aRange.mIdeal.Value()) {
     return 0;
   }
-  return uint32_t(ValueType((std::abs(n - aRange.mIdeal.Value()) * 1000) /
-                            std::max(std::abs(n), std::abs(aRange.mIdeal.Value()))));
+  return uint32_t(ValueType((std::abs(aN - aRange.mIdeal.Value()) * 1000) /
+                            std::max(std::abs(aN), std::abs(aRange.mIdeal.Value()))));
 }
 
 // Binding code doesn't templatize well...
 
 /*static*/ uint32_t
-MediaEngineCameraVideoSource::FitnessDistance(int32_t n,
+MediaEngineCameraVideoSource::FitnessDistance(int32_t aN,
     const OwningLongOrConstrainLongRange& aConstraint, bool aAdvanced)
 {
   if (aConstraint.IsLong()) {
     ConstrainLongRange range;
     (aAdvanced ? range.mExact : range.mIdeal).Construct(aConstraint.GetAsLong());
-    return FitnessDistance(n, range);
+    return FitnessDistance(aN, range);
   } else {
-    return FitnessDistance(n, aConstraint.GetAsConstrainLongRange());
+    return FitnessDistance(aN, aConstraint.GetAsConstrainLongRange());
   }
 }
 
 /*static*/ uint32_t
-MediaEngineCameraVideoSource::FitnessDistance(double n,
+MediaEngineCameraVideoSource::FitnessDistance(double aN,
     const OwningDoubleOrConstrainDoubleRange& aConstraint,
     bool aAdvanced)
 {
   if (aConstraint.IsDouble()) {
     ConstrainDoubleRange range;
     (aAdvanced ? range.mExact : range.mIdeal).Construct(aConstraint.GetAsDouble());
-    return FitnessDistance(n, range);
+    return FitnessDistance(aN, range);
   } else {
-    return FitnessDistance(n, aConstraint.GetAsConstrainDoubleRange());
+    return FitnessDistance(aN, aConstraint.GetAsConstrainDoubleRange());
   }
 }
 
-/*static*/ uint32_t
+// Fitness distance returned as integer math * 1000. Infinity = UINT32_MAX
+
+/* static */ uint32_t
+MediaEngineCameraVideoSource::FitnessDistance(nsString aN,
+                             const ConstrainDOMStringParameters& aParams)
+{
+  struct Func
+  {
+    static bool
+    Contains(const OwningStringOrStringSequence& aStrings, nsString aN)
+    {
+      return aStrings.IsString() ? aStrings.GetAsString() == aN
+                                 : aStrings.GetAsStringSequence().Contains(aN);
+    }
+  };
+
+  if (aParams.mExact.WasPassed() && !Func::Contains(aParams.mExact.Value(), aN)) {
+    return UINT32_MAX;
+  }
+  if (aParams.mIdeal.WasPassed() && !Func::Contains(aParams.mIdeal.Value(), aN)) {
+    return 1000;
+  }
+  return 0;
+}
+
+/* static */ uint32_t
+MediaEngineCameraVideoSource::FitnessDistance(nsString aN,
+    const OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint,
+    bool aAdvanced)
+{
+  if (aConstraint.IsString()) {
+    ConstrainDOMStringParameters params;
+    if (aAdvanced) {
+      params.mExact.Construct();
+      params.mExact.Value().SetAsString() = aConstraint.GetAsString();
+    } else {
+      params.mIdeal.Construct();
+      params.mIdeal.Value().SetAsString() = aConstraint.GetAsString();
+    }
+    return FitnessDistance(aN, params);
+  } else if (aConstraint.IsStringSequence()) {
+    ConstrainDOMStringParameters params;
+    if (aAdvanced) {
+      params.mExact.Construct();
+      params.mExact.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
+    } else {
+      params.mIdeal.Construct();
+      params.mIdeal.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
+    }
+    return FitnessDistance(aN, params);
+  } else {
+    return FitnessDistance(aN, aConstraint.GetAsConstrainDOMStringParameters());
+  }
+}
+
+uint32_t
 MediaEngineCameraVideoSource::GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
                                                  const MediaTrackConstraintSet &aConstraints,
                                                  bool aAdvanced)
 {
   // Treat width|height|frameRate == 0 on capability as "can do any".
   // This allows for orthogonal capabilities that are not in discrete steps.
 
   uint64_t distance =
+    uint64_t(FitnessDistance(mFacingMode, aConstraints.mFacingMode, aAdvanced)) +
     uint64_t(aCandidate.width? FitnessDistance(int32_t(aCandidate.width),
                                                aConstraints.mWidth,
                                                aAdvanced) : 0) +
     uint64_t(aCandidate.height? FitnessDistance(int32_t(aCandidate.height),
                                                 aConstraints.mHeight,
                                                 aAdvanced) : 0) +
     uint64_t(aCandidate.maxFPS? FitnessDistance(double(aCandidate.maxFPS),
                                                 aConstraints.mFrameRate,
@@ -210,17 +262,17 @@ MediaEngineCameraVideoSource::LogConstra
         "             frameRate: { min: %f, max: %f, ideal: %f }" :
         "             frameRate: { min: %f, max: %f }"),
        c.mFrameRate.mMin, c.mFrameRate.mMax,
        c.mFrameRate.mIdeal.WasPassed()? c.mFrameRate.mIdeal.Value() : 0));
 }
 
 bool
 MediaEngineCameraVideoSource::ChooseCapability(
-    const dom::MediaTrackConstraints &aConstraints,
+    const MediaTrackConstraints &aConstraints,
     const MediaEnginePrefs &aPrefs)
 {
   if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
     LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps",
          aPrefs.GetWidth(), aPrefs.GetHeight(),
          aPrefs.mFPS, aPrefs.mMinFPS));
     LogConstraints(aConstraints, false);
     if (aConstraints.mAdvanced.WasPassed()) {
@@ -320,27 +372,86 @@ MediaEngineCameraVideoSource::ChooseCapa
 
   LOG(("chose cap %dx%d @%dfps codec %d raw %d",
        mCapability.width, mCapability.height, mCapability.maxFPS,
        mCapability.codecType, mCapability.rawType));
   return true;
 }
 
 void
+MediaEngineCameraVideoSource::SetName(nsString aName)
+{
+  mDeviceName = aName;
+  bool hasFacingMode = false;
+  VideoFacingModeEnum facingMode = VideoFacingModeEnum::User;
+
+  // Set facing mode based on device name.
+#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
+  if (aName.EqualsLiteral("back")) {
+    hasFacingMode = true;
+    facingMode = VideoFacingModeEnum::Environment;
+  } else if (aName.EqualsLiteral("front")) {
+    hasFacingMode = true;
+    facingMode = VideoFacingModeEnum::User;
+  }
+#endif // MOZ_B2G_CAMERA
+#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
+  // Names are generated. Example: "Camera 0, Facing back, Orientation 90"
+  //
+  // See media/webrtc/trunk/webrtc/modules/video_capture/android/java/src/org/
+  // webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java
+
+  if (aName.Find(NS_LITERAL_STRING("Facing back")) != kNotFound) {
+    hasFacingMode = true;
+    facingMode = VideoFacingModeEnum::Environment;
+  } else if (aName.Find(NS_LITERAL_STRING("Facing front")) != kNotFound) {
+    hasFacingMode = true;
+    facingMode = VideoFacingModeEnum::User;
+  }
+#endif // ANDROID
+#ifdef XP_MACOSX
+  // Kludge to test user-facing cameras on OSX.
+  if (aName.Find(NS_LITERAL_STRING("Face")) != -1) {
+    hasFacingMode = true;
+    facingMode = VideoFacingModeEnum::User;
+  }
+#endif
+  if (hasFacingMode) {
+    mFacingMode.Assign(NS_ConvertUTF8toUTF16(
+        VideoFacingModeEnumValues::strings[uint32_t(facingMode)].value));
+  } else {
+    mFacingMode.Truncate();
+  }
+}
+
+void
 MediaEngineCameraVideoSource::GetName(nsAString& aName)
 {
   aName = mDeviceName;
 }
 
 void
-MediaEngineCameraVideoSource::GetUUID(nsAString& aUUID)
+MediaEngineCameraVideoSource::SetUUID(const char* aUUID)
+{
+  mUniqueId.Assign(aUUID);
+}
+
+void
+MediaEngineCameraVideoSource::GetUUID(nsACString& aUUID)
 {
   aUUID = mUniqueId;
 }
 
+const nsCString&
+MediaEngineCameraVideoSource::GetUUID()
+{
+  return mUniqueId;
+}
+
+
 void
 MediaEngineCameraVideoSource::SetDirectListeners(bool aHasDirectListeners)
 {
   LOG((__FUNCTION__));
   mHasDirectListeners = aHasDirectListeners;
 }
 
 } // namespace mozilla
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -29,17 +29,17 @@ public:
     , mHasDirectListeners(false)
     , mCaptureIndex(aIndex)
     , mTrackID(0)
     , mFps(-1)
   {}
 
 
   virtual void GetName(nsAString& aName) override;
-  virtual void GetUUID(nsAString& aUUID) override;
+  virtual void GetUUID(nsACString& aUUID) override;
   virtual void SetDirectListeners(bool aHasListeners) override;
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) override
   {
     return NS_OK;
   };
@@ -76,32 +76,40 @@ protected:
   ~MediaEngineCameraVideoSource() {}
 
   // guts for appending data to the MSG track
   virtual bool AppendToTrack(SourceMediaStream* aSource,
                              layers::Image* aImage,
                              TrackID aID,
                              StreamTime delta);
   template<class ValueType, class ConstrainRange>
-  static uint32_t FitnessDistance(ValueType n, const ConstrainRange& aRange);
-  static uint32_t FitnessDistance(int32_t n,
+  static uint32_t FitnessDistance(ValueType aN, const ConstrainRange& aRange);
+  static uint32_t FitnessDistance(int32_t aN,
       const dom::OwningLongOrConstrainLongRange& aConstraint, bool aAdvanced);
-  static uint32_t FitnessDistance(double n,
+  static uint32_t FitnessDistance(double aN,
       const dom::OwningDoubleOrConstrainDoubleRange& aConstraint, bool aAdvanced);
+  static uint32_t FitnessDistance(nsString aN,
+    const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint,
+    bool aAdvanced);
+  static uint32_t FitnessDistance(nsString aN,
+      const dom::ConstrainDOMStringParameters& aParams);
 
-  static uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
-                                     const dom::MediaTrackConstraintSet &aConstraints,
-                                     bool aAdvanced);
+  uint32_t GetFitnessDistance(const webrtc::CaptureCapability& aCandidate,
+                              const dom::MediaTrackConstraintSet &aConstraints,
+                              bool aAdvanced);
   static void TrimLessFitCandidates(CapabilitySet& set);
   static void LogConstraints(const dom::MediaTrackConstraintSet& aConstraints,
                              bool aAdvanced);
   virtual size_t NumCapabilities();
   virtual void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut);
   bool ChooseCapability(const dom::MediaTrackConstraints &aConstraints,
                         const MediaEnginePrefs &aPrefs);
+  void SetName(nsString aName);
+  void SetUUID(const char* aUUID);
+  const nsCString& GetUUID(); // protected access
 
   // Engine variables.
 
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
   // image changes).
   // mMonitor also protects mSources[] access/changes.
   // mSources[] is accessed from webrtc threads.
@@ -119,15 +127,17 @@ protected:
   bool mHasDirectListeners;
   int mCaptureIndex;
   TrackID mTrackID;
   int mFps; // Track rate (30 fps by default)
 
   webrtc::CaptureCapability mCapability; // Doesn't work on OS X.
 
   nsTArray<webrtc::CaptureCapability> mHardcodedCapabilities; // For OSX & B2G
+private:
   nsString mDeviceName;
-  nsString mUniqueId;
+  nsCString mUniqueId;
+  nsString mFacingMode;
 };
 
 
 } // namespace mozilla
 #endif // MediaEngineCameraVideoSource_h
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -57,19 +57,19 @@ MediaEngineDefaultVideoSource::~MediaEng
 void
 MediaEngineDefaultVideoSource::GetName(nsAString& aName)
 {
   aName.AssignLiteral(MOZ_UTF16("Default Video Device"));
   return;
 }
 
 void
-MediaEngineDefaultVideoSource::GetUUID(nsAString& aUUID)
+MediaEngineDefaultVideoSource::GetUUID(nsACString& aUUID)
 {
-  aUUID.AssignLiteral(MOZ_UTF16("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676"));
+  aUUID.AssignLiteral("1041FCBD-3F12-4F7B-9E9B-1EC556DD5676");
   return;
 }
 
 nsresult
 MediaEngineDefaultVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                         const MediaEnginePrefs &aPrefs)
 {
   if (mState != kReleased) {
@@ -337,19 +337,19 @@ MediaEngineDefaultAudioSource::~MediaEng
 void
 MediaEngineDefaultAudioSource::GetName(nsAString& aName)
 {
   aName.AssignLiteral(MOZ_UTF16("Default Audio Device"));
   return;
 }
 
 void
-MediaEngineDefaultAudioSource::GetUUID(nsAString& aUUID)
+MediaEngineDefaultAudioSource::GetUUID(nsACString& aUUID)
 {
-  aUUID.AssignLiteral(MOZ_UTF16("B7CBD7C1-53EF-42F9-8353-73F61C70C092"));
+  aUUID.AssignLiteral("B7CBD7C1-53EF-42F9-8353-73F61C70C092");
   return;
 }
 
 nsresult
 MediaEngineDefaultAudioSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                         const MediaEnginePrefs &aPrefs)
 {
   if (mState != kReleased) {
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -34,17 +34,17 @@ class MediaEngineDefaultVideoSource : pu
                                       public MediaEngineVideoSource
 {
 public:
   MediaEngineDefaultVideoSource();
 
   virtual void Shutdown() override {};
 
   virtual void GetName(nsAString&) override;
-  virtual void GetUUID(nsAString&) override;
+  virtual void GetUUID(nsACString&) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream*, TrackID) override;
   virtual nsresult Stop(SourceMediaStream*, TrackID) override;
   virtual void SetDirectListeners(bool aHasDirectListeners) override {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
@@ -104,17 +104,17 @@ class MediaEngineDefaultAudioSource : pu
                                       public MediaEngineAudioSource
 {
 public:
   MediaEngineDefaultAudioSource();
 
   virtual void Shutdown() override {};
 
   virtual void GetName(nsAString&) override;
-  virtual void GetUUID(nsAString&) override;
+  virtual void GetUUID(nsACString&) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream*, TrackID) override;
   virtual nsresult Stop(SourceMediaStream*, TrackID) override;
   virtual void SetDirectListeners(bool aHasDirectListeners) override {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
--- a/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineGonkVideoSource.cpp
@@ -332,18 +332,18 @@ MediaEngineGonkVideoSource::Stop(SourceM
 * constructor and destructor respectively.
 */
 
 void
 MediaEngineGonkVideoSource::Init()
 {
   nsAutoCString deviceName;
   ICameraControl::GetCameraName(mCaptureIndex, deviceName);
-  CopyUTF8toUTF16(deviceName, mDeviceName);
-  CopyUTF8toUTF16(deviceName, mUniqueId);
+  SetName(NS_ConvertUTF8toUTF16(deviceName));
+  SetUUID(deviceName.get());
 
   mInitDone = true;
 }
 
 void
 MediaEngineGonkVideoSource::Shutdown()
 {
   LOG((__FUNCTION__));
--- a/dom/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -104,19 +104,19 @@ MediaEngineTabVideoSource::InitRunnable:
 
 void
 MediaEngineTabVideoSource::GetName(nsAString_internal& aName)
 {
   aName.AssignLiteral(MOZ_UTF16("&getUserMedia.videoSource.tabShare;"));
 }
 
 void
-MediaEngineTabVideoSource::GetUUID(nsAString_internal& aUuid)
+MediaEngineTabVideoSource::GetUUID(nsACString_internal& aUuid)
 {
-  aUuid.AssignLiteral(MOZ_UTF16("uuid"));
+  aUuid.AssignLiteral("tab");
 }
 
 #define DEFAULT_TABSHARE_VIDEO_MAX_WIDTH 4096
 #define DEFAULT_TABSHARE_VIDEO_MAX_HEIGHT 4096
 #define DEFAULT_TABSHARE_VIDEO_FRAMERATE 30
 
 nsresult
 MediaEngineTabVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
--- a/dom/media/webrtc/MediaEngineTabVideoSource.h
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.h
@@ -15,17 +15,17 @@ class MediaEngineTabVideoSource : public
   public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDOMEVENTLISTENER
     NS_DECL_NSITIMERCALLBACK
     MediaEngineTabVideoSource();
 
     virtual void Shutdown() override {};
     virtual void GetName(nsAString_internal&) override;
-    virtual void GetUUID(nsAString_internal&) override;
+    virtual void GetUUID(nsACString_internal&) override;
     virtual nsresult Allocate(const dom::MediaTrackConstraints &,
                               const mozilla::MediaEnginePrefs&) override;
     virtual nsresult Deallocate() override;
     virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) override;
     virtual void SetDirectListeners(bool aHasDirectListeners) override {};
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override;
     virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID) override;
     virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t) override;
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -148,22 +148,22 @@ public:
     , mEchoOn(false), mAgcOn(false), mNoiseOn(false)
     , mEchoCancel(webrtc::kEcDefault)
     , mAGC(webrtc::kAgcDefault)
     , mNoiseSuppress(webrtc::kNsDefault)
     , mPlayoutDelay(0)
     , mNullTransport(nullptr) {
     MOZ_ASSERT(aVoiceEnginePtr);
     mDeviceName.Assign(NS_ConvertUTF8toUTF16(name));
-    mDeviceUUID.Assign(NS_ConvertUTF8toUTF16(uuid));
+    mDeviceUUID.Assign(uuid);
     Init();
   }
 
   virtual void GetName(nsAString& aName) override;
-  virtual void GetUUID(nsAString& aUUID) override;
+  virtual void GetUUID(nsACString& aUUID) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints& aConstraints,
                             const MediaEnginePrefs& aPrefs) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream* aStream, TrackID aID) override;
   virtual nsresult Stop(SourceMediaStream* aSource, TrackID aID) override;
   virtual void SetDirectListeners(bool aHasDirectListeners) override {};
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
@@ -218,17 +218,17 @@ private:
   nsCOMPtr<nsIThread> mThread;
   int mCapIndex;
   int mChannel;
   TrackID mTrackID;
   bool mInitDone;
   bool mStarted;
 
   nsString mDeviceName;
-  nsString mDeviceUUID;
+  nsCString mDeviceUUID;
 
   bool mEchoOn, mAgcOn, mNoiseOn;
   webrtc::EcModes  mEchoCancel;
   webrtc::AgcModes mAGC;
   webrtc::NsModes  mNoiseSuppress;
   int32_t mPlayoutDelay;
 
   NullTransport *mNullTransport;
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -182,17 +182,17 @@ MediaEngineWebRTCAudioSource::GetName(ns
   if (mInitDone) {
     aName.Assign(mDeviceName);
   }
 
   return;
 }
 
 void
-MediaEngineWebRTCAudioSource::GetUUID(nsAString& aUUID)
+MediaEngineWebRTCAudioSource::GetUUID(nsACString& aUUID)
 {
   if (mInitDone) {
     aUUID.Assign(mDeviceUUID);
   }
 
   return;
 }
 
--- a/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -146,19 +146,17 @@ MediaEngineWebRTCVideoSource::NotifyPull
     // nullptr images are allowed
     AppendToTrack(aSource, mImage, aID, delta);
   }
 }
 
 size_t
 MediaEngineWebRTCVideoSource::NumCapabilities()
 {
-  NS_ConvertUTF16toUTF8 uniqueId(mUniqueId); // TODO: optimize this?
-
-  int num = mViECapture->NumberOfCapabilities(uniqueId.get(), kMaxUniqueIdLength);
+  int num = mViECapture->NumberOfCapabilities(GetUUID().get(), kMaxUniqueIdLength);
   if (num > 0) {
     return num;
   }
 
   switch (mMediaSource) {
     case dom::MediaSourceEnum::Camera:
 #ifdef XP_MACOSX
       // Mac doesn't support capabilities.
@@ -200,33 +198,32 @@ MediaEngineWebRTCVideoSource::NumCapabil
 
 void
 MediaEngineWebRTCVideoSource::GetCapability(size_t aIndex,
                                             webrtc::CaptureCapability& aOut)
 {
   if (!mHardcodedCapabilities.IsEmpty()) {
     MediaEngineCameraVideoSource::GetCapability(aIndex, aOut);
   }
-  NS_ConvertUTF16toUTF8 uniqueId(mUniqueId); // TODO: optimize this?
-  mViECapture->GetCaptureCapability(uniqueId.get(), kMaxUniqueIdLength, aIndex, aOut);
+  mViECapture->GetCaptureCapability(GetUUID().get(), kMaxUniqueIdLength, aIndex, aOut);
 }
 
 nsresult
 MediaEngineWebRTCVideoSource::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                        const MediaEnginePrefs &aPrefs)
 {
   LOG((__FUNCTION__));
   if (mState == kReleased && mInitDone) {
     // Note: if shared, we don't allow a later opener to affect the resolution.
     // (This may change depending on spec changes for Constraints/settings)
 
     if (!ChooseCapability(aConstraints, aPrefs)) {
       return NS_ERROR_UNEXPECTED;
     }
-    if (mViECapture->AllocateCaptureDevice(NS_ConvertUTF16toUTF8(mUniqueId).get(),
+    if (mViECapture->AllocateCaptureDevice(GetUUID().get(),
                                            kMaxUniqueIdLength, mCaptureIndex)) {
       return NS_ERROR_FAILURE;
     }
     mState = kAllocated;
     LOG(("Video device %d allocated", mCaptureIndex));
   } else if (MOZ_LOG_TEST(GetMediaManagerLog(), LogLevel::Debug)) {
     MonitorAutoLock lock(mMonitor);
     if (mSources.IsEmpty()) {
@@ -382,19 +379,18 @@ MediaEngineWebRTCVideoSource::Init()
 
   char deviceName[kMaxDeviceNameLength];
   char uniqueId[kMaxUniqueIdLength];
   if (mViECapture->GetCaptureDevice(mCaptureIndex,
                                     deviceName, kMaxDeviceNameLength,
                                     uniqueId, kMaxUniqueIdLength)) {
     return;
   }
-
-  CopyUTF8toUTF16(deviceName, mDeviceName);
-  CopyUTF8toUTF16(uniqueId, mUniqueId);
+  SetName(NS_ConvertUTF8toUTF16(deviceName));
+  SetUUID(uniqueId);
 
   mInitDone = true;
 }
 
 void
 MediaEngineWebRTCVideoSource::Shutdown()
 {
   LOG((__FUNCTION__));
@@ -438,17 +434,15 @@ void MediaEngineWebRTCVideoSource::Refre
   char uniqueId[kMaxUniqueIdLength];
 
   if (mViECapture->GetCaptureDevice(aIndex,
                                     deviceName, sizeof(deviceName),
                                     uniqueId, sizeof(uniqueId))) {
     return;
   }
 
-  CopyUTF8toUTF16(deviceName, mDeviceName);
+  SetName(NS_ConvertUTF8toUTF16(deviceName));
 #ifdef DEBUG
-  nsString temp;
-  CopyUTF8toUTF16(uniqueId, temp);
-  MOZ_ASSERT(temp.Equals(mUniqueId));
+  MOZ_ASSERT(GetUUID().Equals(uniqueId));
 #endif
 }
 
 } // namespace mozilla
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -3,17 +3,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BackgroundChildImpl.h"
 
 #include "ActorsChild.h" // IndexedDB
 #include "BroadcastChannelChild.h"
 #include "ServiceWorkerManagerChild.h"
 #include "FileDescriptorSetChild.h"
-#include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/layout/VsyncChild.h"
@@ -324,28 +323,16 @@ BackgroundChildImpl::AllocPCacheStreamCo
 
 bool
 BackgroundChildImpl::DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor)
 {
   dom::cache::DeallocPCacheStreamControlChild(aActor);
   return true;
 }
 
-media::PMediaChild*
-BackgroundChildImpl::AllocPMediaChild()
-{
-  return media::AllocPMediaChild();
-}
-
-bool
-BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
-{
-  return media::DeallocPMediaChild(aActor);
-}
-
 // -----------------------------------------------------------------------------
 // MessageChannel/MessagePort API
 // -----------------------------------------------------------------------------
 
 dom::PMessagePortChild*
 BackgroundChildImpl::AllocPMessagePortChild(const nsID& aUUID,
                                             const nsID& aDestinationUUID,
                                             const uint32_t& aSequenceID)
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -66,22 +66,16 @@ protected:
 
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const FileDescriptor& aFileDescriptor)
                                override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
 
-  virtual PMediaChild*
-  AllocPMediaChild() override;
-
-  virtual bool
-  DeallocPMediaChild(PMediaChild* aActor) override;
-
   virtual PVsyncChild*
   AllocPVsyncChild() override;
 
   virtual bool
   DeallocPVsyncChild(PVsyncChild* aActor) override;
 
   virtual PUDPSocketChild*
   AllocPUDPSocketChild(const OptionalPrincipalInfo& aPrincipalInfo,
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -1,17 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BackgroundParentImpl.h"
 
 #include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
-#include "mozilla/media/MediaParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
@@ -489,28 +488,16 @@ BackgroundParentImpl::DeallocPServiceWor
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<ServiceWorkerManagerParent*>(aActor);
   return true;
 }
 
-media::PMediaParent*
-BackgroundParentImpl::AllocPMediaParent()
-{
-  return media::AllocPMediaParent();
-}
-
-bool
-BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor)
-{
-  return media::DeallocPMediaParent(aActor);
-}
-
 bool
 BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar()
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   if (BackgroundParent::IsOtherProcessActor(this)) {
     return false;
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -87,22 +87,16 @@ protected:
   DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
 
   virtual PServiceWorkerManagerParent*
   AllocPServiceWorkerManagerParent() override;
 
   virtual bool
   DeallocPServiceWorkerManagerParent(PServiceWorkerManagerParent* aActor) override;
 
-  virtual PMediaParent*
-  AllocPMediaParent() override;
-
-  virtual bool
-  DeallocPMediaParent(PMediaParent* aActor) override;
-
   virtual bool
   RecvShutdownServiceWorkerRegistrar() override;
 
   virtual dom::cache::PCacheStorageParent*
   AllocPCacheStorageParent(const dom::cache::Namespace& aNamespace,
                            const PrincipalInfo& aPrincipalInfo) override;
 
   virtual bool
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -6,17 +6,16 @@ include protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PMessagePort;
-include protocol PMedia;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 
@@ -32,29 +31,27 @@ sync protocol PBackground
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PMessagePort;
-  manages PMedia;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
   PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   PVsync();
-  PMedia();
 
   PUDPSocket(OptionalPrincipalInfo pInfo, nsCString filter);
   PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel,
                     bool privateBrowsing);
 
   PServiceWorkerManager();
 
   ShutdownServiceWorkerRegistrar();