Bug 1046245 - enumerateDevices session-persisted hmac id. r=jesup
authorJan-Ivar Bruaroey <jib@mozilla.com>
Mon, 23 Feb 2015 11:50:48 -0500
changeset 236427 7b6b68c413ffeaf67458c040fbd7e651b086b7e7
parent 236426 c47fa3802a576b6f42232b8a9a3b86e9658e5e61
child 236428 48335c755e70e19dc6808e0e5d8fd0cfcc8fc043
push id57667
push userrjesup@wgate.com
push dateSun, 29 Mar 2015 05:19:06 +0000
treeherdermozilla-inbound@13068deb2193 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1046245
milestone39.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 1046245 - enumerateDevices session-persisted hmac id. r=jesup
dom/media/MediaDevices.cpp
dom/media/MediaManager.cpp
dom/media/MediaManager.h
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -122,19 +122,17 @@ public:
         device->GetId(id);
         // Include name only if page currently has a gUM stream active or
         // persistent permissions (audio or video) have been granted
         if (MediaManager::Get()->IsWindowActivelyCapturing(mWindowId) ||
             HasAPersistentPermission(mWindowId) ||
             Preferences::GetBool("media.navigator.permission.disabled", false)) {
           device->GetName(name);
         }
-        // TODO: return anonymized origin-persistent id (1046245)
-        nsRefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(nsString(), kind,
-                                                             name);
+        nsRefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(id, kind, name);
         infos.AppendElement(info);
       }
     }
     mPromise->MaybeResolve(infos);
     return NS_OK;
   }
 
 private:
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -20,16 +20,17 @@
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIIDNService.h"
 #include "nsNetUtil.h"
+#include "nsPrincipal.h"
 #include "mozilla/Types.h"
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 #include "mozilla/Preferences.h"
@@ -37,16 +38,20 @@
 
 #include "Latency.h"
 
 // For PR_snprintf
 #include "prprf.h"
 
 #include "nsJSUtils.h"
 #include "nsGlobalWindow.h"
+#include "nsIUUIDGenerator.h"
+#include "nspr.h"
+#include "nss.h"
+#include "pk11pub.h"
 
 /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
 #include "MediaEngineDefault.h"
 #if defined(MOZ_WEBRTC)
 #include "MediaEngineWebRTC.h"
 #include "browser_logging/WebRtcLog.h"
 #endif
 
@@ -189,55 +194,71 @@ HostHasPermission(nsIURI &docURI)
     }
 
     begin = end + 1;
   } while (end < domainWhiteList.Length());
 
   return false;
 }
 
-ErrorCallbackRunnable::ErrorCallbackRunnable(
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
-  MediaMgrError& aError,
-  uint64_t aWindowID)
-  : mError(&aError)
-  , mWindowID(aWindowID)
-  , mManager(MediaManager::GetInstance())
+/**
+ * Send an error back to content.
+ * Do this only on the main thread. The onSuccess callback is also passed here
+ * so it can be released correctly.
+ */
+template<class SuccessCallbackType>
+class ErrorCallbackRunnable : public nsRunnable
 {
-  mOnSuccess.swap(aOnSuccess);
-  mOnFailure.swap(aOnFailure);
-}
+public:
+  ErrorCallbackRunnable(
+    nsCOMPtr<SuccessCallbackType>& aOnSuccess,
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
+    MediaMgrError& aError,
+    uint64_t aWindowID)
+    : mError(&aError)
+    , mWindowID(aWindowID)
+    , mManager(MediaManager::GetInstance())
+  {
+    mOnSuccess.swap(aOnSuccess);
+    mOnFailure.swap(aOnFailure);
+  }
 
-ErrorCallbackRunnable::~ErrorCallbackRunnable()
-{
-  MOZ_ASSERT(!mOnSuccess && !mOnFailure);
-}
+  NS_IMETHODIMP
+  Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+    nsCOMPtr<SuccessCallbackType> onSuccess = mOnSuccess.forget();
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
 
