Bug 1173255 - incorporate feedback. r=jesup draft
authorJan-Ivar Bruaroey <jib@mozilla.com>
Thu, 18 Jun 2015 11:46:36 -0400
changeset 273256 822be9f79256208945e988bf3059c66ec4265017
parent 273255 8ba0eb8a22ee89e7bf59d6f726d0df17f1a9241e
child 507020 4c71390ba048a4706a93789bb69b3f14f23f3a29
push id2955
push userjbruaroey@mozilla.com
push dateThu, 18 Jun 2015 15:47:55 +0000
reviewersjesup
bugs1173255
milestone41.0a1
Bug 1173255 - incorporate feedback. r=jesup
dom/media/MediaManager.cpp
dom/media/MediaManager.h
dom/media/PeerConnection.js
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/tests/mochitest/test_getUserMedia_constraints.html
dom/media/webrtc/MediaEngineCameraVideoSource.cpp
dom/media/webrtc/MediaEngineGonkVideoSource.cpp
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1205,16 +1205,23 @@ public:
     return NS_OK;
   }
 
 private:
   nsAutoPtr<GetUserMediaTask> mTask;
 };
 #endif
 
+// TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
+// error: 'this' was not captured for this lambda function
+
+static auto& MediaManager_GetInstance = MediaManager::GetInstance;
+static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
+static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
+
 /**
  * EnumerateRawDevices - Enumerate a list of audio & video devices that
  * satisfy passed-in constraints. List contains raw id's.
  */
 
 already_AddRefed<MediaManager::PledgeSourceSet>
 MediaManager::EnumerateRawDevices(uint64_t aWindowId,
                                   const MediaStreamConstraints& aConstraints)
@@ -1231,17 +1238,17 @@ MediaManager::EnumerateRawDevices(uint64
 
   MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, aConstraints, aWindowId,
                                                  audioLoopDev,  videoLoopDev,
                                                  fake]() mutable {
     nsRefPtr<MediaEngine> backend;
     if (aConstraints.mFake || fake) {
       backend = new MediaEngineDefault(aConstraints.mFakeTracks);
     } else {
-      nsRefPtr<MediaManager> manager = MediaManager::GetInstance();
+      nsRefPtr<MediaManager> manager = MediaManager_GetInstance();
       backend = manager->GetBackend(aWindowId);
     }
 
     ScopedDeletePtr<SourceSet> result(new SourceSet);
     if (IsOn(aConstraints.mVideo)) {
       nsTArray<nsRefPtr<VideoDevice>> sources;
       GetSources(backend, GetInvariant(aConstraints.mVideo),
                  &MediaEngine::EnumerateVideoDevices, sources, videoLoopDev);
@@ -1255,17 +1262,17 @@ MediaManager::EnumerateRawDevices(uint64
                  &MediaEngine::EnumerateAudioDevices, sources, audioLoopDev);
       for (auto& source : sources) {
         result->AppendElement(source);
       }
     }
     SourceSet* handoff = result.forget();
     NS_DispatchToMainThread(NewRunnableFrom([id, handoff]() mutable {
       ScopedDeletePtr<SourceSet> result(handoff);
-      auto* mgr = MediaManager::GetIfExists();
+      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;
@@ -1363,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;
@@ -1776,17 +1792,16 @@ MediaManager::ToJSArray(SourceSet& aDevi
   return var.forget();
 }
 
 already_AddRefed<MediaManager::PledgeSourceSet>
 MediaManager::EnumerateDevicesImpl(uint64_t aWindowId,
                                    const MediaStreamConstraints& aConstraints)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  nsRefPtr<MediaManager> that = this; // hold on to this
 
   // 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
@@ -1804,34 +1819,34 @@ MediaManager::EnumerateDevicesImpl(uint6
     privateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
   }
   // 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([this, that, id, aWindowId,
-           aConstraints](const nsCString& aOriginKey) mutable {
+  p->Then([id, aWindowId, aConstraints](const nsCString& aOriginKey) mutable {
     MOZ_ASSERT(NS_IsMainThread());
+    nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
 
-    nsRefPtr<PledgeSourceSet> p = EnumerateRawDevices(aWindowId, aConstraints);
+    nsRefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId, aConstraints);
     p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
       ScopedDeletePtr<SourceSet> devices(aDevices); // secondary result
 
       // Only run if window is still on our active list.
-      auto* mgr = MediaManager::GetIfExists();
+      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;
       }
-      AnonymizeDevices(*devices, aOriginKey);
+      MediaManager_AnonymizeDevices(*devices, aOriginKey);
       p->Resolve(devices.forget());
       return NS_OK;
     });
   });
   return pledge.forget();
 }
 
 nsresult
