author | Jan-Ivar Bruaroey <jib@mozilla.com> |
Mon, 23 Feb 2015 11:50:48 -0500 | |
changeset 236427 | 7b6b68c413ffeaf67458c040fbd7e651b086b7e7 |
parent 236426 | c47fa3802a576b6f42232b8a9a3b86e9658e5e61 |
child 236428 | 48335c755e70e19dc6808e0e5d8fd0cfcc8fc043 |
push id | 57667 |
push user | rjesup@wgate.com |
push date | Sun, 29 Mar 2015 05:19:06 +0000 |
treeherder | mozilla-inbound@13068deb2193 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jesup |
bugs | 1046245 |
milestone | 39.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
|
--- 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, ¶m); + 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;