-NS_IMETHODIMP
-ErrorCallbackRunnable::Run()
-{
-  // Only run if the window is still active.
-  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess = mOnSuccess.forget();
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
-
-  if (!(mManager->IsWindowStillActive(mWindowID))) {
+    // Only run if the window is still active.
+    if (!(mManager->IsWindowStillActive(mWindowID))) {
+      return NS_OK;
+    }
+    // This is safe since we're on main-thread, and the windowlist can only
+    // be invalidated from the main-thread (see OnNavigation)
+    nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
+    if (window) {
+      nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
+      onFailure->OnError(error);
+    }
     return NS_OK;
   }
-  // This is safe since we're on main-thread, and the windowlist can only
-  // be invalidated from the main-thread (see OnNavigation)
-  nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
-  if (window) {
-    nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
-    onFailure->OnError(error);
+private:
+  ~ErrorCallbackRunnable()
+  {
+    MOZ_ASSERT(!mOnSuccess && !mOnFailure);
   }
-  return NS_OK;
-}
+
+  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 "onSuccess" callback in content. The callback will take a
  * DOMBlob in the case of {picture:true}, and a MediaStream in the case of
  * {audio:true} or {video:true}. There is a constructor available for each
  * form. Do this only on the main thread.
  */
 class SuccessCallbackRunnable : public nsRunnable
@@ -511,16 +532,22 @@ AudioDevice::GetType(nsAString& aType)
 
 NS_IMETHODIMP
 MediaDevice::GetId(nsAString& aID)
 {
   aID.Assign(mID);
   return NS_OK;
 }
 
+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);
@@ -721,16 +748,36 @@ public:
   bool mAgcOn;
   bool mNoiseOn;
   uint32_t mEcho;
   uint32_t mAgc;
   uint32_t mNoise;
   uint32_t mPlayoutDelay;
 };
 