@@ -1849,17 +1864,17 @@ MediaManager::EnumerateDevices(nsPIDOMWi
 
   MediaStreamConstraints c;
   c.mVideo.SetAsBoolean() = true;
   c.mAudio.SetAsBoolean() = true;
 
   nsRefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowId, c);
   p->Then([onSuccess](SourceSet*& aDevices) mutable {
     ScopedDeletePtr<SourceSet> devices(aDevices); // grab result
-    nsCOMPtr<nsIWritableVariant> array = ToJSArray(*devices);
+    nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
     onSuccess->OnSuccess(array);
   }, [onFailure](MediaStreamError& reason) mutable {
     onFailure->OnError(&reason);
   });
   return NS_OK;
 }
 
 /*
@@ -1878,22 +1893,22 @@ MediaManager::GetUserMediaDevices(nsPIDO
   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 = ToJSArray(*devices);
+      nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
       onSuccess->OnSuccess(array);
     } else {
-      nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(aWindowId);
+      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;
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -23,16 +23,17 @@
 #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.
@@ -528,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) {
@@ -568,20 +570,21 @@ public:
   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 nsresult AnonymizeId(nsAString& aId, 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);
@@ -623,13 +626,14 @@ private:
   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/systemservices/MediaChild.cpp
+++ b/dom/media/systemservices/MediaChild.cpp
@@ -17,49 +17,51 @@ PRLogModuleInfo *gMediaChildLog;
 #define LOG(args) MOZ_LOG(gMediaChildLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 namespace media {
 
 already_AddRefed<Pledge<nsCString>>
 GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing)
 {
-  auto* mgr = MediaManager::GetIfExists();
+  nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
   MOZ_ASSERT(mgr);
 
   nsRefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
   uint32_t id = mgr->mGetOriginKeyPledges.Append(*p);
 
-  if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    Child::Get()->SendGetOriginKey(id, aOrigin, aPrivateBrowsing);
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    mgr->GetNonE10sParent()->RecvGetOriginKey(id, aOrigin, aPrivateBrowsing);
   } else {
-    nsRefPtr<Parent> parent = new Parent(true);
-    parent->RecvGetOriginKey(id, aOrigin, aPrivateBrowsing);
+    Child::Get()->SendGetOriginKey(id, aOrigin, aPrivateBrowsing);
   }
   return p.forget();
 }
 
 void
 SanitizeOriginKeys(const uint64_t& aSinceWhen)
 {
   LOG(("SanitizeOriginKeys since %llu", aSinceWhen));
 
-  if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    Child::Get()->SendSanitizeOriginKeys(aSinceWhen);
+  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 {
-    nsRefPtr<Parent> parent = new Parent(true);
-    parent->RecvSanitizeOriginKeys(aSinceWhen);
+    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)
@@ -81,36 +83,34 @@ Child::~Child()
 void Child::ActorDestroy(ActorDestroyReason aWhy)
 {
   mActorDestroyed = true;
 }
 
 bool
 Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey)
 {
-  auto* mgr = MediaManager::GetIfExists();
+  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
@@ -19,37 +19,34 @@ namespace media {
 // 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;
-
 already_AddRefed<Pledge<nsCString>>
 GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing);
 
 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);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
+  virtual ~Child();
 private:
-  virtual ~Child();
 
   bool mActorDestroyed;
 };
 
 PMediaChild* AllocPMediaChild();
 bool DeallocPMediaChild(PMediaChild *aActor);
 
 } // namespace media
--- 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,89 +319,110 @@ 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)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  nsRefPtr<ParentSingleton> singleton(mSingleton);
 
   // First, get profile 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 false;
   }
 
   // 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.
+
   nsRefPtr<Pledge<nsCString>> p = new Pledge<nsCString>();
-  uint32_t id = singleton->mOutstandingPledges.Append(*p);
+  uint32_t id = mOutstandingPledges.Append(*p);
 
   nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   MOZ_ASSERT(sts);
-  rv = sts->Dispatch(NewRunnableFrom([id, profileDir, singleton,
+  nsRefPtr<OriginKeyStore> store(mOriginKeyStore);
+  bool sameProcess = mSameProcess;
+
+  rv = sts->Dispatch(NewRunnableFrom([id, profileDir, store, sameProcess,
                                       aOrigin, aPrivateBrowsing]() -> nsresult {
     MOZ_ASSERT(!NS_IsMainThread());
-    singleton->mOriginKeys.SetProfileDir(profileDir);
+    store->mOriginKeys.SetProfileDir(profileDir);
     nsCString result;
     if (aPrivateBrowsing) {
-      singleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, result);
+      store->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, result);
     } else {
-      singleton->mOriginKeys.GetOriginKey(aOrigin, result);
+      store->mOriginKeys.GetOriginKey(aOrigin, result);
     }
 
     // Pass result back to main thread.
     nsresult rv;
-    rv = NS_DispatchToMainThread(NewRunnableFrom([id, singleton,
+    rv = NS_DispatchToMainThread(NewRunnableFrom([id, store, sameProcess,
                                                   result]() -> nsresult {
-      nsRefPtr<Pledge<nsCString>> p = singleton->mOutstandingPledges.Remove(id);
+      Parent* parent = GccGetSingleton<Super>(); // GetSingleton();
+      if (!parent) {
+        return NS_OK;
+      }
+      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))) {
@@ -409,103 +430,109 @@ Parent::RecvGetOriginKey(const uint32_t&
     }
     return NS_OK;
   }), NS_DISPATCH_NORMAL);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
-  nsRefPtr<media::Parent> that(this);
-  p->Then([this, that, aRequestId](const nsCString& aKey) mutable {
-    if (!mDestroyed) {
-      if (!mSameProcess) {
-        unused << SendGetOriginKeyResponse(aRequestId, aKey);
-      } else {
-        auto* mgr = MediaManager::GetIfExists();
-        if (!mgr) {
-          return NS_OK;
-        }
-        nsRefPtr<Pledge<nsCString>> pledge =
-            mgr->mGetOriginKeyPledges.Remove(aRequestId);
-        if (pledge) {
-          pledge->Resolve(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);
-
   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;
   }
   // 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 {
+  nsRefPtr<OriginKeyStore> store(mOriginKeyStore);
+
+  rv = sts->Dispatch(NewRunnableFrom([profileDir, store, aSinceWhen]() -> nsresult {
     MOZ_ASSERT(!NS_IsMainThread());
-    singleton->mOriginKeys.SetProfileDir(profileDir);
-    singleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
-    singleton->mOriginKeys.Clear(aSinceWhen);
+    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(bool aSameProcess)
-  : 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,36 +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(bool aSameProcess = false);
-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,62 +35,71 @@ 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().
  */
 