+
+void
+MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog)
+{
+  MM_LOG(("%s , rv=%d", errorLog, rv));
+  NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
+      mOnTracksAvailableCallback.forget()));
+  nsString log;
+
+  log.AssignASCII(errorLog);
+  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
+  nsRefPtr<MediaMgrError> error = new MediaMgrError(
+    NS_LITERAL_STRING("InternalError"), log);
+  NS_DispatchToMainThread(
+    new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(onSuccess,
+                                                                 mOnFailure,
+                                                                 *error,
+                                                                 mWindowID));
+}
+
 /**
  * Creates a MediaStream, attaches a listener and fires off a success callback
  * to the DOM with the stream. We also pass in the error callback so it can
  * be released correctly.
  *
  * All of this must be done on the main thread!
  *
  * Note that the various GetUserMedia Runnable classes currently allow for
@@ -1086,18 +1133,21 @@ public:
 
   ~GetUserMediaTask() {
   }
 
   void
   Fail(const nsAString& aName,
        const nsAString& aMessage = EmptyString()) {
     nsRefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage);
-    nsRefPtr<ErrorCallbackRunnable> runnable =
-      new ErrorCallbackRunnable(mOnSuccess, mOnFailure, *error, mWindowID);
+    nsRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>> runnable =
+      new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(mOnSuccess,
+                                                                   mOnFailure,
+                                                                   *error,
+                                                                   mWindowID);
     // These should be empty now
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
 
     NS_DispatchToMainThread(runnable);
     // Do after ErrorCallbackRunnable Run()s, as it checks active window list
     NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
   }
@@ -1312,82 +1362,220 @@ private:
 /**
  * Similar to GetUserMediaTask, but used for the chrome-only
  * GetUserMediaDevices function. Enumerates a list of audio & video devices,
  * wraps them up in nsIMediaDevice objects and returns it to the success
  * callback.
  */
 class GetUserMediaDevicesTask : public Task
 {
+  static unsigned char* unconst_uchar_cast(const char *s) {
+    return reinterpret_cast<unsigned char*>(const_cast<char*>(s));
+  }
+
+  // Cribbed from nricectx.cpp
+  static nsresult
+  hmac_sha1(const char *key, int keyl, const char *buf, int bufl,
+            unsigned char *result) {
+    const CK_MECHANISM_TYPE mech = CKM_SHA_1_HMAC;
+    PK11SlotInfo *slot = 0;
+    MOZ_ASSERT(keyl > 0);
+    SECItem keyi = { siBuffer, unconst_uchar_cast(key),
+                     static_cast<unsigned int>(keyl) };
+    PK11SymKey *skey = 0;
+    PK11Context *hmac_ctx = 0;
+    SECStatus status;
+    unsigned int hmac_len;
+    SECItem param = { siBuffer, nullptr, 0 };
+    nsresult rv = NS_ERROR_UNEXPECTED;
+
+    slot = PK11_GetInternalKeySlot();
+    if (!slot) {
+      goto abort;
+    }
+    skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_SIGN, &keyi,
+                             nullptr);
+    if (!skey) {
+      goto abort;
+    }
+
+    hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN, skey, &param);
+    if (!hmac_ctx) {
+      goto abort;
+    }
+    status = PK11_DigestBegin(hmac_ctx);
+    if (status != SECSuccess) {
+      goto abort;
+    }
+    status = PK11_DigestOp(hmac_ctx, unconst_uchar_cast(buf), bufl);
+    if (status != SECSuccess) {
+      goto abort;
+    }
+    status = PK11_DigestFinal(hmac_ctx, result, &hmac_len, 20);
+    if (status != SECSuccess) {
+      goto abort;
+    }
+    MOZ_ASSERT(hmac_len == 20);
+    rv = NS_OK;
+
+  abort:
+    if (hmac_ctx) {
+      PK11_DestroyContext(hmac_ctx, PR_TRUE);
+    }
+    if (skey) {
+      PK11_FreeSymKey(skey);
+    }
+    if (slot) {
+      PK11_FreeSlot(slot);
+    }
+    return rv;
+  }
+
+  nsresult AnonymizeId(nsAString& aId, const nsACString& origin) {
+    // deviceId would be a supercookie if we returned it. Anonymize it:
+    // 1. Get (or create) a persistent uuid for this origin.
+    // 2. Return hmac_sha1(uuid, id) - an anonymized id unique to origin.
+
+    static bool loaded = false;
+    if (!loaded) {
+      // load OriginUuids from disk.
+    }
+    OriginUuid* originUuid;
+    if (!mManager->mOriginUuids.Get(origin, &originUuid)) {
+      char uuid[NSID_LENGTH];
+      {
+        nsresult rv;
+        nsID id;
+        {
+          nsCOMPtr<nsIUUIDGenerator> uuidgen =
+              do_GetService("@mozilla.org/uuid-generator;1", &rv);
+          NS_ENSURE_SUCCESS(rv, rv);
+          rv = uuidgen->GenerateUUIDInPlace(&id);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+        id.ToProvidedString(uuid);
+      }
+      originUuid = new OriginUuid(uuid, false);
+      mManager->mOriginUuids.Put(origin, originUuid);
+    }
+
+    unsigned char mac[20];
+    {
+      NS_ConvertUTF16toUTF8 id(aId);
+      hmac_sha1(originUuid->mUuid.get(), originUuid->mUuid.Length(),
+                id.get(), id.Length(), mac);
+    }
+    char hex[sizeof(mac) * 2 + 1];
+    auto& m = mac;
+    PR_snprintf(hex, sizeof(hex), // Use first 16 bytes of hmac as id
+                "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x"
+                "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
+                m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7],
+                m[8], m[9], m[10],m[11], m[12], m[13], m[14], m[15]);
+    aId = NS_ConvertUTF8toUTF16(hex);
+    return NS_OK;
+  }
+
 public:
   GetUserMediaDevicesTask(
     const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aOnSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
     uint64_t aWindowId, nsACString& aAudioLoopbackDev,
-    nsACString& aVideoLoopbackDev, bool aUseFakeDevices)
+    nsACString& aVideoLoopbackDev, bool aPrivileged, const nsACString& aOrigin,
+    bool aUseFakeDevices)
     : mConstraints(aConstraints)
     , mOnSuccess(aOnSuccess)
     , mOnFailure(aOnFailure)
     , mManager(MediaManager::GetInstance())
     , mWindowId(aWindowId)
     , mLoopbackAudioDevice(aAudioLoopbackDev)
     , mLoopbackVideoDevice(aVideoLoopbackDev)
+    , mPrivileged(aPrivileged)
+    , mOrigin(aOrigin)
     , mUseFakeDevices(aUseFakeDevices) {}
 
   void // NS_IMETHOD
   Run()
   {
     NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
 
     nsRefPtr<MediaEngine> backend;
     if (mConstraints.mFake || mUseFakeDevices)
       backend = new MediaEngineDefault(mConstraints.mFakeTracks);
     else
       backend = mManager->GetBackend(mWindowId);
 
     typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
 
-    ScopedDeletePtr<SourceSet> final(new SourceSet);
+    ScopedDeletePtr<SourceSet> result(new SourceSet);
     if (IsOn(mConstraints.mVideo)) {
-      nsTArray<nsRefPtr<VideoDevice>> s;
+      nsTArray<nsRefPtr<VideoDevice>> sources;
       GetSources(backend, GetInvariant(mConstraints.mVideo),
-                 &MediaEngine::EnumerateVideoDevices, s, mLoopbackVideoDevice.get());
-      for (uint32_t i = 0; i < s.Length(); i++) {
-        final->AppendElement(s[i]);
+                 &MediaEngine::EnumerateVideoDevices, sources,
+                 mLoopbackVideoDevice.get());
+      for (auto& source : sources) {
+        result->AppendElement(source);
       }
     }
     if (IsOn(mConstraints.mAudio)) {
-      nsTArray<nsRefPtr<AudioDevice>> s;
+      nsTArray<nsRefPtr<AudioDevice>> sources;
       GetSources(backend, GetInvariant(mConstraints.mAudio),
-                 &MediaEngine::EnumerateAudioDevices, s, mLoopbackAudioDevice.get());
-      for (uint32_t i = 0; i < s.Length(); i++) {
-        final->AppendElement(s[i]);
+                 &MediaEngine::EnumerateAudioDevices, sources,
+                 mLoopbackAudioDevice.get());
+      for (auto& source : sources) {
+        result->AppendElement(source);
       }
     }
 
-    NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
-                                                              mOnSuccess, mOnFailure,
-                                                              final.forget()));
+    nsresult rv = NS_OK;
+    if (!mPrivileged) {
+      for (auto& source : *result) {
+        nsString id;
+        source->GetId(id);
+        rv = AnonymizeId(id, mOrigin);
+        if (NS_FAILED(rv)) {
+          break;
+        }
+        source->SetId(id);
+      }
+    }
+    if (NS_SUCCEEDED(rv)) {
+      NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
+                                                                mOnSuccess,
+                                                                mOnFailure,
+                                                                result.forget()));
+    } else {
+      nsRefPtr<MediaMgrError> error = new
+          MediaMgrError(NS_LITERAL_STRING("InternalError"),
+                        NS_LITERAL_STRING("Unexpected error"));
+      NS_DispatchToMainThread(
+        new ErrorCallbackRunnable<nsIGetUserMediaDevicesSuccessCallback>(mOnSuccess,
+                                                                         mOnFailure,
+                                                                         *error,
+                                                                         mWindowId));
+    }
     // DeviceSuccessCallbackRunnable should 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 mUseFakeDevices;
 };
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mMutex("mozilla::MediaManager")
   , mBackend(nullptr) {
   mPrefs.mWidth  = 0; // adaptive default
@@ -1788,17 +1976,18 @@ MediaManager::GetUserMedia(
   return NS_OK;
 }
 
 nsresult
 MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
   const MediaStreamConstraints& aConstraints,
   nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
   nsIDOMGetUserMediaErrorCallback* aOnFailure,
-  uint64_t aInnerWindowID)
+  uint64_t aInnerWindowID,
+  bool aPrivileged)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   NS_ENSURE_TRUE(aOnFailure, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
@@ -1806,38 +1995,43 @@ MediaManager::GetUserMediaDevices(nsPIDO
   // 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);
 
+  nsCString origin;
+  nsPrincipal::GetOriginForURI(aWindow->GetDocumentURI(),
+                               getter_Copies(origin));
+
   MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
     new GetUserMediaDevicesTask(
       aConstraints, onSuccess.forget(), onFailure.forget(),
       (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
-      loopbackAudioDevice, loopbackVideoDevice, useFakeStreams));
+      loopbackAudioDevice, loopbackVideoDevice, aPrivileged, origin,
+      useFakeStreams));
 
   return NS_OK;
 }
 
 nsresult
 MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
                                nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
                                nsIDOMGetUserMediaErrorCallback* aOnFailure)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   MediaStreamConstraints c;
   c.mVideo.SetAsBoolean() = true;
   c.mAudio.SetAsBoolean() = true;
 
   AddWindowID(aWindow->WindowID());