+class PledgeBase
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(PledgeBase);
+protected:
+  virtual ~PledgeBase() {};
+};
+
 template<typename ValueType, typename ErrorType = nsresult>
-class Pledge
+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(ValueType& result) = 0;
     virtual void Fail(ErrorType& error) = 0;
     virtual ~FunctorsBase() {};
   };
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(Pledge);
   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, [](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(ValueType& result)
       {
         mOnSuccess(result);
       }
       void Fail(ErrorType& error)
       {
         mOnFailure(error);
       };
 
       OnSuccessType mOnSuccess;
       OnFailureType mOnFailure;
     };
-    mFunctors = new F(aOnSuccess, aOnFailure);
+    mFunctors = new Functors(aOnSuccess, aOnFailure);
 
     if (mDone) {
       if (!mError) {
         mFunctors->Succeed(mValue);
       } else {
         mFunctors->Fail(*mError);
       }
     }
@@ -120,70 +129,70 @@ protected:
       mError = rv;
       if (mFunctors) {
         mFunctors->Fail(mError);
       }
     }
   }
 
   ValueType mValue;
-protected:
+private:
   ~Pledge() {};
   bool mDone;
   nsRefPtr<ErrorType> mError;
-private:
-  nsAutoPtr<FunctorsBase> mFunctors;
+  ScopedDeletePtr<FunctorsBase> mFunctors;
 };
 
 template<typename ValueType>
-class Pledge<ValueType, nsresult>
+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:
-  NS_INLINE_DECL_REFCOUNTING(Pledge);
   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 F : public FunctorsBase
+    class Functors : public FunctorsBase
     {
     public:
-      F(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
+      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 F(aOnSuccess, aOnFailure);
+    mFunctors = new Functors(aOnSuccess, aOnFailure);
 
     if (mDone) {
       if (mError == NS_OK) {
         mFunctors->Succeed(mValue);
       } else {
         mFunctors->Fail(mError);
       }
     }
@@ -213,22 +222,21 @@ protected:
       mError = error;
       if (mFunctors) {
         mFunctors->Fail(mError);
       }
     }
   }
 
   ValueType mValue;
-protected:
+private:
   ~Pledge() {};
   bool mDone;
   nsresult mError;
-private:
-  nsAutoPtr<FunctorsBase> mFunctors;
+  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()
--- 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/MediaEngineCameraVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.cpp
@@ -376,17 +376,17 @@ MediaEngineCameraVideoSource::ChooseCapa
   return true;
 }
 
 void
 MediaEngineCameraVideoSource::SetName(nsString aName)
 {
   mDeviceName = aName;
   bool hasFacingMode = false;
-  VideoFacingModeEnum facingMode;
+  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;
--- 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__));