-  return GetUserMediaDevices(aWindow, c, aOnSuccess, aOnFailure, 0);
+  return GetUserMediaDevices(aWindow, c, aOnSuccess, aOnFailure, 0, false);
 }
 
 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.
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -295,39 +295,16 @@ typedef enum {
   MEDIA_STOP,
   MEDIA_STOP_TRACK,
   MEDIA_DIRECT_LISTENERS
 } MediaOperation;
 
 class MediaManager;
 class GetUserMediaTask;
 
-/**
- * Send an error back to content.
- * Do this only on the main thread. The onSuccess callback is also passed here
- * so it can be released correctly.
- */
-class ErrorCallbackRunnable : public nsRunnable
-{
-public:
-  ErrorCallbackRunnable(
-    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
-    nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
-    MediaMgrError& aError, uint64_t aWindowID);
-  NS_IMETHOD Run();
-private:
-  ~ErrorCallbackRunnable();
-
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
-  nsRefPtr<MediaMgrError> mError;
-  uint64_t mWindowID;
-  nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
-};
-
 class ReleaseMediaOperationResource : public nsRunnable
 {
 public:
   ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback):
     mStream(aStream),
     mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
   NS_IMETHOD Run() override {return NS_OK;}
@@ -364,30 +341,17 @@ public:
   {}
 
   ~MediaOperationTask()
   {
     // MediaStreams can be released on any thread.
   }
 
   void
-  ReturnCallbackError(nsresult rv, const char* errorLog)
-  {
-    MM_LOG(("%s , rv=%d", errorLog, rv));
-    NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
-          mOnTracksAvailableCallback.forget()));
-    nsString log;
-
-    log.AssignASCII(errorLog);
-    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
-    nsRefPtr<MediaMgrError> error = new MediaMgrError(
-        NS_LITERAL_STRING("InternalError"), log);
-    NS_DispatchToMainThread(new ErrorCallbackRunnable(onSuccess, mOnFailure,
-        *error, mWindowID));
-  }
+  ReturnCallbackError(nsresult rv, const char* errorLog);
 
   void
   Run()
   {
     SourceMediaStream *source = mListener->GetSourceStream();
     // No locking between these is required as all the callbacks for the
     // same MediaStream will occur on the same thread.
     if (!source) // means the stream was never Activated()
@@ -489,25 +453,37 @@ private:
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
   bool mBool;
   uint64_t mWindowID;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
 };
 
+class OriginUuid
+{
+public:
+  OriginUuid(char *aUuid, bool aPrivateBrowsing)
+  : mPrivateBrowsing(aPrivateBrowsing) {
+    mUuid.Append(aUuid);
+  }
+  nsCString mUuid;
+  bool mPrivateBrowsing;
+};
+
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   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;
@@ -539,19 +515,23 @@ public:
 };
 
 // we could add MediaManager if needed
 typedef void (*WindowListenerCallback)(MediaManager *aThis,
                                        uint64_t aWindowID,
                                        StreamListeners *aListeners,
                                        void *aData);
 
+class GetUserMediaDevicesTask;
+
 class MediaManager final : public nsIMediaManagerService,
                            public nsIObserver
 {
+  friend GetUserMediaDevicesTask;
+
 public:
   static already_AddRefed<MediaManager> GetInstance();
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
   static MediaManager* Get();
   static MediaManager* GetIfExists();
@@ -590,17 +570,18 @@ public:
     const dom::MediaStreamConstraints& aConstraints,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
 
   nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
     const dom::MediaStreamConstraints& aConstraints,
     nsIGetUserMediaDevicesSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError,
-    uint64_t aInnerWindowID = 0);
+    uint64_t aInnerWindowID = 0,
+    bool aPrivileged = true);
 
   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);
@@ -627,20 +608,24 @@ private:
 
   void StopScreensharing(uint64_t aWindowID);
   void IterateWindowListeners(nsPIDOMWindow *aWindow,
                               WindowListenerCallback aCallback,
                               void *aData);
 
   void StopMediaStreams();
 
+  // ONLY access from MediaManagerThread so we don't need to lock
+  nsClassHashtable<nsCStringHashKey, OriginUuid> mOriginUuids;
+
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
   nsClassHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
+
   // Always exists
   nsAutoPtr<base::Thread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;