LCOV - code coverage report
Current view: top level - dom/media - MediaManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 3 1881 0.2 %
Date: 2017-07-14 16:53:18 Functions: 1 240 0.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
       2             : /* vim: set ts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "MediaManager.h"
       8             : 
       9             : #include "MediaStreamGraph.h"
      10             : #include "mozilla/dom/MediaStreamTrack.h"
      11             : #include "MediaStreamListener.h"
      12             : #include "nsArray.h"
      13             : #include "nsContentUtils.h"
      14             : #include "nsHashPropertyBag.h"
      15             : #ifdef MOZ_WIDGET_GONK
      16             : #include "nsIAudioManager.h"
      17             : #endif
      18             : #include "nsIEventTarget.h"
      19             : #include "nsIUUIDGenerator.h"
      20             : #include "nsIScriptGlobalObject.h"
      21             : #include "nsIPermissionManager.h"
      22             : #include "nsIPopupWindowManager.h"
      23             : #include "nsIDocShell.h"
      24             : #include "nsIDocument.h"
      25             : #include "nsISupportsPrimitives.h"
      26             : #include "nsIInterfaceRequestorUtils.h"
      27             : #include "nsIIDNService.h"
      28             : #include "nsNetCID.h"
      29             : #include "nsNetUtil.h"
      30             : #include "nsICryptoHash.h"
      31             : #include "nsICryptoHMAC.h"
      32             : #include "nsIKeyModule.h"
      33             : #include "nsAppDirectoryServiceDefs.h"
      34             : #include "nsIInputStream.h"
      35             : #include "nsILineInputStream.h"
      36             : #include "mozilla/Telemetry.h"
      37             : #include "mozilla/Types.h"
      38             : #include "mozilla/PeerIdentity.h"
      39             : #include "mozilla/dom/BindingDeclarations.h"
      40             : #include "mozilla/dom/ContentChild.h"
      41             : #include "mozilla/dom/File.h"
      42             : #include "mozilla/dom/MediaStreamBinding.h"
      43             : #include "mozilla/dom/MediaStreamTrackBinding.h"
      44             : #include "mozilla/dom/GetUserMediaRequestBinding.h"
      45             : #include "mozilla/dom/Promise.h"
      46             : #include "mozilla/dom/MediaDevices.h"
      47             : #include "mozilla/Base64.h"
      48             : #include "mozilla/ipc/BackgroundChild.h"
      49             : #include "mozilla/media/MediaChild.h"
      50             : #include "mozilla/media/MediaTaskUtils.h"
      51             : #include "MediaTrackConstraints.h"
      52             : #include "VideoUtils.h"
      53             : #include "Latency.h"
      54             : #include "nsProxyRelease.h"
      55             : #include "NullPrincipal.h"
      56             : #include "nsVariant.h"
      57             : 
      58             : // For snprintf
      59             : #include "mozilla/Sprintf.h"
      60             : 
      61             : #include "nsJSUtils.h"
      62             : #include "nsGlobalWindow.h"
      63             : #include "nsIUUIDGenerator.h"
      64             : #include "nspr.h"
      65             : #include "nss.h"
      66             : #include "pk11pub.h"
      67             : 
      68             : /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
      69             : #include "MediaEngineDefault.h"
      70             : #if defined(MOZ_WEBRTC)
      71             : #include "MediaEngineWebRTC.h"
      72             : #include "browser_logging/WebRtcLog.h"
      73             : #endif
      74             : 
      75             : #ifdef MOZ_B2G
      76             : #include "MediaPermissionGonk.h"
      77             : #endif
      78             : 
      79             : #if defined (XP_WIN)
      80             : #include "mozilla/WindowsVersion.h"
      81             : #include <winsock2.h>
      82             : #include <iphlpapi.h>
      83             : #include <tchar.h>
      84             : #endif
      85             : 
      86             : // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
      87             : // GetTickCount() and conflicts with MediaStream::GetCurrentTime.
      88             : #ifdef GetCurrentTime
      89             : #undef GetCurrentTime
      90             : #endif
      91             : 
      92             : // XXX Workaround for bug 986974 to maintain the existing broken semantics
      93             : template<>
      94             : struct nsIMediaDevice::COMTypeInfo<mozilla::VideoDevice, void> {
      95             :   static const nsIID kIID;
      96             : };
      97             : const nsIID nsIMediaDevice::COMTypeInfo<mozilla::VideoDevice, void>::kIID = NS_IMEDIADEVICE_IID;
      98             : template<>
      99             : struct nsIMediaDevice::COMTypeInfo<mozilla::AudioDevice, void> {
     100             :   static const nsIID kIID;
     101             : };
     102             : const nsIID nsIMediaDevice::COMTypeInfo<mozilla::AudioDevice, void>::kIID = NS_IMEDIADEVICE_IID;
     103             : 
     104             : namespace {
     105           0 : already_AddRefed<nsIAsyncShutdownClient> GetShutdownPhase() {
     106           0 :   nsCOMPtr<nsIAsyncShutdownService> svc = mozilla::services::GetAsyncShutdown();
     107           0 :   MOZ_RELEASE_ASSERT(svc);
     108             : 
     109           0 :   nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
     110           0 :   nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(shutdownPhase));
     111           0 :   if (!shutdownPhase) {
     112             :     // We are probably in a content process. We need to do cleanup at
     113             :     // XPCOM shutdown in leakchecking builds.
     114           0 :     rv = svc->GetXpcomWillShutdown(getter_AddRefs(shutdownPhase));
     115             :   }
     116           0 :   MOZ_RELEASE_ASSERT(shutdownPhase);
     117           0 :   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     118           0 :   return shutdownPhase.forget();
     119             : }
     120             : }
     121             : 
     122             : namespace mozilla {
     123             : 
     124             : #ifdef LOG
     125             : #undef LOG
     126             : #endif
     127             : 
     128             : LogModule*
     129           0 : GetMediaManagerLog()
     130             : {
     131             :   static LazyLogModule sLog("MediaManager");
     132           0 :   return sLog;
     133             : }
     134             : #define LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
     135             : 
     136             : using dom::BasicTrackSource;
     137             : using dom::ConstrainDOMStringParameters;
     138             : using dom::File;
     139             : using dom::GetUserMediaRequest;
     140             : using dom::MediaSourceEnum;
     141             : using dom::MediaStreamConstraints;
     142             : using dom::MediaStreamError;
     143             : using dom::MediaStreamTrack;
     144             : using dom::MediaStreamTrackSource;
     145             : using dom::MediaTrackConstraints;
     146             : using dom::MediaTrackConstraintSet;
     147             : using dom::OwningBooleanOrMediaTrackConstraints;
     148             : using dom::OwningStringOrStringSequence;
     149             : using dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters;
     150             : using dom::Promise;
     151             : using dom::Sequence;
     152             : using media::NewRunnableFrom;
     153             : using media::NewTaskFrom;
     154             : using media::Pledge;
     155             : using media::Refcountable;
     156             : 
     157             : static Atomic<bool> sInShutdown;
     158             : 
     159             : typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
     160             : 
     161             : static bool
     162           0 : HostIsHttps(nsIURI &docURI)
     163             : {
     164             :   bool isHttps;
     165           0 :   nsresult rv = docURI.SchemeIs("https", &isHttps);
     166           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     167           0 :     return false;
     168             :   }
     169           0 :   return isHttps;
     170             : }
     171             : 
     172           0 : class SourceListener : public MediaStreamListener {
     173             : public:
     174             :   SourceListener();
     175             : 
     176             :   /**
     177             :    * Registers this source listener as belonging to the given window listener.
     178             :    */
     179             :   void Register(GetUserMediaWindowListener* aListener);
     180             : 
     181             :   /**
     182             :    * Marks this listener as active and adds itself as a listener to aStream.
     183             :    */
     184             :   void Activate(SourceMediaStream* aStream,
     185             :                 AudioDevice* aAudioDevice,
     186             :                 VideoDevice* aVideoDevice);
     187             : 
     188             :   /**
     189             :    * Stops all live tracks, finishes the associated MediaStream and cleans up.
     190             :    */
     191             :   void Stop();
     192             : 
     193             :   /**
     194             :    * Removes this SourceListener from its associated MediaStream and marks it
     195             :    * removed. Also removes the weak reference to the associated window listener.
     196             :    */
     197             :   void Remove();
     198             : 
     199             :   /**
     200             :    * Posts a task to stop the device associated with aTrackID and notifies the
     201             :    * associated window listener that a track was stopped.
     202             :    * Should this track be the last live one to be stopped, we'll also clean up.
     203             :    */
     204             :   void StopTrack(TrackID aTrackID);
     205             : 
     206             :   /**
     207             :    * Stops all screen/app/window/audioCapture sharing, but not camera or
     208             :    * microphone.
     209             :    */
     210             :   void StopSharing();
     211             : 
     212             :   MediaStream* Stream() const
     213             :   {
     214             :     return mStream;
     215             :   }
     216             : 
     217             :   SourceMediaStream* GetSourceStream();
     218             : 
     219           0 :   AudioDevice* GetAudioDevice() const
     220             :   {
     221           0 :     return mAudioDevice;
     222             :   }
     223             : 
     224           0 :   VideoDevice* GetVideoDevice() const
     225             :   {
     226           0 :     return mVideoDevice;
     227             :   }
     228             : 
     229             :   void GetSettings(dom::MediaTrackSettings& aOutSettings, TrackID aTrackID);
     230             : 
     231             :   void NotifyPull(MediaStreamGraph* aGraph,
     232             :                   StreamTime aDesiredTime) override;
     233             : 
     234             :   void NotifyEvent(MediaStreamGraph* aGraph,
     235             :                    MediaStreamGraphEvent aEvent) override;
     236             : 
     237             :   void NotifyFinished();
     238             : 
     239             :   /**
     240             :    * this can be in response to our own RemoveListener() (via ::Remove()), or
     241             :    * because the DOM GC'd the DOMLocalMediaStream/etc we're attached to.
     242             :    */
     243             :   void NotifyRemoved();
     244             : 
     245             :   void NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
     246             : 
     247           0 :   bool Activated() const
     248             :   {
     249           0 :     return mActivated;
     250             :   }
     251             : 
     252             :   bool Stopped() const
     253             :   {
     254             :     return mStopped;
     255             :   }
     256             : 
     257             :   bool CapturingVideo() const;
     258             : 
     259             :   bool CapturingAudio() const;
     260             : 
     261             :   bool CapturingScreen() const;
     262             : 
     263             :   bool CapturingWindow() const;
     264             : 
     265             :   bool CapturingApplication() const;
     266             : 
     267             :   bool CapturingBrowser() const;
     268             : 
     269             :   already_AddRefed<PledgeVoid>
     270             :   ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
     271             :                           TrackID aTrackID,
     272             :                           const dom::MediaTrackConstraints& aConstraints,
     273             :                           dom::CallerType aCallerType);
     274             : 
     275             :   PrincipalHandle GetPrincipalHandle() const;
     276             : 
     277             : private:
     278             :   // true after this listener has been Activate()d in a WindowListener.
     279             :   // MainThread only.
     280             :   bool mActivated;
     281             : 
     282             :   // true after this listener has had all devices stopped. MainThread only.
     283             :   bool mStopped;
     284             : 
     285             :   // true after the stream this listener is listening to has finished in the
     286             :   // MediaStreamGraph. MainThread only.
     287             :   bool mFinished;
     288             : 
     289             :   // true after this listener has been removed from its MediaStream.
     290             :   // MainThread only.
     291             :   bool mRemoved;
     292             : 
     293             :   // true if we have stopped mAudioDevice. MainThread only.
     294             :   bool mAudioStopped;
     295             : 
     296             :   // true if we have stopped mVideoDevice. MainThread only.
     297             :   bool mVideoStopped;
     298             : 
     299             :   // never ever indirect off this; just for assertions
     300             :   PRThread* mMainThreadCheck;
     301             : 
     302             :   // Set in Register() on main thread, then read from any thread.
     303             :   PrincipalHandle mPrincipalHandle;
     304             : 
     305             :   // Weak pointer to the window listener that owns us. MainThread only.
     306             :   GetUserMediaWindowListener* mWindowListener;
     307             : 
     308             :   // Set at Activate on MainThread
     309             : 
     310             :   // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
     311             :   // No locking needed as they're only addrefed except on the MediaManager thread
     312             :   RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
     313             :   RefPtr<VideoDevice> mVideoDevice; // threadsafe refcnt
     314             :   RefPtr<SourceMediaStream> mStream; // threadsafe refcnt
     315             : };
     316             : 
     317             : /**
     318             :  * This class represents a WindowID and handles all MediaStreamListeners
     319             :  * (here subclassed as SourceListeners) used to feed GetUserMedia source
     320             :  * streams. It proxies feedback from them into messages for browser chrome.
     321             :  * The SourceListeners are used to Start() and Stop() the underlying
     322             :  * MediaEngineSource when MediaStreams are assigned and deassigned in content.
     323             :  */
     324             : class GetUserMediaWindowListener
     325             : {
     326             :   friend MediaManager;
     327             : public:
     328           0 :   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GetUserMediaWindowListener)
     329             : 
     330             :   // Create in an inactive state
     331           0 :   GetUserMediaWindowListener(base::Thread *aThread,
     332             :     uint64_t aWindowID,
     333             :     const PrincipalHandle& aPrincipalHandle)
     334           0 :     : mMediaThread(aThread)
     335             :     , mWindowID(aWindowID)
     336             :     , mPrincipalHandle(aPrincipalHandle)
     337           0 :     , mChromeNotificationTaskPosted(false)
     338           0 :   {}
     339             : 
     340             :   /**
     341             :    * Registers an inactive gUM source listener for this WindowListener.
     342             :    */
     343           0 :   void Register(SourceListener* aListener)
     344             :   {
     345           0 :     MOZ_ASSERT(NS_IsMainThread());
     346           0 :     if (!aListener || aListener->Activated()) {
     347           0 :       MOZ_ASSERT(false, "Invalid listener");
     348             :       return;
     349             :     }
     350           0 :     if (mInactiveListeners.Contains(aListener)) {
     351           0 :       MOZ_ASSERT(false, "Already registered");
     352             :       return;
     353             :     }
     354           0 :     if (mActiveListeners.Contains(aListener)) {
     355           0 :       MOZ_ASSERT(false, "Already activated");
     356             :       return;
     357             :     }
     358             : 
     359           0 :     aListener->Register(this);
     360           0 :     mInactiveListeners.AppendElement(aListener);
     361           0 :   }
     362             : 
     363             :   /**
     364             :    * Activates an already registered and inactive gUM source listener for this
     365             :    * WindowListener.
     366             :    */
     367           0 :   void Activate(SourceListener* aListener,
     368             :                 SourceMediaStream* aStream,
     369             :                 AudioDevice* aAudioDevice,
     370             :                 VideoDevice* aVideoDevice)
     371             :   {
     372           0 :     MOZ_ASSERT(NS_IsMainThread());
     373             : 
     374           0 :     if (!aListener || aListener->Activated()) {
     375           0 :       MOZ_ASSERT(false, "Cannot activate already activated source listener");
     376             :       return;
     377             :     }
     378             : 
     379           0 :     if (!mInactiveListeners.RemoveElement(aListener)) {
     380           0 :       MOZ_ASSERT(false, "Cannot activate non-registered source listener");
     381             :       return;
     382             :     }
     383             : 
     384           0 :     RefPtr<SourceListener> listener = aListener;
     385           0 :     listener->Activate(aStream, aAudioDevice, aVideoDevice);
     386           0 :     mActiveListeners.AppendElement(listener.forget());
     387           0 :   }
     388             : 
     389             :   // Can be invoked from EITHER MainThread or MSG thread
     390           0 :   void Stop()
     391             :   {
     392           0 :     MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
     393             : 
     394           0 :     for (auto& source : mActiveListeners) {
     395           0 :       source->Stop();
     396             :     }
     397             : 
     398             :     // Once all tracks have stopped, that will trigger the chrome notification
     399           0 :   }
     400             : 
     401             :   /**
     402             :    * Removes all SourceListeners from this window listener.
     403             :    * Removes this window listener from the list of active windows, so callers
     404             :    * need to make sure to hold a strong reference.
     405             :    */
     406           0 :   void RemoveAll()
     407             :   {
     408           0 :     MOZ_ASSERT(NS_IsMainThread());
     409             : 
     410             :     // Shallow copy since SourceListener::Remove() will modify the arrays.
     411           0 :     nsTArray<RefPtr<SourceListener>> listeners(mInactiveListeners.Length()
     412           0 :                                                + mActiveListeners.Length());
     413           0 :     listeners.AppendElements(mInactiveListeners);
     414           0 :     listeners.AppendElements(mActiveListeners);
     415           0 :     for (auto& l : listeners) {
     416           0 :       Remove(l);
     417             :     }
     418             : 
     419           0 :     MOZ_ASSERT(mInactiveListeners.Length() == 0);
     420           0 :     MOZ_ASSERT(mActiveListeners.Length() == 0);
     421             : 
     422           0 :     RefPtr<MediaManager> mgr = MediaManager::GetIfExists();
     423           0 :     if (!mgr) {
     424           0 :       MOZ_ASSERT(false, "MediaManager should stay until everything is removed");
     425             :       return;
     426             :     }
     427             :     GetUserMediaWindowListener* windowListener =
     428           0 :       mgr->GetWindowListener(mWindowID);
     429             : 
     430           0 :     if (!windowListener) {
     431           0 :       nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     432           0 :       auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
     433           0 :       if (globalWindow) {
     434             :         RefPtr<GetUserMediaRequest> req =
     435           0 :           new GetUserMediaRequest(globalWindow->AsInner(),
     436           0 :                                   NullString(), NullString());
     437           0 :         obs->NotifyObservers(req, "recording-device-stopped", nullptr);
     438             :       }
     439           0 :       return;
     440             :     }
     441             : 
     442           0 :     MOZ_ASSERT(windowListener == this,
     443             :                "There should only be one window listener per window ID");
     444             : 
     445           0 :     LOG(("GUMWindowListener %p removing windowID %" PRIu64, this, mWindowID));
     446           0 :     mgr->RemoveWindowID(mWindowID);
     447             :   }
     448             : 
     449           0 :   bool Remove(SourceListener* aListener)
     450             :   {
     451           0 :     MOZ_ASSERT(NS_IsMainThread());
     452             : 
     453           0 :     if (!mInactiveListeners.RemoveElement(aListener) &&
     454           0 :         !mActiveListeners.RemoveElement(aListener)) {
     455           0 :       return false;
     456             :     }
     457             : 
     458           0 :     MOZ_ASSERT(!mInactiveListeners.Contains(aListener),
     459             :                "A SourceListener should only be once in one of "
     460             :                "mInactiveListeners and mActiveListeners");
     461           0 :     MOZ_ASSERT(!mActiveListeners.Contains(aListener),
     462             :                "A SourceListener should only be once in one of "
     463             :                "mInactiveListeners and mActiveListeners");
     464             : 
     465           0 :     LOG(("GUMWindowListener %p removing SourceListener %p.", this, aListener));
     466           0 :     aListener->Remove();
     467             : 
     468           0 :     if (VideoDevice* removedDevice = aListener->GetVideoDevice()) {
     469           0 :       bool revokeVideoPermission = true;
     470           0 :       nsString removedRawId;
     471           0 :       nsString removedSourceType;
     472           0 :       removedDevice->GetRawId(removedRawId);
     473           0 :       removedDevice->GetMediaSource(removedSourceType);
     474           0 :       for (const auto& l : mActiveListeners) {
     475           0 :         if (VideoDevice* device = l->GetVideoDevice()) {
     476           0 :           nsString rawId;
     477           0 :           device->GetRawId(rawId);
     478           0 :           if (removedRawId.Equals(rawId)) {
     479           0 :             revokeVideoPermission = false;
     480           0 :             break;
     481             :           }
     482             :         }
     483             :       }
     484             : 
     485           0 :       if (revokeVideoPermission) {
     486           0 :         nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     487           0 :         auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
     488           0 :         nsPIDOMWindowInner* window = globalWindow ? globalWindow->AsInner()
     489           0 :                                                   : nullptr;
     490             :         RefPtr<GetUserMediaRequest> req =
     491           0 :           new GetUserMediaRequest(window, removedRawId, removedSourceType);
     492           0 :         obs->NotifyObservers(req, "recording-device-stopped", nullptr);
     493             :       }
     494             :     }
     495             : 
     496           0 :     if (AudioDevice* removedDevice = aListener->GetAudioDevice()) {
     497           0 :       bool revokeAudioPermission = true;
     498           0 :       nsString removedRawId;
     499           0 :       nsString removedSourceType;
     500           0 :       removedDevice->GetRawId(removedRawId);
     501           0 :       removedDevice->GetMediaSource(removedSourceType);
     502           0 :       for (const auto& l : mActiveListeners) {
     503           0 :         if (AudioDevice* device = l->GetAudioDevice()) {
     504           0 :           nsString rawId;
     505           0 :           device->GetRawId(rawId);
     506           0 :           if (removedRawId.Equals(rawId)) {
     507           0 :             revokeAudioPermission = false;
     508           0 :             break;
     509             :           }
     510             :         }
     511             :       }
     512             : 
     513           0 :       if (revokeAudioPermission) {
     514           0 :         nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     515           0 :         auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
     516           0 :         nsPIDOMWindowInner* window = globalWindow ? globalWindow->AsInner()
     517           0 :                                                   : nullptr;
     518             :         RefPtr<GetUserMediaRequest> req =
     519           0 :           new GetUserMediaRequest(window, removedRawId, removedSourceType);
     520           0 :         obs->NotifyObservers(req, "recording-device-stopped", nullptr);
     521             :       }
     522             :     }
     523             : 
     524           0 :     if (mInactiveListeners.Length() == 0 &&
     525           0 :         mActiveListeners.Length() == 0) {
     526           0 :       LOG(("GUMWindowListener %p Removed the last SourceListener. "
     527             :            "Cleaning up.", this));
     528           0 :       RemoveAll();
     529             :     }
     530             : 
     531           0 :     return true;
     532             :   }
     533             : 
     534             :   void StopSharing();
     535             : 
     536             :   /**
     537             :    * Called by one of our SourceListeners when one of its tracks has stopped.
     538             :    * Schedules an event for the next stable state to update chrome.
     539             :    */
     540             :   void NotifySourceTrackStopped();
     541             : 
     542             :   /**
     543             :    * Called in stable state to send a notification to update chrome.
     544             :    */
     545             :   void NotifyChromeOfTrackStops();
     546             : 
     547           0 :   bool CapturingVideo() const
     548             :   {
     549           0 :     MOZ_ASSERT(NS_IsMainThread());
     550           0 :     for (auto& l : mActiveListeners) {
     551           0 :       if (l->CapturingVideo()) {
     552           0 :         return true;
     553             :       }
     554             :     }
     555           0 :     return false;
     556             :   }
     557           0 :   bool CapturingAudio() const
     558             :   {
     559           0 :     MOZ_ASSERT(NS_IsMainThread());
     560           0 :     for (auto& l : mActiveListeners) {
     561           0 :       if (l->CapturingAudio()) {
     562           0 :         return true;
     563             :       }
     564             :     }
     565           0 :     return false;
     566             :   }
     567           0 :   bool CapturingScreen() const
     568             :   {
     569           0 :     MOZ_ASSERT(NS_IsMainThread());
     570           0 :     for (auto& l : mActiveListeners) {
     571           0 :       if (l->CapturingScreen()) {
     572           0 :         return true;
     573             :       }
     574             :     }
     575           0 :     return false;
     576             :   }
     577           0 :   bool CapturingWindow() const
     578             :   {
     579           0 :     MOZ_ASSERT(NS_IsMainThread());
     580           0 :     for (auto& l : mActiveListeners) {
     581           0 :       if (l->CapturingWindow()) {
     582           0 :         return true;
     583             :       }
     584             :     }
     585           0 :     return false;
     586             :   }
     587           0 :   bool CapturingApplication() const
     588             :   {
     589           0 :     MOZ_ASSERT(NS_IsMainThread());
     590           0 :     for (auto& l : mActiveListeners) {
     591           0 :       if (l->CapturingApplication()) {
     592           0 :         return true;
     593             :       }
     594             :     }
     595           0 :     return false;
     596             :   }
     597           0 :   bool CapturingBrowser() const
     598             :   {
     599           0 :     MOZ_ASSERT(NS_IsMainThread());
     600           0 :     for (auto& l : mActiveListeners) {
     601           0 :       if (l->CapturingBrowser()) {
     602           0 :         return true;
     603             :       }
     604             :     }
     605           0 :     return false;
     606             :   }
     607             : 
     608           0 :   uint64_t WindowID() const
     609             :   {
     610           0 :     return mWindowID;
     611             :   }
     612             : 
     613           0 :   PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
     614             : 
     615             : private:
     616           0 :   ~GetUserMediaWindowListener()
     617           0 :   {
     618           0 :     for (auto& l : mInactiveListeners) {
     619           0 :       l->NotifyRemoved();
     620             :     }
     621           0 :     mInactiveListeners.Clear();
     622           0 :     for (auto& l : mActiveListeners) {
     623           0 :       l->NotifyRemoved();
     624             :     }
     625           0 :     mActiveListeners.Clear();
     626           0 :     Unused << mMediaThread;
     627             :     // It's OK to release mStream on any thread; they have thread-safe
     628             :     // refcounts.
     629           0 :   }
     630             : 
     631             :   // Set at construction
     632             :   base::Thread* mMediaThread;
     633             : 
     634             :   uint64_t mWindowID;
     635             :   const PrincipalHandle mPrincipalHandle;
     636             : 
     637             :   // true if we have scheduled a task to notify chrome in the next stable state.
     638             :   // The task will reset this to false. MainThread only.
     639             :   bool mChromeNotificationTaskPosted;
     640             : 
     641             :   nsTArray<RefPtr<SourceListener>> mInactiveListeners;
     642             :   nsTArray<RefPtr<SourceListener>> mActiveListeners;
     643             : };
     644             : 
     645             : /**
     646             :  * Send an error back to content.
     647             :  * Do this only on the main thread. The onSuccess callback is also passed here
     648             :  * so it can be released correctly.
     649             :  */
     650             : template<class SuccessCallbackType>
     651             : class ErrorCallbackRunnable : public Runnable
     652             : {
     653             : public:
     654           0 :   ErrorCallbackRunnable(nsCOMPtr<SuccessCallbackType>&& aOnSuccess,
     655             :                         nsCOMPtr<nsIDOMGetUserMediaErrorCallback>&& aOnFailure,
     656             :                         MediaMgrError& aError,
     657             :                         uint64_t aWindowID)
     658             :     : Runnable("ErrorCallbackRunnable")
     659             :     , mError(&aError)
     660             :     , mWindowID(aWindowID)
     661           0 :     , mManager(MediaManager::GetInstance())
     662             :   {
     663           0 :     mOnSuccess.swap(aOnSuccess);
     664           0 :     mOnFailure.swap(aOnFailure);
     665           0 :   }
     666             : 
     667             :   NS_IMETHOD
     668           0 :   Run() override
     669             :   {
     670           0 :     MOZ_ASSERT(NS_IsMainThread());
     671             : 
     672           0 :     nsCOMPtr<SuccessCallbackType> onSuccess = mOnSuccess.forget();
     673           0 :     nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
     674             : 
     675             :     // Only run if the window is still active.
     676           0 :     if (!(mManager->IsWindowStillActive(mWindowID))) {
     677           0 :       return NS_OK;
     678             :     }
     679             :     // This is safe since we're on main-thread, and the windowlist can only
     680             :     // be invalidated from the main-thread (see OnNavigation)
     681           0 :     if (auto* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID)) {
     682             :       RefPtr<MediaStreamError> error =
     683           0 :         new MediaStreamError(window->AsInner(), *mError);
     684           0 :       onFailure->OnError(error);
     685             :     }
     686           0 :     return NS_OK;
     687             :   }
     688             : private:
     689           0 :   ~ErrorCallbackRunnable()
     690             :   {
     691           0 :     MOZ_ASSERT(!mOnSuccess && !mOnFailure);
     692           0 :   }
     693             : 
     694             :   nsCOMPtr<SuccessCallbackType> mOnSuccess;
     695             :   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
     696             :   RefPtr<MediaMgrError> mError;
     697             :   uint64_t mWindowID;
     698             :   RefPtr<MediaManager> mManager; // get ref to this when creating the runnable
     699             : };
     700             : 
     701             : /**
     702             :  * nsIMediaDevice implementation.
     703             :  */
     704           0 : NS_IMPL_ISUPPORTS(MediaDevice, nsIMediaDevice)
     705             : 
     706           0 : MediaDevice::MediaDevice(MediaEngineSource* aSource, bool aIsVideo)
     707           0 :   : mScary(aSource->GetScary())
     708           0 :   , mMediaSource(aSource->GetMediaSource())
     709             :   , mSource(aSource)
     710           0 :   , mIsVideo(aIsVideo)
     711             : {
     712           0 :   mSource->GetName(mName);
     713           0 :   nsCString id;
     714           0 :   mSource->GetUUID(id);
     715           0 :   CopyUTF8toUTF16(id, mID);
     716           0 : }
     717             : 
     718           0 : VideoDevice::VideoDevice(MediaEngineVideoSource* aSource)
     719           0 :   : MediaDevice(aSource, true)
     720           0 : {}
     721             : 
     722             : /**
     723             :  * Helper functions that implement the constraints algorithm from
     724             :  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
     725             :  */
     726             : 
     727             : bool
     728           0 : MediaDevice::StringsContain(const OwningStringOrStringSequence& aStrings,
     729             :                             nsString aN)
     730             : {
     731           0 :   return aStrings.IsString() ? aStrings.GetAsString() == aN
     732           0 :                              : aStrings.GetAsStringSequence().Contains(aN);
     733             : }
     734             : 
     735             : /* static */ uint32_t
     736           0 : MediaDevice::FitnessDistance(nsString aN,
     737             :                              const ConstrainDOMStringParameters& aParams)
     738             : {
     739           0 :   if (aParams.mExact.WasPassed() && !StringsContain(aParams.mExact.Value(), aN)) {
     740           0 :     return UINT32_MAX;
     741             :   }
     742           0 :   if (aParams.mIdeal.WasPassed() && !StringsContain(aParams.mIdeal.Value(), aN)) {
     743           0 :     return 1;
     744             :   }
     745           0 :   return 0;
     746             : }
     747             : 
     748             : // Binding code doesn't templatize well...
     749             : 
     750             : /* static */ uint32_t
     751           0 : MediaDevice::FitnessDistance(nsString aN,
     752             :     const OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint)
     753             : {
     754           0 :   if (aConstraint.IsString()) {
     755           0 :     ConstrainDOMStringParameters params;
     756           0 :     params.mIdeal.Construct();
     757           0 :     params.mIdeal.Value().SetAsString() = aConstraint.GetAsString();
     758           0 :     return FitnessDistance(aN, params);
     759           0 :   } else if (aConstraint.IsStringSequence()) {
     760           0 :     ConstrainDOMStringParameters params;
     761           0 :     params.mIdeal.Construct();
     762           0 :     params.mIdeal.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
     763           0 :     return FitnessDistance(aN, params);
     764             :   } else {
     765           0 :     return FitnessDistance(aN, aConstraint.GetAsConstrainDOMStringParameters());
     766             :   }
     767             : }
     768             : 
     769             : uint32_t
     770           0 : MediaDevice::GetBestFitnessDistance(
     771             :     const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
     772             :     bool aIsChrome)
     773             : {
     774           0 :   nsString mediaSource;
     775           0 :   GetMediaSource(mediaSource);
     776             : 
     777             :   // This code is reused for audio, where the mediaSource constraint does
     778             :   // not currently have a function, but because it defaults to "camera" in
     779             :   // webidl, we ignore it for audio here.
     780           0 :   if (!mediaSource.EqualsASCII("microphone")) {
     781           0 :     for (const auto& constraint : aConstraintSets) {
     782           0 :       if (constraint->mMediaSource.mIdeal.find(mediaSource) ==
     783           0 :           constraint->mMediaSource.mIdeal.end()) {
     784           0 :         return UINT32_MAX;
     785             :       }
     786             :     }
     787             :   }
     788             :   // Forward request to underlying object to interrogate per-mode capabilities.
     789             :   // Pass in device's origin-specific id for deviceId constraint comparison.
     790           0 :   nsString id;
     791           0 :   if (aIsChrome) {
     792           0 :     GetRawId(id);
     793             :   } else {
     794           0 :     GetId(id);
     795             :   }
     796           0 :   return mSource->GetBestFitnessDistance(aConstraintSets, id);
     797             : }
     798             : 
     799           0 : AudioDevice::AudioDevice(MediaEngineAudioSource* aSource)
     800           0 :   : MediaDevice(aSource, false)
     801             : {
     802           0 :   mMediaSource = aSource->GetMediaSource();
     803           0 : }
     804             : 
     805             : NS_IMETHODIMP
     806           0 : MediaDevice::GetName(nsAString& aName)
     807             : {
     808           0 :   aName.Assign(mName);
     809           0 :   return NS_OK;
     810             : }
     811             : 
     812             : NS_IMETHODIMP
     813           0 : MediaDevice::GetType(nsAString& aType)
     814             : {
     815           0 :   return NS_OK;
     816             : }
     817             : 
     818             : NS_IMETHODIMP
     819           0 : VideoDevice::GetType(nsAString& aType)
     820             : {
     821           0 :   aType.AssignLiteral(u"video");
     822           0 :   return NS_OK;
     823             : }
     824             : 
     825             : NS_IMETHODIMP
     826           0 : AudioDevice::GetType(nsAString& aType)
     827             : {
     828           0 :   aType.AssignLiteral(u"audio");
     829           0 :   return NS_OK;
     830             : }
     831             : 
     832             : NS_IMETHODIMP
     833           0 : MediaDevice::GetId(nsAString& aID)
     834             : {
     835           0 :   aID.Assign(mID);
     836           0 :   return NS_OK;
     837             : }
     838             : 
     839             : NS_IMETHODIMP
     840           0 : MediaDevice::GetRawId(nsAString& aID)
     841             : {
     842           0 :   aID.Assign(mRawID);
     843           0 :   return NS_OK;
     844             : }
     845             : 
     846             : NS_IMETHODIMP
     847           0 : MediaDevice::GetScary(bool* aScary)
     848             : {
     849           0 :   *aScary = mScary;
     850           0 :   return NS_OK;
     851             : }
     852             : 
     853             : void
     854           0 : MediaDevice::SetId(const nsAString& aID)
     855             : {
     856           0 :   mID.Assign(aID);
     857           0 : }
     858             : 
     859             : void
     860           0 : MediaDevice::SetRawId(const nsAString& aID)
     861             : {
     862           0 :   mRawID.Assign(aID);
     863           0 : }
     864             : 
     865             : NS_IMETHODIMP
     866           0 : MediaDevice::GetMediaSource(nsAString& aMediaSource)
     867             : {
     868           0 :   if (mMediaSource == MediaSourceEnum::Microphone) {
     869           0 :     aMediaSource.Assign(NS_LITERAL_STRING("microphone"));
     870           0 :   } else if (mMediaSource == MediaSourceEnum::AudioCapture) {
     871           0 :     aMediaSource.Assign(NS_LITERAL_STRING("audioCapture"));
     872           0 :   } else if (mMediaSource == MediaSourceEnum::Window) { // this will go away
     873           0 :     aMediaSource.Assign(NS_LITERAL_STRING("window"));
     874             :   } else { // all the rest are shared
     875           0 :     aMediaSource.Assign(NS_ConvertUTF8toUTF16(
     876           0 :       dom::MediaSourceEnumValues::strings[uint32_t(mMediaSource)].value));
     877             :   }
     878           0 :   return NS_OK;
     879             : }
     880             : 
     881             : VideoDevice::Source*
     882           0 : VideoDevice::GetSource()
     883             : {
     884           0 :   return static_cast<Source*>(&*mSource);
     885             : }
     886             : 
     887             : AudioDevice::Source*
     888           0 : AudioDevice::GetSource()
     889             : {
     890           0 :   return static_cast<Source*>(&*mSource);
     891             : }
     892             : 
     893           0 : nsresult MediaDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
     894             :                                const MediaEnginePrefs &aPrefs,
     895             :                                const ipc::PrincipalInfo& aPrincipalInfo,
     896             :                                const char** aOutBadConstraint) {
     897           0 :   return GetSource()->Allocate(aConstraints, aPrefs, mID, aPrincipalInfo,
     898           0 :                                getter_AddRefs(mAllocationHandle),
     899           0 :                                aOutBadConstraint);
     900             : }
     901             : 
     902           0 : nsresult MediaDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
     903             :                               const MediaEnginePrefs &aPrefs,
     904             :                               const char** aOutBadConstraint) {
     905           0 :   return GetSource()->Restart(mAllocationHandle, aConstraints, aPrefs, mID,
     906           0 :                               aOutBadConstraint);
     907             : }
     908             : 
     909           0 : nsresult MediaDevice::Deallocate() {
     910           0 :   return GetSource()->Deallocate(mAllocationHandle);
     911             : }
     912             : 
     913             : static bool
     914           0 : IsOn(const OwningBooleanOrMediaTrackConstraints &aUnion) {
     915           0 :   return !aUnion.IsBoolean() || aUnion.GetAsBoolean();
     916             : }
     917             : 
     918             : static const MediaTrackConstraints&
     919           0 : GetInvariant(const OwningBooleanOrMediaTrackConstraints &aUnion) {
     920           0 :   static const MediaTrackConstraints empty;
     921           0 :   return aUnion.IsMediaTrackConstraints() ?
     922           0 :       aUnion.GetAsMediaTrackConstraints() : empty;
     923             : }
     924             : 
     925             : /**
     926             :  * This class is only needed since fake tracks are added dynamically.
     927             :  * Instead of refactoring to add them explicitly we let the DOMMediaStream
     928             :  * query us for the source as they become available.
     929             :  * Since they are used only for testing the API surface, we make them very
     930             :  * simple.
     931             :  */
     932             : class FakeTrackSourceGetter : public MediaStreamTrackSourceGetter
     933             : {
     934             : public:
     935             :   NS_DECL_ISUPPORTS_INHERITED
     936           0 :   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FakeTrackSourceGetter,
     937             :                                            MediaStreamTrackSourceGetter)
     938             : 
     939           0 :   explicit FakeTrackSourceGetter(nsIPrincipal* aPrincipal)
     940           0 :     : mPrincipal(aPrincipal) {}
     941             : 
     942             :   already_AddRefed<dom::MediaStreamTrackSource>
     943           0 :   GetMediaStreamTrackSource(TrackID aInputTrackID) override
     944             :   {
     945           0 :     NS_ASSERTION(kAudioTrack != aInputTrackID,
     946             :                  "Only fake tracks should appear dynamically");
     947           0 :     NS_ASSERTION(kVideoTrack != aInputTrackID,
     948             :                  "Only fake tracks should appear dynamically");
     949           0 :     return do_AddRef(new BasicTrackSource(mPrincipal));
     950             :   }
     951             : 
     952             : protected:
     953           0 :   virtual ~FakeTrackSourceGetter() {}
     954             : 
     955             :   nsCOMPtr<nsIPrincipal> mPrincipal;
     956             : };
     957             : 
     958           0 : NS_IMPL_ADDREF_INHERITED(FakeTrackSourceGetter, MediaStreamTrackSourceGetter)
     959           0 : NS_IMPL_RELEASE_INHERITED(FakeTrackSourceGetter, MediaStreamTrackSourceGetter)
     960           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FakeTrackSourceGetter)
     961           0 : NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSourceGetter)
     962           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(FakeTrackSourceGetter,
     963             :                                    MediaStreamTrackSourceGetter,
     964             :                                    mPrincipal)
     965             : 
     966             : /**
     967             :  * Creates a MediaStream, attaches a listener and fires off a success callback
     968             :  * to the DOM with the stream. We also pass in the error callback so it can
     969             :  * be released correctly.
     970             :  *
     971             :  * All of this must be done on the main thread!
     972             :  *
     973             :  * Note that the various GetUserMedia Runnable classes currently allow for
     974             :  * two streams.  If we ever need to support getting more than two streams
     975             :  * at once, we could convert everything to nsTArray<RefPtr<blah> >'s,
     976             :  * though that would complicate the constructors some.  Currently the
     977             :  * GetUserMedia spec does not allow for more than 2 streams to be obtained in
     978             :  * one call, to simplify handling of constraints.
     979             :  */
     980             : class GetUserMediaStreamRunnable : public Runnable
     981             : {
     982             : public:
     983           0 :   GetUserMediaStreamRunnable(
     984             :     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
     985             :     nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
     986             :     uint64_t aWindowID,
     987             :     GetUserMediaWindowListener* aWindowListener,
     988             :     SourceListener* aSourceListener,
     989             :     const ipc::PrincipalInfo& aPrincipalInfo,
     990             :     const MediaStreamConstraints& aConstraints,
     991             :     AudioDevice* aAudioDevice,
     992             :     VideoDevice* aVideoDevice,
     993             :     PeerIdentity* aPeerIdentity)
     994           0 :     : Runnable("GetUserMediaStreamRunnable")
     995             :     , mConstraints(aConstraints)
     996             :     , mAudioDevice(aAudioDevice)
     997             :     , mVideoDevice(aVideoDevice)
     998             :     , mWindowID(aWindowID)
     999             :     , mWindowListener(aWindowListener)
    1000             :     , mSourceListener(aSourceListener)
    1001             :     , mPrincipalInfo(aPrincipalInfo)
    1002             :     , mPeerIdentity(aPeerIdentity)
    1003           0 :     , mManager(MediaManager::GetInstance())
    1004             :   {
    1005           0 :     mOnSuccess.swap(aOnSuccess);
    1006           0 :     mOnFailure.swap(aOnFailure);
    1007           0 :   }
    1008             : 
    1009           0 :   ~GetUserMediaStreamRunnable() {}
    1010             : 
    1011           0 :   class TracksAvailableCallback : public OnTracksAvailableCallback
    1012             :   {
    1013             :   public:
    1014           0 :     TracksAvailableCallback(MediaManager* aManager,
    1015             :                             already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
    1016             :                             uint64_t aWindowID,
    1017             :                             DOMMediaStream* aStream)
    1018           0 :       : mWindowID(aWindowID), mOnSuccess(aSuccess), mManager(aManager),
    1019           0 :         mStream(aStream) {}
    1020           0 :     void NotifyTracksAvailable(DOMMediaStream* aStream) override
    1021             :     {
    1022             :       // We're in the main thread, so no worries here.
    1023           0 :       if (!(mManager->IsWindowStillActive(mWindowID))) {
    1024           0 :         return;
    1025             :       }
    1026             : 
    1027             :       // Start currentTime from the point where this stream was successfully
    1028             :       // returned.
    1029           0 :       aStream->SetLogicalStreamStartTime(aStream->GetPlaybackStream()->GetCurrentTime());
    1030             : 
    1031             :       // This is safe since we're on main-thread, and the windowlist can only
    1032             :       // be invalidated from the main-thread (see OnNavigation)
    1033           0 :       LOG(("Returning success for getUserMedia()"));
    1034           0 :       mOnSuccess->OnSuccess(aStream);
    1035             :     }
    1036             :     uint64_t mWindowID;
    1037             :     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
    1038             :     RefPtr<MediaManager> mManager;
    1039             :     // Keep the DOMMediaStream alive until the NotifyTracksAvailable callback
    1040             :     // has fired, otherwise we might immediately destroy the DOMMediaStream and
    1041             :     // shut down the underlying MediaStream prematurely.
    1042             :     // This creates a cycle which is broken when NotifyTracksAvailable
    1043             :     // is fired (which will happen unless the browser shuts down,
    1044             :     // since we only add this callback when we've successfully appended
    1045             :     // the desired tracks in the MediaStreamGraph) or when
    1046             :     // DOMMediaStream::NotifyMediaStreamGraphShutdown is called.
    1047             :     RefPtr<DOMMediaStream> mStream;
    1048             :   };
    1049             : 
    1050             :   NS_IMETHOD
    1051           0 :   Run() override
    1052             :   {
    1053           0 :     MOZ_ASSERT(NS_IsMainThread());
    1054           0 :     nsGlobalWindow* globalWindow = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
    1055           0 :     nsPIDOMWindowInner* window = globalWindow ? globalWindow->AsInner() : nullptr;
    1056             : 
    1057             :     // We're on main-thread, and the windowlist can only
    1058             :     // be invalidated from the main-thread (see OnNavigation)
    1059             :     GetUserMediaWindowListener* listener =
    1060           0 :       mManager->GetWindowListener(mWindowID);
    1061           0 :     if (!listener || !window || !window->GetExtantDoc()) {
    1062             :       // This window is no longer live.  mListener has already been removed
    1063           0 :       return NS_OK;
    1064             :     }
    1065             : 
    1066             :     MediaStreamGraph::GraphDriverType graphDriverType =
    1067           0 :       mAudioDevice ? MediaStreamGraph::AUDIO_THREAD_DRIVER
    1068           0 :                    : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
    1069             :     MediaStreamGraph* msg =
    1070             :       MediaStreamGraph::GetInstance(graphDriverType,
    1071           0 :                                     dom::AudioChannel::Normal, window);
    1072             : 
    1073           0 :     RefPtr<DOMMediaStream> domStream;
    1074           0 :     RefPtr<SourceMediaStream> stream;
    1075             :     // AudioCapture is a special case, here, in the sense that we're not really
    1076             :     // using the audio source and the SourceMediaStream, which acts as
    1077             :     // placeholders. We re-route a number of stream internaly in the MSG and mix
    1078             :     // them down instead.
    1079           0 :     if (mAudioDevice &&
    1080           0 :         mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) {
    1081             :       // It should be possible to pipe the capture stream to anything. CORS is
    1082             :       // not a problem here, we got explicit user content.
    1083           0 :       nsCOMPtr<nsIPrincipal> principal = window->GetExtantDoc()->NodePrincipal();
    1084             :       domStream =
    1085           0 :         DOMMediaStream::CreateAudioCaptureStreamAsInput(window, principal, msg);
    1086             : 
    1087           0 :       stream = msg->CreateSourceStream(); // Placeholder
    1088           0 :       msg->RegisterCaptureStreamForWindow(
    1089           0 :             mWindowID, domStream->GetInputStream()->AsProcessedStream());
    1090           0 :       window->SetAudioCapture(true);
    1091             :     } else {
    1092             :       class LocalTrackSource : public MediaStreamTrackSource
    1093             :       {
    1094             :       public:
    1095           0 :         LocalTrackSource(nsIPrincipal* aPrincipal,
    1096             :                          const nsString& aLabel,
    1097             :                          SourceListener* aListener,
    1098             :                          const MediaSourceEnum aSource,
    1099             :                          const TrackID aTrackID,
    1100             :                          const PeerIdentity* aPeerIdentity)
    1101           0 :           : MediaStreamTrackSource(aPrincipal, aLabel), mListener(aListener),
    1102           0 :             mSource(aSource), mTrackID(aTrackID), mPeerIdentity(aPeerIdentity) {}
    1103             : 
    1104           0 :         MediaSourceEnum GetMediaSource() const override
    1105             :         {
    1106           0 :           return mSource;
    1107             :         }
    1108             : 
    1109           0 :         const PeerIdentity* GetPeerIdentity() const override
    1110             :         {
    1111           0 :           return mPeerIdentity;
    1112             :         }
    1113             : 
    1114             : 
    1115             :         already_AddRefed<PledgeVoid>
    1116           0 :         ApplyConstraints(nsPIDOMWindowInner* aWindow,
    1117             :                          const MediaTrackConstraints& aConstraints,
    1118             :                          dom::CallerType aCallerType) override
    1119             :         {
    1120           0 :           if (sInShutdown || !mListener) {
    1121             :             // Track has been stopped, or we are in shutdown. In either case
    1122             :             // there's no observable outcome, so pretend we succeeded.
    1123           0 :             RefPtr<PledgeVoid> p = new PledgeVoid();
    1124           0 :             p->Resolve(false);
    1125           0 :             return p.forget();
    1126             :           }
    1127           0 :           return mListener->ApplyConstraintsToTrack(aWindow, mTrackID,
    1128           0 :                                                     aConstraints, aCallerType);
    1129             :         }
    1130             : 
    1131             :         void
    1132           0 :         GetSettings(dom::MediaTrackSettings& aOutSettings) override
    1133             :         {
    1134           0 :           if (mListener) {
    1135           0 :             mListener->GetSettings(aOutSettings, mTrackID);
    1136             :           }
    1137           0 :         }
    1138             : 
    1139           0 :         void Stop() override
    1140             :         {
    1141           0 :           if (mListener) {
    1142           0 :             mListener->StopTrack(mTrackID);
    1143           0 :             mListener = nullptr;
    1144             :           }
    1145           0 :         }
    1146             : 
    1147             :       protected:
    1148           0 :         ~LocalTrackSource() {}
    1149             : 
    1150             :         RefPtr<SourceListener> mListener;
    1151             :         const MediaSourceEnum mSource;
    1152             :         const TrackID mTrackID;
    1153             :         const RefPtr<const PeerIdentity> mPeerIdentity;
    1154             :       };
    1155             : 
    1156           0 :       nsCOMPtr<nsIPrincipal> principal;
    1157           0 :       if (mPeerIdentity) {
    1158           0 :         principal = NullPrincipal::CreateWithInheritedAttributes(window->GetExtantDoc()->NodePrincipal());
    1159             :       } else {
    1160           0 :         principal = window->GetExtantDoc()->NodePrincipal();
    1161             :       }
    1162             : 
    1163             :       // Normal case, connect the source stream to the track union stream to
    1164             :       // avoid us blocking. Pass a simple TrackSourceGetter for potential
    1165             :       // fake tracks. Apart from them gUM never adds tracks dynamically.
    1166             :       domStream =
    1167           0 :         DOMLocalMediaStream::CreateSourceStreamAsInput(window, msg,
    1168           0 :                                                        new FakeTrackSourceGetter(principal));
    1169           0 :       stream = domStream->GetInputStream()->AsSourceStream();
    1170             : 
    1171           0 :       if (mAudioDevice) {
    1172           0 :         nsString audioDeviceName;
    1173           0 :         mAudioDevice->GetName(audioDeviceName);
    1174             :         const MediaSourceEnum source =
    1175           0 :           mAudioDevice->GetSource()->GetMediaSource();
    1176             :         RefPtr<MediaStreamTrackSource> audioSource =
    1177             :           new LocalTrackSource(principal, audioDeviceName, mSourceListener,
    1178           0 :                                source, kAudioTrack, mPeerIdentity);
    1179           0 :         MOZ_ASSERT(IsOn(mConstraints.mAudio));
    1180             :         RefPtr<MediaStreamTrack> track =
    1181           0 :           domStream->CreateDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioSource,
    1182           0 :                                     GetInvariant(mConstraints.mAudio));
    1183           0 :         domStream->AddTrackInternal(track);
    1184             :       }
    1185           0 :       if (mVideoDevice) {
    1186           0 :         nsString videoDeviceName;
    1187           0 :         mVideoDevice->GetName(videoDeviceName);
    1188             :         const MediaSourceEnum source =
    1189           0 :           mVideoDevice->GetSource()->GetMediaSource();
    1190             :         RefPtr<MediaStreamTrackSource> videoSource =
    1191             :           new LocalTrackSource(principal, videoDeviceName, mSourceListener,
    1192           0 :                                source, kVideoTrack, mPeerIdentity);
    1193           0 :         MOZ_ASSERT(IsOn(mConstraints.mVideo));
    1194             :         RefPtr<MediaStreamTrack> track =
    1195           0 :           domStream->CreateDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoSource,
    1196           0 :                                     GetInvariant(mConstraints.mVideo));
    1197           0 :         domStream->AddTrackInternal(track);
    1198             :       }
    1199             :     }
    1200             : 
    1201           0 :     if (!domStream || !stream || sInShutdown) {
    1202           0 :       nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
    1203           0 :       LOG(("Returning error for getUserMedia() - no stream"));
    1204             : 
    1205           0 :       if (auto* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID)) {
    1206           0 :         RefPtr<MediaStreamError> error = new MediaStreamError(window->AsInner(),
    1207           0 :             NS_LITERAL_STRING("InternalError"),
    1208           0 :             sInShutdown ? NS_LITERAL_STRING("In shutdown") :
    1209           0 :                           NS_LITERAL_STRING("No stream."));
    1210           0 :         onFailure->OnError(error);
    1211             :       }
    1212           0 :       return NS_OK;
    1213             :     }
    1214             : 
    1215             :     // Activate our source listener. We'll call Start() on the source when we
    1216             :     // get a callback that the MediaStream has started consuming. The listener
    1217             :     // is freed when the page is invalidated (on navigation or close).
    1218           0 :     mWindowListener->Activate(mSourceListener, stream, mAudioDevice, mVideoDevice);
    1219             : 
    1220             :     // Note: includes JS callbacks; must be released on MainThread
    1221             :     auto callback = MakeRefPtr<Refcountable<UniquePtr<OnTracksAvailableCallback>>>(
    1222           0 :         new TracksAvailableCallback(mManager, mOnSuccess.forget(), mWindowID, domStream));
    1223             : 
    1224             :     // Dispatch to the media thread to ask it to start the sources,
    1225             :     // because that can take a while.
    1226             :     // Pass ownership of domStream through the lambda to
    1227             :     // GetUserMediaNotificationEvent to ensure it's kept alive until the
    1228             :     // GetUserMediaNotificationEvent runs or is discarded.
    1229           0 :     RefPtr<GetUserMediaStreamRunnable> self = this;
    1230           0 :     MediaManager::PostTask(NewTaskFrom([self, domStream, callback]() mutable {
    1231           0 :       MOZ_ASSERT(MediaManager::IsInMediaThread());
    1232           0 :       SourceMediaStream* source = self->mSourceListener->GetSourceStream();
    1233             : 
    1234           0 :       RefPtr<MediaMgrError> error = nullptr;
    1235           0 :       if (self->mAudioDevice) {
    1236             :         nsresult rv =
    1237           0 :           self->mAudioDevice->GetSource()->Start(source, kAudioTrack,
    1238           0 :                                                  self->mSourceListener->GetPrincipalHandle());
    1239           0 :         if (NS_FAILED(rv)) {
    1240           0 :           nsString log;
    1241           0 :           log.AssignASCII("Starting audio failed");
    1242           0 :           error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log);
    1243             :         }
    1244             :       }
    1245             : 
    1246           0 :       if (!error && self->mVideoDevice) {
    1247             :         nsresult rv =
    1248           0 :           self->mVideoDevice->GetSource()->Start(source, kVideoTrack,
    1249           0 :                                                  self->mSourceListener->GetPrincipalHandle());
    1250           0 :         if (NS_FAILED(rv)) {
    1251           0 :           nsString log;
    1252           0 :           log.AssignASCII("Starting video failed");
    1253           0 :           error = new MediaMgrError(NS_LITERAL_STRING("InternalError"), log);
    1254             :         }
    1255             :       }
    1256             : 
    1257           0 :       if (error) {
    1258             :         // The DOM stream and track callback must be released on main thread.
    1259           0 :         NS_DispatchToMainThread(do_AddRef(new ReleaseMediaOperationResource(
    1260           0 :           domStream.forget(), callback.forget())));
    1261             : 
    1262             :         // Dispatch the error callback on main thread.
    1263           0 :         nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
    1264           0 :         NS_DispatchToMainThread(do_AddRef(
    1265             :           new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(
    1266           0 :             Move(onSuccess), Move(self->mOnFailure), *error, self->mWindowID)));
    1267             : 
    1268             :         // This should be empty now
    1269           0 :         MOZ_ASSERT(!self->mOnFailure);
    1270           0 :         return NS_OK;
    1271             :       }
    1272             : 
    1273             :       // Start() queued the tracks to be added synchronously to avoid races
    1274           0 :       source->FinishAddTracks();
    1275             : 
    1276           0 :       source->SetPullEnabled(true);
    1277           0 :       source->AdvanceKnownTracksTime(STREAM_TIME_MAX);
    1278             : 
    1279           0 :       LOG(("started all sources"));
    1280             : 
    1281             :       // Forward onTracksAvailableCallback to GetUserMediaNotificationEvent,
    1282             :       // because onTracksAvailableCallback needs to be added to domStream
    1283             :       // on the main thread.
    1284             :       // The event runnable must always be released on mainthread due to the JS
    1285             :       // callbacks in the TracksAvailableCallback.
    1286           0 :       NS_DispatchToMainThread(do_AddRef(
    1287             :         new GetUserMediaNotificationEvent(
    1288             :           GetUserMediaNotificationEvent::STARTING,
    1289           0 :           domStream.forget(),
    1290           0 :           callback.forget(),
    1291           0 :           self->mWindowID,
    1292           0 :           self->mOnFailure.forget())));
    1293           0 :       NS_DispatchToMainThread(NS_NewRunnableFunction("MediaManager::SendPendingGUMRequest",
    1294           0 :                                                      []() -> void {
    1295           0 :         RefPtr<MediaManager> manager = MediaManager::GetInstance();
    1296           0 :         manager->SendPendingGUMRequest();
    1297           0 :       }));
    1298           0 :       return NS_OK;
    1299           0 :     }));
    1300             : 
    1301           0 :     if (!IsPincipalInfoPrivate(mPrincipalInfo)) {
    1302             :       // Call GetPrincipalKey again, this time w/persist = true, to promote
    1303             :       // deviceIds to persistent, in case they're not already. Fire'n'forget.
    1304             :       RefPtr<Pledge<nsCString>> p =
    1305           0 :         media::GetPrincipalKey(mPrincipalInfo, true);
    1306             :     }
    1307           0 :     return NS_OK;
    1308             :   }
    1309             : 
    1310             : private:
    1311             :   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
    1312             :   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
    1313             :   MediaStreamConstraints mConstraints;
    1314             :   RefPtr<AudioDevice> mAudioDevice;
    1315             :   RefPtr<VideoDevice> mVideoDevice;
    1316             :   uint64_t mWindowID;
    1317             :   RefPtr<GetUserMediaWindowListener> mWindowListener;
    1318             :   RefPtr<SourceListener> mSourceListener;
    1319             :   ipc::PrincipalInfo mPrincipalInfo;
    1320             :   RefPtr<PeerIdentity> mPeerIdentity;
    1321             :   RefPtr<MediaManager> mManager; // get ref to this when creating the runnable
    1322             : };
    1323             : 
    1324             : // Source getter returning full list
    1325             : 
    1326             : template<class DeviceType>
    1327             : static void
    1328           0 : GetSources(MediaEngine *engine, MediaSourceEnum aSrcType,
    1329             :            void (MediaEngine::* aEnumerate)(MediaSourceEnum,
    1330             :                nsTArray<RefPtr<typename DeviceType::Source> >*),
    1331             :            nsTArray<RefPtr<DeviceType>>& aResult,
    1332             :            const char* media_device_name = nullptr)
    1333             : {
    1334           0 :   nsTArray<RefPtr<typename DeviceType::Source>> sources;
    1335             : 
    1336           0 :   (engine->*aEnumerate)(aSrcType, &sources);
    1337             :   /**
    1338             :     * We're allowing multiple tabs to access the same camera for parity
    1339             :     * with Chrome.  See bug 811757 for some of the issues surrounding
    1340             :     * this decision.  To disallow, we'd filter by IsAvailable() as we used
    1341             :     * to.
    1342             :     */
    1343           0 :   if (media_device_name && *media_device_name)  {
    1344           0 :     for (auto& source : sources) {
    1345           0 :       nsString deviceName;
    1346           0 :       source->GetName(deviceName);
    1347           0 :       if (deviceName.EqualsASCII(media_device_name)) {
    1348           0 :         aResult.AppendElement(new DeviceType(source));
    1349           0 :         break;
    1350             :       }
    1351           0 :     }
    1352             :   } else {
    1353           0 :     for (auto& source : sources) {
    1354           0 :       aResult.AppendElement(new DeviceType(source));
    1355             :     }
    1356             :   }
    1357           0 : }
    1358             : 
    1359             : // TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
    1360             : // error: 'this' was not captured for this lambda function
    1361             : 
    1362             : static auto& MediaManager_GetInstance = MediaManager::GetInstance;
    1363             : static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
    1364             : static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
    1365             : 
    1366             : already_AddRefed<MediaManager::PledgeChar>
    1367           0 : MediaManager::SelectSettings(
    1368             :     MediaStreamConstraints& aConstraints,
    1369             :     bool aIsChrome,
    1370             :     RefPtr<Refcountable<UniquePtr<SourceSet>>>& aSources)
    1371             : {
    1372           0 :   MOZ_ASSERT(NS_IsMainThread());
    1373           0 :   RefPtr<PledgeChar> p = new PledgeChar();
    1374           0 :   uint32_t id = mOutstandingCharPledges.Append(*p);
    1375             : 
    1376             :   // Algorithm accesses device capabilities code and must run on media thread.
    1377             :   // Modifies passed-in aSources.
    1378             : 
    1379           0 :   MediaManager::PostTask(NewTaskFrom([id, aConstraints,
    1380           0 :                                       aSources, aIsChrome]() mutable {
    1381           0 :     auto& sources = **aSources;
    1382             : 
    1383             :     // Since the advanced part of the constraints algorithm needs to know when
    1384             :     // a candidate set is overconstrained (zero members), we must split up the
    1385             :     // list into videos and audios, and put it back together again at the end.
    1386             : 
    1387           0 :     nsTArray<RefPtr<VideoDevice>> videos;
    1388           0 :     nsTArray<RefPtr<AudioDevice>> audios;
    1389             : 
    1390           0 :     for (auto& source : sources) {
    1391           0 :       if (source->mIsVideo) {
    1392           0 :         RefPtr<VideoDevice> video = static_cast<VideoDevice*>(source.get());
    1393           0 :         videos.AppendElement(video);
    1394             :       } else {
    1395           0 :         RefPtr<AudioDevice> audio = static_cast<AudioDevice*>(source.get());
    1396           0 :         audios.AppendElement(audio);
    1397             :       }
    1398             :     }
    1399           0 :     sources.Clear();
    1400           0 :     const char* badConstraint = nullptr;
    1401           0 :     bool needVideo = IsOn(aConstraints.mVideo);
    1402           0 :     bool needAudio = IsOn(aConstraints.mAudio);
    1403             : 
    1404           0 :     if (needVideo && videos.Length()) {
    1405           0 :       badConstraint = MediaConstraintsHelper::SelectSettings(
    1406           0 :           NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos,
    1407           0 :           aIsChrome);
    1408             :     }
    1409           0 :     if (!badConstraint && needAudio && audios.Length()) {
    1410           0 :       badConstraint = MediaConstraintsHelper::SelectSettings(
    1411           0 :           NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios,
    1412           0 :           aIsChrome);
    1413             :     }
    1414           0 :     if (!badConstraint &&
    1415           0 :         !needVideo == !videos.Length() &&
    1416           0 :         !needAudio == !audios.Length()) {
    1417           0 :       for (auto& video : videos) {
    1418           0 :         sources.AppendElement(video);
    1419             :       }
    1420           0 :       for (auto& audio : audios) {
    1421           0 :         sources.AppendElement(audio);
    1422             :       }
    1423             :     }
    1424           0 :     NS_DispatchToMainThread(NewRunnableFrom([id, badConstraint]() mutable {
    1425           0 :       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
    1426           0 :       RefPtr<PledgeChar> p = mgr->mOutstandingCharPledges.Remove(id);
    1427           0 :       if (p) {
    1428           0 :         p->Resolve(badConstraint);
    1429             :       }
    1430           0 :       return NS_OK;
    1431           0 :     }));
    1432           0 :   }));
    1433           0 :   return p.forget();
    1434             : }
    1435             : 
    1436             : /**
    1437             :  * Runs on a seperate thread and is responsible for enumerating devices.
    1438             :  * Depending on whether a picture or stream was asked for, either
    1439             :  * ProcessGetUserMedia is called, and the results are sent back to the DOM.
    1440             :  *
    1441             :  * Do not run this on the main thread. The success and error callbacks *MUST*
    1442             :  * be dispatched on the main thread!
    1443             :  */
    1444             : class GetUserMediaTask : public Runnable
    1445             : {
    1446             : public:
    1447           0 :   GetUserMediaTask(
    1448             :     const MediaStreamConstraints& aConstraints,
    1449             :     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aOnSuccess,
    1450             :     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
    1451             :     uint64_t aWindowID,
    1452             :     GetUserMediaWindowListener* aWindowListener,
    1453             :     SourceListener* aSourceListener,
    1454             :     MediaEnginePrefs& aPrefs,
    1455             :     const ipc::PrincipalInfo& aPrincipalInfo,
    1456             :     bool aIsChrome,
    1457             :     MediaManager::SourceSet* aSourceSet)
    1458           0 :     : Runnable("GetUserMediaTask")
    1459             :     , mConstraints(aConstraints)
    1460             :     , mOnSuccess(aOnSuccess)
    1461             :     , mOnFailure(aOnFailure)
    1462             :     , mWindowID(aWindowID)
    1463             :     , mWindowListener(aWindowListener)
    1464             :     , mSourceListener(aSourceListener)
    1465             :     , mPrefs(aPrefs)
    1466             :     , mPrincipalInfo(aPrincipalInfo)
    1467             :     , mIsChrome(aIsChrome)
    1468             :     , mDeviceChosen(false)
    1469             :     , mSourceSet(aSourceSet)
    1470           0 :     , mManager(MediaManager::GetInstance())
    1471           0 :   {}
    1472             : 
    1473           0 :   ~GetUserMediaTask() {
    1474           0 :   }
    1475             : 
    1476             :   void
    1477           0 :   Fail(const nsAString& aName,
    1478             :        const nsAString& aMessage = EmptyString(),
    1479           0 :        const nsAString& aConstraint = EmptyString()) {
    1480           0 :     RefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage, aConstraint);
    1481             :     auto errorRunnable = MakeRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>>(
    1482           0 :         Move(mOnSuccess), Move(mOnFailure), *error, mWindowID);
    1483             :     // These should be empty now
    1484           0 :     MOZ_ASSERT(!mOnSuccess);
    1485           0 :     MOZ_ASSERT(!mOnFailure);
    1486             : 
    1487           0 :     NS_DispatchToMainThread(errorRunnable.forget());
    1488             :     // Do after ErrorCallbackRunnable Run()s, as it checks active window list
    1489           0 :     NS_DispatchToMainThread(NewRunnableMethod<RefPtr<SourceListener>>(
    1490             :       "GetUserMediaWindowListener::Remove",
    1491             :       mWindowListener,
    1492             :       &GetUserMediaWindowListener::Remove,
    1493           0 :       mSourceListener));
    1494           0 :   }
    1495             : 
    1496             :   NS_IMETHOD
    1497           0 :   Run() override
    1498             :   {
    1499           0 :     MOZ_ASSERT(!NS_IsMainThread());
    1500           0 :     MOZ_ASSERT(mOnSuccess);
    1501           0 :     MOZ_ASSERT(mOnFailure);
    1502           0 :     MOZ_ASSERT(mDeviceChosen);
    1503             : 
    1504             :     // Allocate a video or audio device and return a MediaStream via
    1505             :     // a GetUserMediaStreamRunnable.
    1506             : 
    1507             :     nsresult rv;
    1508           0 :     const char* errorMsg = nullptr;
    1509           0 :     const char* badConstraint = nullptr;
    1510             : 
    1511           0 :     if (mAudioDevice) {
    1512           0 :       auto& constraints = GetInvariant(mConstraints.mAudio);
    1513           0 :       rv = mAudioDevice->Allocate(constraints, mPrefs, mPrincipalInfo,
    1514           0 :                                   &badConstraint);
    1515           0 :       if (NS_FAILED(rv)) {
    1516           0 :         errorMsg = "Failed to allocate audiosource";
    1517           0 :         if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
    1518           0 :           nsTArray<RefPtr<AudioDevice>> audios;
    1519           0 :           audios.AppendElement(mAudioDevice);
    1520           0 :           badConstraint = MediaConstraintsHelper::SelectSettings(
    1521           0 :               NormalizedConstraints(constraints), audios, mIsChrome);
    1522             :         }
    1523             :       }
    1524             :     }
    1525           0 :     if (!errorMsg && mVideoDevice) {
    1526           0 :       auto& constraints = GetInvariant(mConstraints.mVideo);
    1527           0 :       rv = mVideoDevice->Allocate(constraints, mPrefs, mPrincipalInfo,
    1528           0 :                                   &badConstraint);
    1529           0 :       if (NS_FAILED(rv)) {
    1530           0 :         errorMsg = "Failed to allocate videosource";
    1531           0 :         if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
    1532           0 :           nsTArray<RefPtr<VideoDevice>> videos;
    1533           0 :           videos.AppendElement(mVideoDevice);
    1534           0 :           badConstraint = MediaConstraintsHelper::SelectSettings(
    1535           0 :               NormalizedConstraints(constraints), videos, mIsChrome);
    1536             :         }
    1537           0 :         if (mAudioDevice) {
    1538           0 :           mAudioDevice->Deallocate();
    1539             :         }
    1540             :       }
    1541             :     }
    1542           0 :     if (errorMsg) {
    1543           0 :       LOG(("%s %" PRIu32, errorMsg, static_cast<uint32_t>(rv)));
    1544           0 :       if (badConstraint) {
    1545           0 :         Fail(NS_LITERAL_STRING("OverconstrainedError"),
    1546           0 :              NS_LITERAL_STRING(""),
    1547           0 :              NS_ConvertUTF8toUTF16(badConstraint));
    1548             :       } else {
    1549           0 :         Fail(NS_LITERAL_STRING("NotReadableError"),
    1550           0 :              NS_ConvertUTF8toUTF16(errorMsg));
    1551             :       }
    1552           0 :       NS_DispatchToMainThread(NS_NewRunnableFunction("MediaManager::SendPendingGUMRequest",
    1553           0 :                                                      []() -> void {
    1554           0 :         RefPtr<MediaManager> manager = MediaManager::GetInstance();
    1555           0 :         manager->SendPendingGUMRequest();
    1556           0 :       }));
    1557           0 :       return NS_OK;
    1558             :     }
    1559           0 :     PeerIdentity* peerIdentity = nullptr;
    1560           0 :     if (!mConstraints.mPeerIdentity.IsEmpty()) {
    1561           0 :       peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
    1562             :     }
    1563             : 
    1564           0 :     NS_DispatchToMainThread(do_AddRef(
    1565             :         new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID,
    1566             :                                        mWindowListener, mSourceListener,
    1567             :                                        mPrincipalInfo, mConstraints,
    1568             :                                        mAudioDevice, mVideoDevice,
    1569           0 :                                        peerIdentity)));
    1570           0 :     MOZ_ASSERT(!mOnSuccess);
    1571           0 :     MOZ_ASSERT(!mOnFailure);
    1572           0 :     return NS_OK;
    1573             :   }
    1574             : 
    1575             :   nsresult
    1576           0 :   Denied(const nsAString& aName,
    1577           0 :          const nsAString& aMessage = EmptyString())
    1578             :   {
    1579           0 :     MOZ_ASSERT(mOnSuccess);
    1580           0 :     MOZ_ASSERT(mOnFailure);
    1581             : 
    1582             :     // We add a disabled listener to the StreamListeners array until accepted
    1583             :     // If this was the only active MediaStream, remove the window from the list.
    1584           0 :     if (NS_IsMainThread()) {
    1585             :       // This is safe since we're on main-thread, and the window can only
    1586             :       // be invalidated from the main-thread (see OnNavigation)
    1587           0 :       nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess = mOnSuccess.forget();
    1588           0 :       nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
    1589             : 
    1590           0 :       if (auto* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID)) {
    1591           0 :         RefPtr<MediaStreamError> error = new MediaStreamError(window->AsInner(),
    1592           0 :                                                               aName, aMessage);
    1593           0 :         onFailure->OnError(error);
    1594             :       }
    1595             :       // Should happen *after* error runs for consistency, but may not matter
    1596           0 :       mWindowListener->Remove(mSourceListener);
    1597             :     } else {
    1598             :       // This will re-check the window being alive on main-thread
    1599             :       // and remove the listener on MainThread as well
    1600           0 :       Fail(aName, aMessage);
    1601             :     }
    1602             : 
    1603           0 :     MOZ_ASSERT(!mOnSuccess);
    1604           0 :     MOZ_ASSERT(!mOnFailure);
    1605             : 
    1606           0 :     return NS_OK;
    1607             :   }
    1608             : 
    1609             :   nsresult
    1610             :   SetContraints(const MediaStreamConstraints& aConstraints)
    1611             :   {
    1612             :     mConstraints = aConstraints;
    1613             :     return NS_OK;
    1614             :   }
    1615             : 
    1616             :   const MediaStreamConstraints&
    1617           0 :   GetConstraints()
    1618             :   {
    1619           0 :     return mConstraints;
    1620             :   }
    1621             : 
    1622             :   nsresult
    1623           0 :   SetAudioDevice(AudioDevice* aAudioDevice)
    1624             :   {
    1625           0 :     mAudioDevice = aAudioDevice;
    1626           0 :     mDeviceChosen = true;
    1627           0 :     return NS_OK;
    1628             :   }
    1629             : 
    1630             :   nsresult
    1631           0 :   SetVideoDevice(VideoDevice* aVideoDevice)
    1632             :   {
    1633           0 :     mVideoDevice = aVideoDevice;
    1634           0 :     mDeviceChosen = true;
    1635           0 :     return NS_OK;
    1636             :   }
    1637             : 
    1638             :   uint64_t
    1639           0 :   GetWindowID()
    1640             :   {
    1641           0 :     return mWindowID;
    1642             :   }
    1643             : 
    1644             : private:
    1645             :   MediaStreamConstraints mConstraints;
    1646             : 
    1647             :   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
    1648             :   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
    1649             :   uint64_t mWindowID;
    1650             :   RefPtr<GetUserMediaWindowListener> mWindowListener;
    1651             :   RefPtr<SourceListener> mSourceListener;
    1652             :   RefPtr<AudioDevice> mAudioDevice;
    1653             :   RefPtr<VideoDevice> mVideoDevice;
    1654             :   MediaEnginePrefs mPrefs;
    1655             :   ipc::PrincipalInfo mPrincipalInfo;
    1656             :   bool mIsChrome;
    1657             : 
    1658             :   bool mDeviceChosen;
    1659             : public:
    1660             :   nsAutoPtr<MediaManager::SourceSet> mSourceSet;
    1661             : private:
    1662             :   RefPtr<MediaManager> mManager; // get ref to this when creating the runnable
    1663             : };
    1664             : 
    1665             : #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
    1666             : class GetUserMediaRunnableWrapper : public Runnable
    1667             : {
    1668             : public:
    1669             :   // This object must take ownership of task
    1670             :   GetUserMediaRunnableWrapper(GetUserMediaTask* task)
    1671             :     : Runnable("GetUserMediaRunnableWrapper")
    1672             :     , mTask(task) {
    1673             :   }
    1674             : 
    1675             :   ~GetUserMediaRunnableWrapper() {
    1676             :   }
    1677             : 
    1678             :   NS_IMETHOD Run() override {
    1679             :     mTask->Run();
    1680             :     return NS_OK;
    1681             :   }
    1682             : 
    1683             : private:
    1684             :   nsAutoPtr<GetUserMediaTask> mTask;
    1685             : };
    1686             : #endif
    1687             : 
    1688             : /**
    1689             :  * EnumerateRawDevices - Enumerate a list of audio & video devices that
    1690             :  * satisfy passed-in constraints. List contains raw id's.
    1691             :  */
    1692             : 
    1693             : already_AddRefed<MediaManager::PledgeSourceSet>
    1694           0 : MediaManager::EnumerateRawDevices(uint64_t aWindowId,
    1695             :                                   MediaSourceEnum aVideoType,
    1696             :                                   MediaSourceEnum aAudioType,
    1697             :                                   bool aFake)
    1698             : {
    1699           0 :   MOZ_ASSERT(NS_IsMainThread());
    1700           0 :   MOZ_ASSERT(aVideoType != MediaSourceEnum::Other ||
    1701             :              aAudioType != MediaSourceEnum::Other);
    1702           0 :   RefPtr<PledgeSourceSet> p = new PledgeSourceSet();
    1703           0 :   uint32_t id = mOutstandingPledges.Append(*p);
    1704             : 
    1705           0 :   nsAdoptingCString audioLoopDev, videoLoopDev;
    1706           0 :   if (!aFake) {
    1707             :     // Fake stream not requested. The entire device stack is available.
    1708             :     // Loop in loopback devices if they are set, and their respective type is
    1709             :     // requested. This is currently used for automated media tests only.
    1710           0 :     if (aVideoType == MediaSourceEnum::Camera) {
    1711           0 :       videoLoopDev = Preferences::GetCString("media.video_loopback_dev");
    1712             :     }
    1713           0 :     if (aAudioType == MediaSourceEnum::Microphone) {
    1714           0 :       audioLoopDev = Preferences::GetCString("media.audio_loopback_dev");
    1715             :     }
    1716             :   }
    1717             : 
    1718           0 :   RefPtr<Runnable> task = NewTaskFrom([id, aWindowId, audioLoopDev,
    1719             :                                        videoLoopDev, aVideoType,
    1720           0 :                                        aAudioType, aFake]() mutable {
    1721             :     // Only enumerate what's asked for, and only fake cams and mics.
    1722           0 :     bool hasVideo = aVideoType != MediaSourceEnum::Other;
    1723           0 :     bool hasAudio = aAudioType != MediaSourceEnum::Other;
    1724           0 :     bool fakeCams = aFake && aVideoType == MediaSourceEnum::Camera;
    1725           0 :     bool fakeMics = aFake && aAudioType == MediaSourceEnum::Microphone;
    1726             : 
    1727           0 :     RefPtr<MediaEngine> fakeBackend, realBackend;
    1728           0 :     if (fakeCams || fakeMics) {
    1729           0 :       fakeBackend = new MediaEngineDefault();
    1730             :     }
    1731           0 :     if ((!fakeCams && hasVideo) || (!fakeMics && hasAudio)) {
    1732           0 :       RefPtr<MediaManager> manager = MediaManager_GetInstance();
    1733           0 :       realBackend = manager->GetBackend(aWindowId);
    1734             :     }
    1735             : 
    1736           0 :     auto result = MakeUnique<SourceSet>();
    1737             : 
    1738           0 :     if (hasVideo) {
    1739           0 :       nsTArray<RefPtr<VideoDevice>> videos;
    1740           0 :       GetSources(fakeCams? fakeBackend : realBackend, aVideoType,
    1741           0 :                  &MediaEngine::EnumerateVideoDevices, videos, videoLoopDev);
    1742           0 :       for (auto& source : videos) {
    1743           0 :         result->AppendElement(source);
    1744             :       }
    1745             :     }
    1746           0 :     if (hasAudio) {
    1747           0 :       nsTArray<RefPtr<AudioDevice>> audios;
    1748           0 :       GetSources(fakeMics? fakeBackend : realBackend, aAudioType,
    1749           0 :                  &MediaEngine::EnumerateAudioDevices, audios, audioLoopDev);
    1750           0 :       for (auto& source : audios) {
    1751           0 :         result->AppendElement(source);
    1752             :       }
    1753             :     }
    1754           0 :     SourceSet* handoff = result.release();
    1755           0 :     NS_DispatchToMainThread(NewRunnableFrom([id, handoff]() mutable {
    1756           0 :       UniquePtr<SourceSet> result(handoff); // grab result
    1757           0 :       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
    1758           0 :       if (!mgr) {
    1759           0 :         return NS_OK;
    1760             :       }
    1761           0 :       RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
    1762           0 :       if (p) {
    1763           0 :         p->Resolve(result.release());
    1764             :       }
    1765           0 :       return NS_OK;
    1766           0 :     }));
    1767           0 :   });
    1768             : 
    1769           0 :   if (!aFake &&
    1770           0 :       (aVideoType == MediaSourceEnum::Camera ||
    1771           0 :        aAudioType == MediaSourceEnum::Microphone) &&
    1772           0 :       Preferences::GetBool("media.navigator.permission.device", false)) {
    1773             :     // Need to ask permission to retrieve list of all devices;
    1774             :     // notify frontend observer and wait for callback notification to post task.
    1775             :     const char16_t* const type =
    1776           0 :       (aVideoType != MediaSourceEnum::Camera)     ? u"audio" :
    1777           0 :       (aAudioType != MediaSourceEnum::Microphone) ? u"video" :
    1778           0 :                                                     u"all";
    1779           0 :     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    1780           0 :     obs->NotifyObservers(static_cast<nsIRunnable*>(task),
    1781             :                          "getUserMedia:ask-device-permission",
    1782           0 :                          type);
    1783             :   } else {
    1784             :     // Don't need to ask permission to retrieve list of all devices;
    1785             :     // post the retrieval task immediately.
    1786           0 :     MediaManager::PostTask(task.forget());
    1787             :   }
    1788             : 
    1789           0 :   return p.forget();
    1790             : }
    1791             : 
    1792           0 : MediaManager::MediaManager()
    1793             :   : mMediaThread(nullptr)
    1794           0 :   , mBackend(nullptr) {
    1795           0 :   mPrefs.mFreq         = 1000; // 1KHz test tone
    1796           0 :   mPrefs.mWidth        = 0; // adaptive default
    1797           0 :   mPrefs.mHeight       = 0; // adaptive default
    1798           0 :   mPrefs.mFPS          = MediaEngine::DEFAULT_VIDEO_FPS;
    1799           0 :   mPrefs.mMinFPS       = MediaEngine::DEFAULT_VIDEO_MIN_FPS;
    1800           0 :   mPrefs.mAecOn        = false;
    1801           0 :   mPrefs.mAgcOn        = false;
    1802           0 :   mPrefs.mNoiseOn      = false;
    1803           0 :   mPrefs.mExtendedFilter = true;
    1804           0 :   mPrefs.mDelayAgnostic = true;
    1805           0 :   mPrefs.mFakeDeviceChangeEventOn = false;
    1806             : #ifdef MOZ_WEBRTC
    1807           0 :   mPrefs.mAec          = webrtc::kEcUnchanged;
    1808           0 :   mPrefs.mAgc          = webrtc::kAgcUnchanged;
    1809           0 :   mPrefs.mNoise        = webrtc::kNsUnchanged;
    1810             : #else
    1811             :   mPrefs.mAec          = 0;
    1812             :   mPrefs.mAgc          = 0;
    1813             :   mPrefs.mNoise        = 0;
    1814             : #endif
    1815           0 :   mPrefs.mPlayoutDelay = 0;
    1816           0 :   mPrefs.mFullDuplex = false;
    1817           0 :   mPrefs.mChannels     = 0; // max channels default
    1818             :   nsresult rv;
    1819           0 :   nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
    1820           0 :   if (NS_SUCCEEDED(rv)) {
    1821           0 :     nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
    1822           0 :     if (branch) {
    1823           0 :       GetPrefs(branch, nullptr);
    1824             :     }
    1825             :   }
    1826           0 :   LOG(("%s: default prefs: %dx%d @%dfps (min %d), %dHz test tones, aec: %s,"
    1827             :        "agc: %s, noise: %s, aec level: %d, agc level: %d, noise level: %d,"
    1828             :        "playout delay: %d, %sfull_duplex, extended aec %s, delay_agnostic %s "
    1829             :        "channels %d",
    1830             :        __FUNCTION__, mPrefs.mWidth, mPrefs.mHeight,
    1831             :        mPrefs.mFPS, mPrefs.mMinFPS, mPrefs.mFreq, mPrefs.mAecOn ? "on" : "off",
    1832             :        mPrefs.mAgcOn ? "on": "off", mPrefs.mNoiseOn ? "on": "off", mPrefs.mAec,
    1833             :        mPrefs.mAgc, mPrefs.mNoise, mPrefs.mPlayoutDelay, mPrefs.mFullDuplex ? "" : "not ",
    1834             :        mPrefs.mExtendedFilter ? "on" : "off", mPrefs.mDelayAgnostic ? "on" : "off",
    1835             :        mPrefs.mChannels));
    1836           0 : }
    1837             : 
    1838           0 : NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
    1839             : 
    1840           3 : /* static */ StaticRefPtr<MediaManager> MediaManager::sSingleton;
    1841             : 
    1842             : #ifdef DEBUG
    1843             : /* static */ bool
    1844           0 : MediaManager::IsInMediaThread()
    1845             : {
    1846           0 :   return sSingleton?
    1847           0 :       (sSingleton->mMediaThread->thread_id() == PlatformThread::CurrentId()) :
    1848           0 :       false;
    1849             : }
    1850             : #endif
    1851             : 
    1852             : // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
    1853             : // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
    1854             : // from MediaManager thread.
    1855             : 
    1856             : // Guaranteed never to return nullptr.
    1857             : /* static */  MediaManager*
    1858           0 : MediaManager::Get() {
    1859           0 :   if (!sSingleton) {
    1860           0 :     MOZ_ASSERT(NS_IsMainThread());
    1861             : 
    1862             :     static int timesCreated = 0;
    1863           0 :     timesCreated++;
    1864           0 :     MOZ_RELEASE_ASSERT(timesCreated == 1);
    1865             : 
    1866           0 :     sSingleton = new MediaManager();
    1867             : 
    1868           0 :     sSingleton->mMediaThread = new base::Thread("MediaManager");
    1869           0 :     base::Thread::Options options;
    1870             : #if defined(_WIN32)
    1871             :     options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD;
    1872             : #else
    1873           0 :     options.message_loop_type = MessageLoop::TYPE_MOZILLA_NONMAINTHREAD;
    1874             : #endif
    1875           0 :     if (!sSingleton->mMediaThread->StartWithOptions(options)) {
    1876           0 :       MOZ_CRASH();
    1877             :     }
    1878             : 
    1879           0 :     LOG(("New Media thread for gum"));
    1880             : 
    1881           0 :     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    1882           0 :     if (obs) {
    1883           0 :       obs->AddObserver(sSingleton, "last-pb-context-exited", false);
    1884           0 :       obs->AddObserver(sSingleton, "getUserMedia:got-device-permission", false);
    1885           0 :       obs->AddObserver(sSingleton, "getUserMedia:privileged:allow", false);
    1886           0 :       obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
    1887           0 :       obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
    1888           0 :       obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
    1889           0 :       obs->AddObserver(sSingleton, "phone-state-changed", false);
    1890             :     }
    1891             :     // else MediaManager won't work properly and will leak (see bug 837874)
    1892           0 :     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
    1893           0 :     if (prefs) {
    1894           0 :       prefs->AddObserver("media.navigator.video.default_width", sSingleton, false);
    1895           0 :       prefs->AddObserver("media.navigator.video.default_height", sSingleton, false);
    1896           0 :       prefs->AddObserver("media.navigator.video.default_fps", sSingleton, false);
    1897           0 :       prefs->AddObserver("media.navigator.video.default_minfps", sSingleton, false);
    1898           0 :       prefs->AddObserver("media.navigator.audio.fake_frequency", sSingleton, false);
    1899           0 :       prefs->AddObserver("media.navigator.audio.full_duplex", sSingleton, false);
    1900             : #ifdef MOZ_WEBRTC
    1901           0 :       prefs->AddObserver("media.getusermedia.aec_enabled", sSingleton, false);
    1902           0 :       prefs->AddObserver("media.getusermedia.aec", sSingleton, false);
    1903           0 :       prefs->AddObserver("media.getusermedia.agc_enabled", sSingleton, false);
    1904           0 :       prefs->AddObserver("media.getusermedia.agc", sSingleton, false);
    1905           0 :       prefs->AddObserver("media.getusermedia.noise_enabled", sSingleton, false);
    1906           0 :       prefs->AddObserver("media.getusermedia.noise", sSingleton, false);
    1907           0 :       prefs->AddObserver("media.getusermedia.playout_delay", sSingleton, false);
    1908           0 :       prefs->AddObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", sSingleton, false);
    1909           0 :       prefs->AddObserver("media.getusermedia.channels", sSingleton, false);
    1910             : #endif
    1911             :     }
    1912             : 
    1913             :     // Prepare async shutdown
    1914             : 
    1915           0 :     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
    1916             : 
    1917           0 :     class Blocker : public media::ShutdownBlocker
    1918             :     {
    1919             :     public:
    1920           0 :       Blocker()
    1921           0 :       : media::ShutdownBlocker(NS_LITERAL_STRING(
    1922           0 :           "Media shutdown: blocking on media thread")) {}
    1923             : 
    1924           0 :       NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient*) override
    1925             :       {
    1926           0 :         MOZ_RELEASE_ASSERT(MediaManager::GetIfExists());
    1927           0 :         MediaManager::GetIfExists()->Shutdown();
    1928           0 :         return NS_OK;
    1929             :       }
    1930             :     };
    1931             : 
    1932           0 :     sSingleton->mShutdownBlocker = new Blocker();
    1933           0 :     nsresult rv = shutdownPhase->AddBlocker(sSingleton->mShutdownBlocker,
    1934           0 :                                             NS_LITERAL_STRING(__FILE__),
    1935             :                                             __LINE__,
    1936           0 :                                             NS_LITERAL_STRING("Media shutdown"));
    1937           0 :     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
    1938             : #ifdef MOZ_B2G
    1939             :     // Init MediaPermissionManager before sending out any permission requests.
    1940             :     (void) MediaPermissionManager::GetInstance();
    1941             : #endif //MOZ_B2G
    1942             :   }
    1943           0 :   return sSingleton;
    1944             : }
    1945             : 
    1946             : /* static */  MediaManager*
    1947           0 : MediaManager::GetIfExists() {
    1948           0 :   return sSingleton;
    1949             : }
    1950             : 
    1951             : /* static */ already_AddRefed<MediaManager>
    1952           0 : MediaManager::GetInstance()
    1953             : {
    1954             :   // so we can have non-refcounted getters
    1955           0 :   RefPtr<MediaManager> service = MediaManager::Get();
    1956           0 :   return service.forget();
    1957             : }
    1958             : 
    1959             : media::Parent<media::NonE10s>*
    1960           0 : MediaManager::GetNonE10sParent()
    1961             : {
    1962           0 :   if (!mNonE10sParent) {
    1963           0 :     mNonE10sParent = new media::Parent<media::NonE10s>();
    1964             :   }
    1965           0 :   return mNonE10sParent;
    1966             : }
    1967             : 
    1968             : /* static */ void
    1969           3 : MediaManager::StartupInit()
    1970             : {
    1971             : #ifdef WIN32
    1972             :   if (!IsWin8OrLater()) {
    1973             :     // Bug 1107702 - Older Windows fail in GetAdaptersInfo (and others) if the
    1974             :     // first(?) call occurs after the process size is over 2GB (kb/2588507).
    1975             :     // Attempt to 'prime' the pump by making a call at startup.
    1976             :     unsigned long out_buf_len = sizeof(IP_ADAPTER_INFO);
    1977             :     PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *) moz_xmalloc(out_buf_len);
    1978             :     if (GetAdaptersInfo(pAdapterInfo, &out_buf_len) == ERROR_BUFFER_OVERFLOW) {
    1979             :       free(pAdapterInfo);
    1980             :       pAdapterInfo = (IP_ADAPTER_INFO *) moz_xmalloc(out_buf_len);
    1981             :       GetAdaptersInfo(pAdapterInfo, &out_buf_len);
    1982             :     }
    1983             :     if (pAdapterInfo) {
    1984             :       free(pAdapterInfo);
    1985             :     }
    1986             :   }
    1987             : #endif
    1988           3 : }
    1989             : 
    1990             : /* static */
    1991             : void
    1992           0 : MediaManager::PostTask(already_AddRefed<Runnable> task)
    1993             : {
    1994           0 :   if (sInShutdown) {
    1995             :     // Can't safely delete task here since it may have items with specific
    1996             :     // thread-release requirements.
    1997             :     // XXXkhuey well then who is supposed to delete it?! We don't signal
    1998             :     // that we failed ...
    1999           0 :     MOZ_CRASH();
    2000             :     return;
    2001             :   }
    2002           0 :   NS_ASSERTION(Get(), "MediaManager singleton?");
    2003           0 :   NS_ASSERTION(Get()->mMediaThread, "No thread yet");
    2004           0 :   Get()->mMediaThread->message_loop()->PostTask(Move(task));
    2005           0 : }
    2006             : 
    2007             : /* static */ nsresult
    2008           0 : MediaManager::NotifyRecordingStatusChange(nsPIDOMWindowInner* aWindow,
    2009             :                                           const nsString& aMsg)
    2010             : {
    2011           0 :   NS_ENSURE_ARG(aWindow);
    2012             : 
    2013           0 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    2014           0 :   if (!obs) {
    2015           0 :     NS_WARNING("Could not get the Observer service for GetUserMedia recording notification.");
    2016           0 :     return NS_ERROR_FAILURE;
    2017             :   }
    2018             : 
    2019           0 :   RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
    2020             : 
    2021           0 :   nsCString pageURL;
    2022           0 :   nsCOMPtr<nsIURI> docURI = aWindow->GetDocumentURI();
    2023           0 :   NS_ENSURE_TRUE(docURI, NS_ERROR_FAILURE);
    2024             : 
    2025           0 :   nsresult rv = docURI->GetSpec(pageURL);
    2026           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2027             : 
    2028           0 :   NS_ConvertUTF8toUTF16 requestURL(pageURL);
    2029             : 
    2030           0 :   props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL);
    2031             : 
    2032           0 :   obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
    2033             :                        "recording-device-events",
    2034           0 :                        aMsg.get());
    2035             : 
    2036           0 :   return NS_OK;
    2037             : }
    2038             : 
    2039           0 : int MediaManager::AddDeviceChangeCallback(DeviceChangeCallback* aCallback)
    2040             : {
    2041           0 :   bool fakeDeviceChangeEventOn = mPrefs.mFakeDeviceChangeEventOn;
    2042           0 :   MediaManager::PostTask(NewTaskFrom([fakeDeviceChangeEventOn]() {
    2043           0 :     RefPtr<MediaManager> manager = MediaManager_GetInstance();
    2044           0 :     manager->GetBackend(0)->AddDeviceChangeCallback(manager);
    2045           0 :     if (fakeDeviceChangeEventOn)
    2046           0 :       manager->GetBackend(0)->SetFakeDeviceChangeEvents();
    2047           0 :   }));
    2048             : 
    2049           0 :   return DeviceChangeCallback::AddDeviceChangeCallback(aCallback);
    2050             : }
    2051             : 
    2052           0 : void MediaManager::OnDeviceChange() {
    2053           0 :   RefPtr<MediaManager> self(this);
    2054           0 :   NS_DispatchToMainThread(media::NewRunnableFrom([self,this]() mutable {
    2055           0 :     MOZ_ASSERT(NS_IsMainThread());
    2056           0 :     DeviceChangeCallback::OnDeviceChange();
    2057           0 :     return NS_OK;
    2058           0 :   }));
    2059           0 : }
    2060             : 
    2061           0 : nsresult MediaManager::GenerateUUID(nsAString& aResult)
    2062             : {
    2063             :   nsresult rv;
    2064             :   nsCOMPtr<nsIUUIDGenerator> uuidgen =
    2065           0 :       do_GetService("@mozilla.org/uuid-generator;1", &rv);
    2066           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2067             : 
    2068             :   // Generate a call ID.
    2069             :   nsID id;
    2070           0 :   rv = uuidgen->GenerateUUIDInPlace(&id);
    2071           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2072             : 
    2073             :   char buffer[NSID_LENGTH];
    2074           0 :   id.ToProvidedString(buffer);
    2075           0 :   aResult.Assign(NS_ConvertUTF8toUTF16(buffer));
    2076           0 :   return NS_OK;
    2077             : }
    2078             : 
    2079             : enum class GetUserMediaSecurityState {
    2080             :   Other = 0,
    2081             :   HTTPS = 1,
    2082             :   File = 2,
    2083             :   App = 3,
    2084             :   Localhost = 4,
    2085             :   Loop = 5,
    2086             :   Privileged = 6
    2087             : };
    2088             : 
    2089             : /**
    2090             :  * The entry point for this file. A call from Navigator::mozGetUserMedia
    2091             :  * will end up here. MediaManager is a singleton that is responsible
    2092             :  * for handling all incoming getUserMedia calls from every window.
    2093             :  */
    2094             : nsresult
    2095           0 : MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
    2096             :                            const MediaStreamConstraints& aConstraintsPassedIn,
    2097             :                            nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
    2098             :                            nsIDOMGetUserMediaErrorCallback* aOnFailure,
    2099             :                            dom::CallerType aCallerType)
    2100             : {
    2101           0 :   MOZ_ASSERT(NS_IsMainThread());
    2102           0 :   MOZ_ASSERT(aWindow);
    2103           0 :   MOZ_ASSERT(aOnFailure);
    2104           0 :   MOZ_ASSERT(aOnSuccess);
    2105           0 :   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess(aOnSuccess);
    2106           0 :   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
    2107           0 :   uint64_t windowID = aWindow->WindowID();
    2108             : 
    2109           0 :   MediaStreamConstraints c(aConstraintsPassedIn); // use a modifiable copy
    2110             : 
    2111             :   // Do all the validation we can while we're sync (to return an
    2112             :   // already-rejected promise on failure).
    2113             : 
    2114           0 :   if (!IsOn(c.mVideo) && !IsOn(c.mAudio)) {
    2115             :     RefPtr<MediaStreamError> error =
    2116             :         new MediaStreamError(aWindow,
    2117           0 :                              NS_LITERAL_STRING("TypeError"),
    2118           0 :                              NS_LITERAL_STRING("audio and/or video is required"));
    2119           0 :     onFailure->OnError(error);
    2120           0 :     return NS_OK;
    2121             :   }
    2122           0 :   if (sInShutdown) {
    2123             :     RefPtr<MediaStreamError> error =
    2124             :         new MediaStreamError(aWindow,
    2125           0 :                              NS_LITERAL_STRING("AbortError"),
    2126           0 :                              NS_LITERAL_STRING("In shutdown"));
    2127           0 :     onFailure->OnError(error);
    2128           0 :     return NS_OK;
    2129             :   }
    2130             : 
    2131             :   // Determine permissions early (while we still have a stack).
    2132             : 
    2133           0 :   nsIURI* docURI = aWindow->GetDocumentURI();
    2134           0 :   if (!docURI) {
    2135           0 :     return NS_ERROR_UNEXPECTED;
    2136             :   }
    2137           0 :   bool isChrome = (aCallerType == dom::CallerType::System);
    2138           0 :   bool privileged = isChrome ||
    2139           0 :       Preferences::GetBool("media.navigator.permission.disabled", false);
    2140           0 :   bool isHTTPS = false;
    2141           0 :   docURI->SchemeIs("https", &isHTTPS);
    2142           0 :   nsCString host;
    2143           0 :   nsresult rv = docURI->GetHost(host);
    2144             :   // Test for some other schemes that ServiceWorker recognizes
    2145             :   bool isFile;
    2146           0 :   docURI->SchemeIs("file", &isFile);
    2147             :   bool isApp;
    2148           0 :   docURI->SchemeIs("app", &isApp);
    2149             :   // Same localhost check as ServiceWorkers uses
    2150             :   // (see IsOriginPotentiallyTrustworthy())
    2151           0 :   bool isLocalhost = NS_SUCCEEDED(rv) &&
    2152           0 :                      (host.LowerCaseEqualsLiteral("localhost") ||
    2153           0 :                       host.LowerCaseEqualsLiteral("127.0.0.1") ||
    2154           0 :                       host.LowerCaseEqualsLiteral("::1"));
    2155             : 
    2156             :   // Record telemetry about whether the source of the call was secure, i.e.,
    2157             :   // privileged or HTTPS.  We may handle other cases
    2158           0 :   if (privileged) {
    2159             :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN,
    2160           0 :                           (uint32_t) GetUserMediaSecurityState::Privileged);
    2161           0 :   } else if (isHTTPS) {
    2162             :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN,
    2163           0 :                           (uint32_t) GetUserMediaSecurityState::HTTPS);
    2164           0 :   } else if (isFile) {
    2165             :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN,
    2166           0 :                           (uint32_t) GetUserMediaSecurityState::File);
    2167           0 :   } else if (isApp) {
    2168             :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN,
    2169           0 :                           (uint32_t) GetUserMediaSecurityState::App);
    2170           0 :   } else if (isLocalhost) {
    2171             :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN,
    2172           0 :                           (uint32_t) GetUserMediaSecurityState::Localhost);
    2173             :   } else {
    2174             :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_SECURE_ORIGIN,
    2175           0 :                           (uint32_t) GetUserMediaSecurityState::Other);
    2176             :   }
    2177             : 
    2178             :   nsCOMPtr<nsIPrincipal> principal =
    2179           0 :     nsGlobalWindow::Cast(aWindow)->GetPrincipal();
    2180           0 :   if (NS_WARN_IF(!principal)) {
    2181           0 :     return NS_ERROR_FAILURE;
    2182             :   }
    2183             : 
    2184             :   // This principal needs to be sent to different threads and so via IPC.
    2185             :   // For this reason it's better to convert it to PrincipalInfo right now.
    2186           0 :   ipc::PrincipalInfo principalInfo;
    2187           0 :   rv = PrincipalToPrincipalInfo(principal, &principalInfo);
    2188           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2189           0 :     return rv;
    2190             :   }
    2191             : 
    2192           0 :   if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
    2193           0 :     c.mVideo.SetAsBoolean() = false;
    2194             :   }
    2195             : 
    2196           0 :   MediaSourceEnum videoType = MediaSourceEnum::Other; // none
    2197           0 :   MediaSourceEnum audioType = MediaSourceEnum::Other; // none
    2198             : 
    2199           0 :   if (c.mVideo.IsMediaTrackConstraints()) {
    2200           0 :     auto& vc = c.mVideo.GetAsMediaTrackConstraints();
    2201           0 :     videoType = StringToEnum(dom::MediaSourceEnumValues::strings,
    2202             :                              vc.mMediaSource,
    2203           0 :                              MediaSourceEnum::Other);
    2204           0 :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_TYPE,
    2205           0 :                           (uint32_t) videoType);
    2206           0 :     switch (videoType) {
    2207             :       case MediaSourceEnum::Camera:
    2208           0 :         break;
    2209             : 
    2210             :       case MediaSourceEnum::Browser:
    2211             :         // If no window id is passed in then default to the caller's window.
    2212             :         // Functional defaults are helpful in tests, but also a natural outcome
    2213             :         // of the constraints API's limited semantics for requiring input.
    2214           0 :         if (!vc.mBrowserWindow.WasPassed()) {
    2215           0 :           nsPIDOMWindowOuter* outer = aWindow->GetOuterWindow();
    2216           0 :           vc.mBrowserWindow.Construct(outer->WindowID());
    2217             :         }
    2218             :         MOZ_FALLTHROUGH;
    2219             :       case MediaSourceEnum::Screen:
    2220             :       case MediaSourceEnum::Application:
    2221             :       case MediaSourceEnum::Window:
    2222             :         // Deny screensharing request if support is disabled, or
    2223             :         // the requesting document is not from a host on the whitelist.
    2224           0 :         if (!Preferences::GetBool(((videoType == MediaSourceEnum::Browser)?
    2225             :                                    "media.getusermedia.browser.enabled" :
    2226             :                                    "media.getusermedia.screensharing.enabled"),
    2227           0 :                                   false) ||
    2228           0 :             (!privileged && !HostIsHttps(*docURI))) {
    2229             :           RefPtr<MediaStreamError> error =
    2230             :               new MediaStreamError(aWindow,
    2231           0 :                                    NS_LITERAL_STRING("NotAllowedError"));
    2232           0 :           onFailure->OnError(error);
    2233           0 :           return NS_OK;
    2234             :         }
    2235           0 :         break;
    2236             : 
    2237             :       case MediaSourceEnum::Microphone:
    2238             :       case MediaSourceEnum::Other:
    2239             :       default: {
    2240             :         RefPtr<MediaStreamError> error =
    2241             :             new MediaStreamError(aWindow,
    2242           0 :                                  NS_LITERAL_STRING("OverconstrainedError"),
    2243           0 :                                  NS_LITERAL_STRING(""),
    2244           0 :                                  NS_LITERAL_STRING("mediaSource"));
    2245           0 :         onFailure->OnError(error);
    2246           0 :         return NS_OK;
    2247             :       }
    2248             :     }
    2249             : 
    2250           0 :     if (vc.mAdvanced.WasPassed() && videoType != MediaSourceEnum::Camera) {
    2251             :       // iterate through advanced, forcing all unset mediaSources to match "root"
    2252             :       const char *unset = EnumToASCII(dom::MediaSourceEnumValues::strings,
    2253           0 :                                       MediaSourceEnum::Camera);
    2254           0 :       for (MediaTrackConstraintSet& cs : vc.mAdvanced.Value()) {
    2255           0 :         if (cs.mMediaSource.EqualsASCII(unset)) {
    2256           0 :           cs.mMediaSource = vc.mMediaSource;
    2257             :         }
    2258             :       }
    2259             :     }
    2260           0 :     if (!privileged) {
    2261             :       // only allow privileged content to set the window id
    2262           0 :       if (vc.mBrowserWindow.WasPassed()) {
    2263           0 :         vc.mBrowserWindow.Value() = -1;
    2264             :       }
    2265           0 :       if (vc.mAdvanced.WasPassed()) {
    2266           0 :         for (MediaTrackConstraintSet& cs : vc.mAdvanced.Value()) {
    2267           0 :           if (cs.mBrowserWindow.WasPassed()) {
    2268           0 :             cs.mBrowserWindow.Value() = -1;
    2269             :           }
    2270             :         }
    2271             :       }
    2272             :     }
    2273           0 :   } else if (IsOn(c.mVideo)) {
    2274           0 :     videoType = MediaSourceEnum::Camera;
    2275           0 :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_TYPE,
    2276           0 :                           (uint32_t) videoType);
    2277             :   }
    2278             : 
    2279           0 :   if (c.mAudio.IsMediaTrackConstraints()) {
    2280           0 :     auto& ac = c.mAudio.GetAsMediaTrackConstraints();
    2281           0 :     MediaConstraintsHelper::ConvertOldWithWarning(ac.mMozAutoGainControl,
    2282             :                                                   ac.mAutoGainControl,
    2283             :                                                   "MozAutoGainControlWarning",
    2284           0 :                                                   aWindow);
    2285           0 :     MediaConstraintsHelper::ConvertOldWithWarning(ac.mMozNoiseSuppression,
    2286             :                                                   ac.mNoiseSuppression,
    2287             :                                                   "MozNoiseSuppressionWarning",
    2288           0 :                                                   aWindow);
    2289           0 :     audioType = StringToEnum(dom::MediaSourceEnumValues::strings,
    2290             :                              ac.mMediaSource,
    2291           0 :                              MediaSourceEnum::Other);
    2292             :     // Work around WebIDL default since spec uses same dictionary w/audio & video.
    2293           0 :     if (audioType == MediaSourceEnum::Camera) {
    2294           0 :       audioType = MediaSourceEnum::Microphone;
    2295           0 :       ac.mMediaSource.AssignASCII(EnumToASCII(dom::MediaSourceEnumValues::strings,
    2296           0 :                                               audioType));
    2297             :     }
    2298           0 :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_TYPE,
    2299           0 :                           (uint32_t) audioType);
    2300             : 
    2301           0 :     switch (audioType) {
    2302             :       case MediaSourceEnum::Microphone:
    2303           0 :         break;
    2304             : 
    2305             :       case MediaSourceEnum::AudioCapture:
    2306             :         // Only enable AudioCapture if the pref is enabled. If it's not, we can
    2307             :         // deny right away.
    2308           0 :         if (!Preferences::GetBool("media.getusermedia.audiocapture.enabled")) {
    2309             :           RefPtr<MediaStreamError> error =
    2310             :             new MediaStreamError(aWindow,
    2311           0 :                                  NS_LITERAL_STRING("NotAllowedError"));
    2312           0 :           onFailure->OnError(error);
    2313           0 :           return NS_OK;
    2314             :         }
    2315           0 :         break;
    2316             : 
    2317             :       case MediaSourceEnum::Other:
    2318             :       default: {
    2319             :         RefPtr<MediaStreamError> error =
    2320             :             new MediaStreamError(aWindow,
    2321           0 :                                  NS_LITERAL_STRING("OverconstrainedError"),
    2322           0 :                                  NS_LITERAL_STRING(""),
    2323           0 :                                  NS_LITERAL_STRING("mediaSource"));
    2324           0 :         onFailure->OnError(error);
    2325           0 :         return NS_OK;
    2326             :       }
    2327             :     }
    2328           0 :     if (ac.mAdvanced.WasPassed()) {
    2329             :       // iterate through advanced, forcing all unset mediaSources to match "root"
    2330             :       const char *unset = EnumToASCII(dom::MediaSourceEnumValues::strings,
    2331           0 :                                       MediaSourceEnum::Camera);
    2332           0 :       for (MediaTrackConstraintSet& cs : ac.mAdvanced.Value()) {
    2333           0 :         if (cs.mMediaSource.EqualsASCII(unset)) {
    2334           0 :           cs.mMediaSource = ac.mMediaSource;
    2335             :         }
    2336             :       }
    2337             :     }
    2338           0 :   } else if (IsOn(c.mAudio)) {
    2339           0 :     audioType = MediaSourceEnum::Microphone;
    2340           0 :     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_TYPE,
    2341           0 :                           (uint32_t) audioType);
    2342             :   }
    2343             : 
    2344             :   // Create a window listener if it doesn't already exist.
    2345             :   RefPtr<GetUserMediaWindowListener> windowListener =
    2346           0 :     GetWindowListener(windowID);
    2347           0 :   if (windowListener) {
    2348           0 :     PrincipalHandle existingPrincipalHandle = windowListener->GetPrincipalHandle();
    2349           0 :     MOZ_ASSERT(PrincipalHandleMatches(existingPrincipalHandle, principal));
    2350             :   } else {
    2351             :     windowListener = new GetUserMediaWindowListener(mMediaThread, windowID,
    2352           0 :                                                     MakePrincipalHandle(principal));
    2353           0 :     AddWindowID(windowID, windowListener);
    2354             :   }
    2355             : 
    2356           0 :   RefPtr<SourceListener> sourceListener = new SourceListener();
    2357           0 :   windowListener->Register(sourceListener);
    2358             : 
    2359           0 :   if (!privileged) {
    2360             :     // Check if this site has had persistent permissions denied.
    2361             :     nsCOMPtr<nsIPermissionManager> permManager =
    2362           0 :       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
    2363           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2364             : 
    2365           0 :     uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
    2366           0 :     if (IsOn(c.mAudio)) {
    2367           0 :       if (audioType == MediaSourceEnum::Microphone &&
    2368           0 :           Preferences::GetBool("media.getusermedia.microphone.deny", false)) {
    2369           0 :         audioPerm = nsIPermissionManager::DENY_ACTION;
    2370             :       } else {
    2371           0 :         rv = permManager->TestExactPermissionFromPrincipal(
    2372           0 :           principal, "microphone", &audioPerm);
    2373           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2374             :       }
    2375             :     }
    2376             : 
    2377           0 :     uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
    2378           0 :     if (IsOn(c.mVideo)) {
    2379           0 :       if (videoType == MediaSourceEnum::Camera &&
    2380           0 :           Preferences::GetBool("media.getusermedia.camera.deny", false)) {
    2381           0 :         videoPerm = nsIPermissionManager::DENY_ACTION;
    2382             :       } else {
    2383           0 :         rv = permManager->TestExactPermissionFromPrincipal(
    2384             :           principal, videoType == MediaSourceEnum::Camera ? "camera" : "screen",
    2385           0 :           &videoPerm);
    2386           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2387             :       }
    2388             :     }
    2389             : 
    2390           0 :     if ((!IsOn(c.mAudio) && !IsOn(c.mVideo)) ||
    2391           0 :         (IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) ||
    2392           0 :         (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION)) {
    2393             :       RefPtr<MediaStreamError> error =
    2394           0 :           new MediaStreamError(aWindow, NS_LITERAL_STRING("NotAllowedError"));
    2395           0 :       onFailure->OnError(error);
    2396           0 :       windowListener->Remove(sourceListener);
    2397           0 :       return NS_OK;
    2398             :     }
    2399             :   }
    2400             : 
    2401             :   // Get list of all devices, with origin-specific device ids.
    2402             : 
    2403           0 :   MediaEnginePrefs prefs = mPrefs;
    2404             : 
    2405           0 :   nsString callID;
    2406           0 :   rv = GenerateUUID(callID);
    2407           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2408             : 
    2409           0 :   bool fake = c.mFake.WasPassed()? c.mFake.Value() :
    2410           0 :       Preferences::GetBool("media.navigator.streams.fake");
    2411             : 
    2412             :   bool askPermission =
    2413           0 :       (!privileged || Preferences::GetBool("media.navigator.permission.force")) &&
    2414           0 :       (!fake || Preferences::GetBool("media.navigator.permission.fake"));
    2415             : 
    2416           0 :   RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowID, videoType,
    2417           0 :                                                    audioType, fake);
    2418           0 :   RefPtr<MediaManager> self = this;
    2419           0 :   p->Then([self, onSuccess, onFailure, windowID, c, windowListener,
    2420             :            sourceListener, askPermission, prefs, isHTTPS, callID, principalInfo,
    2421           0 :            isChrome](SourceSet*& aDevices) mutable {
    2422             :     // grab result
    2423           0 :     auto devices = MakeRefPtr<Refcountable<UniquePtr<SourceSet>>>(aDevices);
    2424             : 
    2425             :     // Ensure that our windowID is still good.
    2426           0 :     if (!nsGlobalWindow::GetInnerWindowWithId(windowID)) {
    2427           0 :       return;
    2428             :     }
    2429             : 
    2430             :     // Apply any constraints. This modifies the passed-in list.
    2431           0 :     RefPtr<PledgeChar> p2 = self->SelectSettings(c, isChrome, devices);
    2432             : 
    2433           0 :     p2->Then([self, onSuccess, onFailure, windowID, c,
    2434             :               windowListener, sourceListener, askPermission, prefs, isHTTPS,
    2435             :               callID, principalInfo, isChrome, devices
    2436           0 :              ](const char*& badConstraint) mutable {
    2437             : 
    2438             :       // Ensure that the captured 'this' pointer and our windowID are still good.
    2439           0 :       auto* globalWindow = nsGlobalWindow::GetInnerWindowWithId(windowID);
    2440           0 :       RefPtr<nsPIDOMWindowInner> window = globalWindow ? globalWindow->AsInner()
    2441           0 :                                                        : nullptr;
    2442           0 :       if (!MediaManager::Exists() || !window) {
    2443           0 :         return;
    2444             :       }
    2445             : 
    2446           0 :       if (badConstraint) {
    2447           0 :         nsString constraint;
    2448           0 :         constraint.AssignASCII(badConstraint);
    2449             :         RefPtr<MediaStreamError> error =
    2450             :             new MediaStreamError(window,
    2451           0 :                                  NS_LITERAL_STRING("OverconstrainedError"),
    2452           0 :                                  NS_LITERAL_STRING(""),
    2453           0 :                                  constraint);
    2454           0 :         onFailure->OnError(error);
    2455           0 :         return;
    2456             :       }
    2457           0 :       if (!(*devices)->Length()) {
    2458             :         RefPtr<MediaStreamError> error =
    2459           0 :             new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
    2460           0 :         onFailure->OnError(error);
    2461           0 :         return;
    2462             :       }
    2463             : 
    2464           0 :       nsCOMPtr<nsIMutableArray> devicesCopy = nsArray::Create(); // before we give up devices below
    2465           0 :       if (!askPermission) {
    2466           0 :         for (auto& device : **devices) {
    2467           0 :           nsresult rv = devicesCopy->AppendElement(device, /*weak =*/ false);
    2468           0 :           if (NS_WARN_IF(NS_FAILED(rv))) {
    2469           0 :             return;
    2470             :           }
    2471             :         }
    2472             :       }
    2473             : 
    2474             :       // Pass callbacks and listeners along to GetUserMediaTask.
    2475             :       RefPtr<GetUserMediaTask> task (new GetUserMediaTask(c,
    2476           0 :                                                           onSuccess.forget(),
    2477           0 :                                                           onFailure.forget(),
    2478             :                                                           windowID,
    2479             :                                                           windowListener,
    2480             :                                                           sourceListener,
    2481             :                                                           prefs,
    2482             :                                                           principalInfo,
    2483             :                                                           isChrome,
    2484           0 :                                                           devices->release()));
    2485             :       // Store the task w/callbacks.
    2486           0 :       self->mActiveCallbacks.Put(callID, task.forget());
    2487             : 
    2488             :       // Add a WindowID cross-reference so OnNavigation can tear things down
    2489             :       nsTArray<nsString>* array;
    2490           0 :       if (!self->mCallIds.Get(windowID, &array)) {
    2491           0 :         array = new nsTArray<nsString>();
    2492           0 :         self->mCallIds.Put(windowID, array);
    2493             :       }
    2494           0 :       array->AppendElement(callID);
    2495             : 
    2496           0 :       nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    2497           0 :       if (!askPermission) {
    2498           0 :         obs->NotifyObservers(devicesCopy, "getUserMedia:privileged:allow",
    2499           0 :                              callID.BeginReading());
    2500             :       } else {
    2501             :         RefPtr<GetUserMediaRequest> req =
    2502           0 :             new GetUserMediaRequest(window, callID, c, isHTTPS);
    2503           0 :         if (!Preferences::GetBool("media.navigator.permission.force") && array->Length() > 1) {
    2504             :           // there is at least 1 pending gUM request
    2505             :           // For the scarySources test case, always send the request
    2506           0 :           self->mPendingGUMRequest.AppendElement(req.forget());
    2507             :         } else {
    2508           0 :           obs->NotifyObservers(req, "getUserMedia:request", nullptr);
    2509             :         }
    2510             :       }
    2511             : 
    2512             : #ifdef MOZ_WEBRTC
    2513           0 :       EnableWebRtcLog();
    2514             : #endif
    2515           0 :     }, [onFailure](MediaStreamError*& reason) mutable {
    2516           0 :       onFailure->OnError(reason);
    2517           0 :     });
    2518           0 :   }, [onFailure](MediaStreamError*& reason) mutable {
    2519           0 :     onFailure->OnError(reason);
    2520           0 :   });
    2521           0 :   return NS_OK;
    2522             : }
    2523             : 
    2524             : /* static */ void
    2525           0 : MediaManager::AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey)
    2526             : {
    2527           0 :   if (!aOriginKey.IsEmpty()) {
    2528           0 :     for (auto& device : aDevices) {
    2529           0 :       nsString id;
    2530           0 :       device->GetId(id);
    2531           0 :       device->SetRawId(id);
    2532           0 :       AnonymizeId(id, aOriginKey);
    2533           0 :       device->SetId(id);
    2534             :     }
    2535             :   }
    2536           0 : }
    2537             : 
    2538             : /* static */ nsresult
    2539           0 : MediaManager::AnonymizeId(nsAString& aId, const nsACString& aOriginKey)
    2540             : {
    2541           0 :   MOZ_ASSERT(NS_IsMainThread());
    2542             : 
    2543             :   nsresult rv;
    2544             :   nsCOMPtr<nsIKeyObjectFactory> factory =
    2545           0 :     do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv);
    2546           0 :   if (NS_FAILED(rv)) {
    2547           0 :     return rv;
    2548             :   }
    2549           0 :   nsCString rawKey;
    2550           0 :   rv = Base64Decode(aOriginKey, rawKey);
    2551           0 :   if (NS_FAILED(rv)) {
    2552           0 :     return rv;
    2553             :   }
    2554           0 :   nsCOMPtr<nsIKeyObject> key;
    2555           0 :   rv = factory->KeyFromString(nsIKeyObject::HMAC, rawKey, getter_AddRefs(key));
    2556           0 :   if (NS_FAILED(rv)) {
    2557           0 :     return rv;
    2558             :   }
    2559             : 
    2560             :   nsCOMPtr<nsICryptoHMAC> hasher =
    2561           0 :     do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
    2562           0 :   if (NS_FAILED(rv)) {
    2563           0 :     return rv;
    2564             :   }
    2565           0 :   rv = hasher->Init(nsICryptoHMAC::SHA256, key);
    2566           0 :   if (NS_FAILED(rv)) {
    2567           0 :     return rv;
    2568             :   }
    2569           0 :   NS_ConvertUTF16toUTF8 id(aId);
    2570           0 :   rv = hasher->Update(reinterpret_cast<const uint8_t*> (id.get()), id.Length());
    2571           0 :   if (NS_FAILED(rv)) {
    2572           0 :     return rv;
    2573             :   }
    2574           0 :   nsCString mac;
    2575           0 :   rv = hasher->Finish(true, mac);
    2576           0 :   if (NS_FAILED(rv)) {
    2577           0 :     return rv;
    2578             :   }
    2579             : 
    2580           0 :   aId = NS_ConvertUTF8toUTF16(mac);
    2581           0 :   return NS_OK;
    2582             : }
    2583             : 
    2584             : /* static */
    2585             : already_AddRefed<nsIWritableVariant>
    2586           0 : MediaManager::ToJSArray(SourceSet& aDevices)
    2587             : {
    2588           0 :   MOZ_ASSERT(NS_IsMainThread());
    2589           0 :   RefPtr<nsVariantCC> var = new nsVariantCC();
    2590           0 :   size_t len = aDevices.Length();
    2591           0 :   if (len) {
    2592           0 :     nsTArray<nsIMediaDevice*> tmp(len);
    2593           0 :     for (auto& device : aDevices) {
    2594           0 :       tmp.AppendElement(device);
    2595             :     }
    2596           0 :     auto* elements = static_cast<const void*>(tmp.Elements());
    2597           0 :     nsresult rv = var->SetAsArray(nsIDataType::VTYPE_INTERFACE,
    2598             :                                   &NS_GET_IID(nsIMediaDevice), len,
    2599           0 :                                   const_cast<void*>(elements));
    2600           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2601           0 :       return nullptr;
    2602             :     }
    2603             :   } else {
    2604           0 :     var->SetAsEmptyArray(); // because SetAsArray() fails on zero length arrays.
    2605             :   }
    2606           0 :   return var.forget();
    2607             : }
    2608             : 
    2609             : already_AddRefed<MediaManager::PledgeSourceSet>
    2610           0 : MediaManager::EnumerateDevicesImpl(uint64_t aWindowId,
    2611             :                                    MediaSourceEnum aVideoType,
    2612             :                                    MediaSourceEnum aAudioType,
    2613             :                                    bool aFake)
    2614             : {
    2615           0 :   MOZ_ASSERT(NS_IsMainThread());
    2616             :   nsPIDOMWindowInner* window =
    2617           0 :     nsGlobalWindow::GetInnerWindowWithId(aWindowId)->AsInner();
    2618             : 
    2619             :   // This function returns a pledge, a promise-like object with the future result
    2620           0 :   RefPtr<PledgeSourceSet> pledge = new PledgeSourceSet();
    2621           0 :   uint32_t id = mOutstandingPledges.Append(*pledge);
    2622             : 
    2623             :   // To get a device list anonymized for a particular origin, we must:
    2624             :   // 1. Get an origin-key (for either regular or private browsing)
    2625             :   // 2. Get the raw devices list
    2626             :   // 3. Anonymize the raw list with the origin-key.
    2627             : 
    2628             :   nsCOMPtr<nsIPrincipal> principal =
    2629           0 :     nsGlobalWindow::Cast(window)->GetPrincipal();
    2630           0 :   MOZ_ASSERT(principal);
    2631             : 
    2632           0 :   ipc::PrincipalInfo principalInfo;
    2633           0 :   nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
    2634           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2635           0 :     RefPtr<PledgeSourceSet> p = new PledgeSourceSet();
    2636             :     RefPtr<MediaStreamError> error =
    2637           0 :       new MediaStreamError(window, NS_LITERAL_STRING("NotAllowedError"));
    2638           0 :     p->Reject(error);
    2639           0 :     return p.forget();
    2640             :   }
    2641             : 
    2642           0 :   bool persist = IsActivelyCapturingOrHasAPermission(aWindowId);
    2643             : 
    2644             :   // GetPrincipalKey is an async API that returns a pledge (a promise-like
    2645             :   // pattern). We use .Then() to pass in a lambda to run back on this same
    2646             :   // thread later once GetPrincipalKey resolves. Needed variables are "captured"
    2647             :   // (passed by value) safely into the lambda.
    2648             : 
    2649           0 :   RefPtr<Pledge<nsCString>> p = media::GetPrincipalKey(principalInfo, persist);
    2650           0 :   p->Then([id, aWindowId, aVideoType, aAudioType,
    2651           0 :            aFake](const nsCString& aOriginKey) mutable {
    2652           0 :     MOZ_ASSERT(NS_IsMainThread());
    2653           0 :     RefPtr<MediaManager> mgr = MediaManager_GetInstance();
    2654             : 
    2655           0 :     RefPtr<PledgeSourceSet> p = mgr->EnumerateRawDevices(aWindowId, aVideoType,
    2656           0 :                                                          aAudioType, aFake);
    2657           0 :     p->Then([id, aWindowId, aOriginKey](SourceSet*& aDevices) mutable {
    2658           0 :       UniquePtr<SourceSet> devices(aDevices); // secondary result
    2659             : 
    2660             :       // Only run if window is still on our active list.
    2661           0 :       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
    2662           0 :       if (!mgr) {
    2663           0 :         return NS_OK;
    2664             :       }
    2665           0 :       RefPtr<PledgeSourceSet> p = mgr->mOutstandingPledges.Remove(id);
    2666           0 :       if (!p || !mgr->IsWindowStillActive(aWindowId)) {
    2667           0 :         return NS_OK;
    2668             :       }
    2669           0 :       MediaManager_AnonymizeDevices(*devices, aOriginKey);
    2670           0 :       p->Resolve(devices.release());
    2671           0 :       return NS_OK;
    2672           0 :     });
    2673           0 :   });
    2674           0 :   return pledge.forget();
    2675             : }
    2676             : 
    2677             : nsresult
    2678           0 : MediaManager::EnumerateDevices(nsPIDOMWindowInner* aWindow,
    2679             :                                nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
    2680             :                                nsIDOMGetUserMediaErrorCallback* aOnFailure)
    2681             : {
    2682           0 :   MOZ_ASSERT(NS_IsMainThread());
    2683           0 :   NS_ENSURE_TRUE(!sInShutdown, NS_ERROR_FAILURE);
    2684           0 :   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
    2685           0 :   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
    2686           0 :   uint64_t windowId = aWindow->WindowID();
    2687             : 
    2688           0 :   nsIPrincipal* principal = aWindow->GetExtantDoc()->NodePrincipal();
    2689             : 
    2690             :   RefPtr<GetUserMediaWindowListener> windowListener =
    2691           0 :     GetWindowListener(windowId);
    2692           0 :   if (windowListener) {
    2693             :     PrincipalHandle existingPrincipalHandle =
    2694           0 :       windowListener->GetPrincipalHandle();
    2695           0 :     MOZ_ASSERT(PrincipalHandleMatches(existingPrincipalHandle, principal));
    2696             :   } else {
    2697             :     windowListener = new GetUserMediaWindowListener(mMediaThread, windowId,
    2698           0 :                                                     MakePrincipalHandle(principal));
    2699           0 :     AddWindowID(windowId, windowListener);
    2700             :   }
    2701             : 
    2702             :   // Create an inactive SourceListener to act as a placeholder, so the
    2703             :   // window listener doesn't clean itself up until we're done.
    2704           0 :   RefPtr<SourceListener> sourceListener = new SourceListener();
    2705           0 :   windowListener->Register(sourceListener);
    2706             : 
    2707           0 :   bool fake = Preferences::GetBool("media.navigator.streams.fake");
    2708             : 
    2709           0 :   RefPtr<PledgeSourceSet> p = EnumerateDevicesImpl(windowId,
    2710             :                                                    MediaSourceEnum::Camera,
    2711             :                                                    MediaSourceEnum::Microphone,
    2712           0 :                                                    fake);
    2713           0 :   p->Then([onSuccess, windowListener, sourceListener](SourceSet*& aDevices) mutable {
    2714           0 :     UniquePtr<SourceSet> devices(aDevices); // grab result
    2715           0 :     DebugOnly<bool> rv = windowListener->Remove(sourceListener);
    2716           0 :     MOZ_ASSERT(rv);
    2717           0 :     nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*devices);
    2718           0 :     onSuccess->OnSuccess(array);
    2719           0 :   }, [onFailure, windowListener, sourceListener](MediaStreamError*& reason) mutable {
    2720           0 :     DebugOnly<bool> rv = windowListener->Remove(sourceListener);
    2721           0 :     MOZ_ASSERT(rv);
    2722           0 :     onFailure->OnError(reason);
    2723           0 :   });
    2724           0 :   return NS_OK;
    2725             : }
    2726             : 
    2727             : /*
    2728             :  * GetUserMediaDevices - called by the UI-part of getUserMedia from chrome JS.
    2729             :  */
    2730             : 
    2731             : nsresult
    2732           0 : MediaManager::GetUserMediaDevices(nsPIDOMWindowInner* aWindow,
    2733             :                                   const MediaStreamConstraints& aConstraints,
    2734             :                                   nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
    2735             :                                   nsIDOMGetUserMediaErrorCallback* aOnFailure,
    2736             :                                   uint64_t aWindowId,
    2737             :                                   const nsAString& aCallID)
    2738             : {
    2739           0 :   MOZ_ASSERT(NS_IsMainThread());
    2740           0 :   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
    2741           0 :   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
    2742           0 :   if (!aWindowId) {
    2743           0 :     aWindowId = aWindow->WindowID();
    2744             :   }
    2745             : 
    2746             :   // Ignore passed-in constraints, instead locate + return already-constrained list.
    2747             : 
    2748             :   nsTArray<nsString>* callIDs;
    2749           0 :   if (!mCallIds.Get(aWindowId, &callIDs)) {
    2750           0 :     return NS_ERROR_UNEXPECTED;
    2751             :   }
    2752             : 
    2753           0 :   for (auto& callID : *callIDs) {
    2754           0 :     RefPtr<GetUserMediaTask> task;
    2755           0 :     if (!aCallID.Length() || aCallID == callID) {
    2756           0 :       if (mActiveCallbacks.Get(callID, getter_AddRefs(task))) {
    2757           0 :         nsCOMPtr<nsIWritableVariant> array = MediaManager_ToJSArray(*task->mSourceSet);
    2758           0 :         onSuccess->OnSuccess(array);
    2759           0 :         return NS_OK;
    2760             :       }
    2761             :     }
    2762             :   }
    2763           0 :   return NS_ERROR_UNEXPECTED;
    2764             : }
    2765             : 
    2766             : MediaEngine*
    2767           0 : MediaManager::GetBackend(uint64_t aWindowId)
    2768             : {
    2769           0 :   MOZ_ASSERT(MediaManager::IsInMediaThread());
    2770             :   // Plugin backends as appropriate. The default engine also currently
    2771             :   // includes picture support for Android.
    2772             :   // This IS called off main-thread.
    2773           0 :   if (!mBackend) {
    2774           0 :     MOZ_RELEASE_ASSERT(!sInShutdown);  // we should never create a new backend in shutdown
    2775             : #if defined(MOZ_WEBRTC)
    2776           0 :     mBackend = new MediaEngineWebRTC(mPrefs);
    2777             : #else
    2778             :     mBackend = new MediaEngineDefault();
    2779             : #endif
    2780             :   }
    2781           0 :   return mBackend;
    2782             : }
    2783             : 
    2784             : static void
    2785           0 : StopSharingCallback(MediaManager *aThis,
    2786             :                     uint64_t aWindowID,
    2787             :                     GetUserMediaWindowListener *aListener,
    2788             :                     void *aData)
    2789             : {
    2790           0 :   MOZ_ASSERT(NS_IsMainThread());
    2791             : 
    2792             :   // Grab a strong ref since RemoveAll() might destroy the listener mid-way
    2793             :   // when clearing the mActiveWindows reference.
    2794           0 :   RefPtr<GetUserMediaWindowListener> listener(aListener);
    2795           0 :   if (!listener) {
    2796           0 :     return;
    2797             :   }
    2798             : 
    2799           0 :   listener->Stop();
    2800           0 :   listener->RemoveAll();
    2801           0 :   MOZ_ASSERT(!aThis->GetWindowListener(aWindowID));
    2802             : }
    2803             : 
    2804             : 
    2805             : void
    2806           0 : MediaManager::OnNavigation(uint64_t aWindowID)
    2807             : {
    2808           0 :   MOZ_ASSERT(NS_IsMainThread());
    2809           0 :   LOG(("OnNavigation for %" PRIu64, aWindowID));
    2810             : 
    2811             :   // Stop the streams for this window. The runnables check this value before
    2812             :   // making a call to content.
    2813             : 
    2814             :   nsTArray<nsString>* callIDs;
    2815           0 :   if (mCallIds.Get(aWindowID, &callIDs)) {
    2816           0 :     for (auto& callID : *callIDs) {
    2817           0 :       mActiveCallbacks.Remove(callID);
    2818             :     }
    2819           0 :     mCallIds.Remove(aWindowID);
    2820             :   }
    2821             : 
    2822             :   // This is safe since we're on main-thread, and the windowlist can only
    2823             :   // be added to from the main-thread
    2824           0 :   auto* window = nsGlobalWindow::GetInnerWindowWithId(aWindowID);
    2825           0 :   if (window) {
    2826           0 :     IterateWindowListeners(window->AsInner(), StopSharingCallback, nullptr);
    2827             :   } else {
    2828           0 :     RemoveWindowID(aWindowID);
    2829             :   }
    2830           0 :   MOZ_ASSERT(!GetWindowListener(aWindowID));
    2831             : 
    2832           0 :   RemoveMediaDevicesCallback(aWindowID);
    2833           0 : }
    2834             : 
    2835             : void
    2836           0 : MediaManager::RemoveMediaDevicesCallback(uint64_t aWindowID)
    2837             : {
    2838           0 :   MutexAutoLock lock(mCallbackMutex);
    2839           0 :   for (DeviceChangeCallback* observer : mDeviceChangeCallbackList)
    2840             :   {
    2841           0 :     dom::MediaDevices* mediadevices = static_cast<dom::MediaDevices *>(observer);
    2842           0 :     MOZ_ASSERT(mediadevices);
    2843           0 :     if (mediadevices) {
    2844           0 :       nsPIDOMWindowInner* window = mediadevices->GetOwner();
    2845           0 :       MOZ_ASSERT(window);
    2846           0 :       if (window && window->WindowID() == aWindowID) {
    2847           0 :         DeviceChangeCallback::RemoveDeviceChangeCallbackLocked(observer);
    2848           0 :         return;
    2849             :       }
    2850             :     }
    2851             :   }
    2852             : }
    2853             : 
    2854             : void
    2855           0 : MediaManager::AddWindowID(uint64_t aWindowId,
    2856             :                           GetUserMediaWindowListener* aListener)
    2857             : {
    2858           0 :   MOZ_ASSERT(NS_IsMainThread());
    2859             :   // Store the WindowID in a hash table and mark as active. The entry is removed
    2860             :   // when this window is closed or navigated away from.
    2861             :   // This is safe since we're on main-thread, and the windowlist can only
    2862             :   // be invalidated from the main-thread (see OnNavigation)
    2863           0 :   if (IsWindowStillActive(aWindowId)) {
    2864           0 :     MOZ_ASSERT(false, "Window already added");
    2865             :     return;
    2866             :   }
    2867           0 :   GetActiveWindows()->Put(aWindowId, aListener);
    2868           0 : }
    2869             : 
    2870             : void
    2871           0 : MediaManager::RemoveWindowID(uint64_t aWindowId)
    2872             : {
    2873           0 :   mActiveWindows.Remove(aWindowId);
    2874             : 
    2875             :   // get outer windowID
    2876           0 :   auto* window = nsGlobalWindow::GetInnerWindowWithId(aWindowId);
    2877           0 :   if (!window) {
    2878           0 :     LOG(("No inner window for %" PRIu64, aWindowId));
    2879           0 :     return;
    2880             :   }
    2881             : 
    2882           0 :   nsPIDOMWindowOuter* outer = window->AsInner()->GetOuterWindow();
    2883           0 :   if (!outer) {
    2884           0 :     LOG(("No outer window for inner %" PRIu64, aWindowId));
    2885           0 :     return;
    2886             :   }
    2887             : 
    2888           0 :   uint64_t outerID = outer->WindowID();
    2889             : 
    2890             :   // Notify the UI that this window no longer has gUM active
    2891             :   char windowBuffer[32];
    2892           0 :   SprintfLiteral(windowBuffer, "%" PRIu64, outerID);
    2893           0 :   nsString data = NS_ConvertUTF8toUTF16(windowBuffer);
    2894             : 
    2895           0 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    2896           0 :   obs->NotifyObservers(nullptr, "recording-window-ended", data.get());
    2897           0 :   LOG(("Sent recording-window-ended for window %" PRIu64 " (outer %" PRIu64 ")",
    2898             :        aWindowId, outerID));
    2899             : }
    2900             : 
    2901             : void
    2902           0 : MediaManager::GetPref(nsIPrefBranch *aBranch, const char *aPref,
    2903             :                       const char *aData, int32_t *aVal)
    2904             : {
    2905             :   int32_t temp;
    2906           0 :   if (aData == nullptr || strcmp(aPref,aData) == 0) {
    2907           0 :     if (NS_SUCCEEDED(aBranch->GetIntPref(aPref, &temp))) {
    2908           0 :       *aVal = temp;
    2909             :     }
    2910             :   }
    2911           0 : }
    2912             : 
    2913             : void
    2914           0 : MediaManager::GetPrefBool(nsIPrefBranch *aBranch, const char *aPref,
    2915             :                           const char *aData, bool *aVal)
    2916             : {
    2917             :   bool temp;
    2918           0 :   if (aData == nullptr || strcmp(aPref,aData) == 0) {
    2919           0 :     if (NS_SUCCEEDED(aBranch->GetBoolPref(aPref, &temp))) {
    2920           0 :       *aVal = temp;
    2921             :     }
    2922             :   }
    2923           0 : }
    2924             : 
    2925             : void
    2926           0 : MediaManager::GetPrefs(nsIPrefBranch *aBranch, const char *aData)
    2927             : {
    2928           0 :   GetPref(aBranch, "media.navigator.video.default_width", aData, &mPrefs.mWidth);
    2929           0 :   GetPref(aBranch, "media.navigator.video.default_height", aData, &mPrefs.mHeight);
    2930           0 :   GetPref(aBranch, "media.navigator.video.default_fps", aData, &mPrefs.mFPS);
    2931           0 :   GetPref(aBranch, "media.navigator.video.default_minfps", aData, &mPrefs.mMinFPS);
    2932           0 :   GetPref(aBranch, "media.navigator.audio.fake_frequency", aData, &mPrefs.mFreq);
    2933             : #ifdef MOZ_WEBRTC
    2934           0 :   GetPrefBool(aBranch, "media.getusermedia.aec_enabled", aData, &mPrefs.mAecOn);
    2935           0 :   GetPrefBool(aBranch, "media.getusermedia.agc_enabled", aData, &mPrefs.mAgcOn);
    2936           0 :   GetPrefBool(aBranch, "media.getusermedia.noise_enabled", aData, &mPrefs.mNoiseOn);
    2937           0 :   GetPref(aBranch, "media.getusermedia.aec", aData, &mPrefs.mAec);
    2938           0 :   GetPref(aBranch, "media.getusermedia.agc", aData, &mPrefs.mAgc);
    2939           0 :   GetPref(aBranch, "media.getusermedia.noise", aData, &mPrefs.mNoise);
    2940           0 :   GetPref(aBranch, "media.getusermedia.playout_delay", aData, &mPrefs.mPlayoutDelay);
    2941           0 :   GetPrefBool(aBranch, "media.getusermedia.aec_extended_filter", aData, &mPrefs.mExtendedFilter);
    2942           0 :   GetPrefBool(aBranch, "media.getusermedia.aec_aec_delay_agnostic", aData, &mPrefs.mDelayAgnostic);
    2943           0 :   GetPref(aBranch, "media.getusermedia.channels", aData, &mPrefs.mChannels);
    2944           0 :   GetPrefBool(aBranch, "media.ondevicechange.fakeDeviceChangeEvent.enabled", aData, &mPrefs.mFakeDeviceChangeEventOn);
    2945             : #endif
    2946           0 :   GetPrefBool(aBranch, "media.navigator.audio.full_duplex", aData, &mPrefs.mFullDuplex);
    2947           0 : }
    2948             : 
    2949             : void
    2950           0 : MediaManager::Shutdown()
    2951             : {
    2952           0 :   MOZ_ASSERT(NS_IsMainThread());
    2953           0 :   if (sInShutdown) {
    2954           0 :     return;
    2955             :   }
    2956           0 :   sInShutdown = true;
    2957             : 
    2958           0 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    2959             : 
    2960           0 :   obs->RemoveObserver(this, "last-pb-context-exited");
    2961           0 :   obs->RemoveObserver(this, "getUserMedia:privileged:allow");
    2962           0 :   obs->RemoveObserver(this, "getUserMedia:response:allow");
    2963           0 :   obs->RemoveObserver(this, "getUserMedia:response:deny");
    2964           0 :   obs->RemoveObserver(this, "getUserMedia:revoke");
    2965             : 
    2966           0 :   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
    2967           0 :   if (prefs) {
    2968           0 :     prefs->RemoveObserver("media.navigator.video.default_width", this);
    2969           0 :     prefs->RemoveObserver("media.navigator.video.default_height", this);
    2970           0 :     prefs->RemoveObserver("media.navigator.video.default_fps", this);
    2971           0 :     prefs->RemoveObserver("media.navigator.video.default_minfps", this);
    2972           0 :     prefs->RemoveObserver("media.navigator.audio.fake_frequency", this);
    2973             : #ifdef MOZ_WEBRTC
    2974           0 :     prefs->RemoveObserver("media.getusermedia.aec_enabled", this);
    2975           0 :     prefs->RemoveObserver("media.getusermedia.aec", this);
    2976           0 :     prefs->RemoveObserver("media.getusermedia.agc_enabled", this);
    2977           0 :     prefs->RemoveObserver("media.getusermedia.agc", this);
    2978           0 :     prefs->RemoveObserver("media.getusermedia.noise_enabled", this);
    2979           0 :     prefs->RemoveObserver("media.getusermedia.noise", this);
    2980           0 :     prefs->RemoveObserver("media.getusermedia.playout_delay", this);
    2981           0 :     prefs->RemoveObserver("media.ondevicechange.fakeDeviceChangeEvent.enabled", this);
    2982           0 :     prefs->RemoveObserver("media.getusermedia.channels", this);
    2983             : #endif
    2984           0 :     prefs->RemoveObserver("media.navigator.audio.full_duplex", this);
    2985             :   }
    2986             : 
    2987             :   // Close off any remaining active windows.
    2988           0 :   GetActiveWindows()->Clear();
    2989           0 :   mActiveCallbacks.Clear();
    2990           0 :   mCallIds.Clear();
    2991           0 :   mPendingGUMRequest.Clear();
    2992             : #ifdef MOZ_WEBRTC
    2993           0 :   StopWebRtcLog();
    2994             : #endif
    2995             : 
    2996             :   // Because mMediaThread is not an nsThread, we must dispatch to it so it can
    2997             :   // clean up BackgroundChild. Continue stopping thread once this is done.
    2998             : 
    2999           0 :   class ShutdownTask : public Runnable
    3000             :   {
    3001             :   public:
    3002           0 :     ShutdownTask(MediaManager* aManager, already_AddRefed<Runnable> aReply)
    3003           0 :       : mozilla::Runnable("ShutdownTask")
    3004             :       , mManager(aManager)
    3005           0 :       , mReply(aReply)
    3006             :     {
    3007           0 :     }
    3008             : 
    3009             :   private:
    3010             :     NS_IMETHOD
    3011           0 :     Run() override
    3012             :     {
    3013           0 :       LOG(("MediaManager Thread Shutdown"));
    3014           0 :       MOZ_ASSERT(MediaManager::IsInMediaThread());
    3015             :       // Must shutdown backend on MediaManager thread, since that's where we started it from!
    3016             :       {
    3017           0 :         if (mManager->mBackend) {
    3018           0 :           mManager->mBackend->Shutdown(); // ok to invoke multiple times
    3019           0 :           mManager->mBackend->RemoveDeviceChangeCallback(mManager);
    3020             :         }
    3021             :       }
    3022           0 :       mozilla::ipc::BackgroundChild::CloseForCurrentThread();
    3023             :       // must explicitly do this before dispatching the reply, since the reply may kill us with Stop()
    3024           0 :       mManager->mBackend = nullptr; // last reference, will invoke Shutdown() again
    3025             : 
    3026           0 :       if (NS_FAILED(NS_DispatchToMainThread(mReply.forget()))) {
    3027           0 :         LOG(("Will leak thread: DispatchToMainthread of reply runnable failed in MediaManager shutdown"));
    3028             :       }
    3029             : 
    3030           0 :       return NS_OK;
    3031             :     }
    3032             :     RefPtr<MediaManager> mManager;
    3033             :     RefPtr<Runnable> mReply;
    3034             :   };
    3035             : 
    3036             :   // Post ShutdownTask to execute on mMediaThread and pass in a lambda
    3037             :   // callback to be executed back on this thread once it is done.
    3038             :   //
    3039             :   // The lambda callback "captures" the 'this' pointer for member access.
    3040             :   // This is safe since this is guaranteed to be here since sSingleton isn't
    3041             :   // cleared until the lambda function clears it.
    3042             : 
    3043             :   // note that this == sSingleton
    3044           0 :   MOZ_ASSERT(this == sSingleton);
    3045           0 :   RefPtr<MediaManager> that = this;
    3046             : 
    3047             :   // Release the backend (and call Shutdown()) from within the MediaManager thread
    3048             :   // Don't use MediaManager::PostTask() because we're sInShutdown=true here!
    3049             :   RefPtr<ShutdownTask> shutdown = new ShutdownTask(this,
    3050           0 :       media::NewRunnableFrom([this, that]() mutable {
    3051           0 :     LOG(("MediaManager shutdown lambda running, releasing MediaManager singleton and thread"));
    3052           0 :     if (mMediaThread) {
    3053           0 :       mMediaThread->Stop();
    3054             :     }
    3055             : 
    3056             :     // Remove async shutdown blocker
    3057             : 
    3058           0 :     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetShutdownPhase();
    3059           0 :     shutdownPhase->RemoveBlocker(sSingleton->mShutdownBlocker);
    3060             : 
    3061             :     // we hold a ref to 'that' which is the same as sSingleton
    3062           0 :     sSingleton = nullptr;
    3063             : 
    3064           0 :     return NS_OK;
    3065           0 :   }));
    3066           0 :   mMediaThread->message_loop()->PostTask(shutdown.forget());
    3067             : }
    3068             : 
    3069             : void
    3070           0 : MediaManager::SendPendingGUMRequest()
    3071             : {
    3072           0 :   if (mPendingGUMRequest.Length() > 0) {
    3073           0 :     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    3074           0 :     obs->NotifyObservers(mPendingGUMRequest[0], "getUserMedia:request", nullptr);
    3075           0 :     mPendingGUMRequest.RemoveElementAt(0);
    3076             :   }
    3077           0 : }
    3078             : 
    3079             : nsresult
    3080           0 : MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
    3081             :   const char16_t* aData)
    3082             : {
    3083           0 :   MOZ_ASSERT(NS_IsMainThread());
    3084             : 
    3085           0 :   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    3086           0 :     nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
    3087           0 :     if (branch) {
    3088           0 :       GetPrefs(branch,NS_ConvertUTF16toUTF8(aData).get());
    3089           0 :       LOG(("%s: %dx%d @%dfps (min %d)", __FUNCTION__,
    3090             :            mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS));
    3091             :     }
    3092           0 :   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
    3093             :     // Clear memory of private-browsing-specific deviceIds. Fire and forget.
    3094           0 :     media::SanitizeOriginKeys(0, true);
    3095           0 :     return NS_OK;
    3096           0 :   } else if (!strcmp(aTopic, "getUserMedia:got-device-permission")) {
    3097           0 :     MOZ_ASSERT(aSubject);
    3098           0 :     nsCOMPtr<nsIRunnable> task = do_QueryInterface(aSubject);
    3099           0 :     MediaManager::PostTask(NewTaskFrom([task] {
    3100           0 :       task->Run();
    3101           0 :     }));
    3102           0 :     return NS_OK;
    3103           0 :   } else if (!strcmp(aTopic, "getUserMedia:privileged:allow") ||
    3104           0 :              !strcmp(aTopic, "getUserMedia:response:allow")) {
    3105           0 :     nsString key(aData);
    3106           0 :     RefPtr<GetUserMediaTask> task;
    3107           0 :     mActiveCallbacks.Remove(key, getter_AddRefs(task));
    3108           0 :     if (!task) {
    3109           0 :       return NS_OK;
    3110             :     }
    3111             : 
    3112             :     nsTArray<nsString>* array;
    3113           0 :     if (!mCallIds.Get(task->GetWindowID(), &array)) {
    3114           0 :       return NS_OK;
    3115             :     }
    3116           0 :     array->RemoveElement(key);
    3117             : 
    3118           0 :     if (aSubject) {
    3119             :       // A particular device or devices were chosen by the user.
    3120             :       // NOTE: does not allow setting a device to null; assumes nullptr
    3121           0 :       nsCOMPtr<nsIArray> array(do_QueryInterface(aSubject));
    3122           0 :       MOZ_ASSERT(array);
    3123           0 :       uint32_t len = 0;
    3124           0 :       array->GetLength(&len);
    3125           0 :       bool videoFound = false, audioFound = false;
    3126           0 :       for (uint32_t i = 0; i < len; i++) {
    3127           0 :         nsCOMPtr<nsIMediaDevice> device;
    3128           0 :         array->QueryElementAt(i, NS_GET_IID(nsIMediaDevice),
    3129           0 :                               getter_AddRefs(device));
    3130           0 :         MOZ_ASSERT(device); // shouldn't be returning anything else...
    3131           0 :         if (device) {
    3132           0 :           nsString type;
    3133           0 :           device->GetType(type);
    3134           0 :           if (type.EqualsLiteral("video")) {
    3135           0 :             if (!videoFound) {
    3136           0 :               task->SetVideoDevice(static_cast<VideoDevice*>(device.get()));
    3137           0 :               videoFound = true;
    3138             :             }
    3139           0 :           } else if (type.EqualsLiteral("audio")) {
    3140           0 :             if (!audioFound) {
    3141           0 :               task->SetAudioDevice(static_cast<AudioDevice*>(device.get()));
    3142           0 :               audioFound = true;
    3143             :             }
    3144             :           } else {
    3145           0 :             NS_WARNING("Unknown device type in getUserMedia");
    3146             :           }
    3147             :         }
    3148             :       }
    3149           0 :       bool needVideo = IsOn(task->GetConstraints().mVideo);
    3150           0 :       bool needAudio = IsOn(task->GetConstraints().mAudio);
    3151           0 :       MOZ_ASSERT(needVideo || needAudio);
    3152             : 
    3153           0 :       if ((needVideo && !videoFound) || (needAudio && !audioFound)) {
    3154           0 :         task->Denied(NS_LITERAL_STRING("NotAllowedError"));
    3155           0 :         return NS_OK;
    3156             :       }
    3157             :     }
    3158             : 
    3159           0 :     if (sInShutdown) {
    3160           0 :       return task->Denied(NS_LITERAL_STRING("In shutdown"));
    3161             :     }
    3162             :     // Reuse the same thread to save memory.
    3163           0 :     MediaManager::PostTask(task.forget());
    3164           0 :     return NS_OK;
    3165             : 
    3166           0 :   } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
    3167           0 :     nsString errorMessage(NS_LITERAL_STRING("NotAllowedError"));
    3168             : 
    3169           0 :     if (aSubject) {
    3170           0 :       nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
    3171           0 :       MOZ_ASSERT(msg);
    3172           0 :       msg->GetData(errorMessage);
    3173           0 :       if (errorMessage.IsEmpty())
    3174           0 :         errorMessage.AssignLiteral(u"InternalError");
    3175             :     }
    3176             : 
    3177           0 :     nsString key(aData);
    3178           0 :     RefPtr<GetUserMediaTask> task;
    3179           0 :     mActiveCallbacks.Remove(key, getter_AddRefs(task));
    3180           0 :     if (task) {
    3181           0 :       task->Denied(errorMessage);
    3182             :       nsTArray<nsString>* array;
    3183           0 :       if (!mCallIds.Get(task->GetWindowID(), &array)) {
    3184           0 :         return NS_OK;
    3185             :       }
    3186           0 :       array->RemoveElement(key);
    3187           0 :       SendPendingGUMRequest();
    3188             :     }
    3189           0 :     return NS_OK;
    3190             : 
    3191           0 :   } else if (!strcmp(aTopic, "getUserMedia:revoke")) {
    3192             :     nsresult rv;
    3193             :     // may be windowid or screen:windowid
    3194           0 :     nsDependentString data(aData);
    3195           0 :     if (Substring(data, 0, strlen("screen:")).EqualsLiteral("screen:")) {
    3196           0 :       uint64_t windowID = PromiseFlatString(Substring(data, strlen("screen:"))).ToInteger64(&rv);
    3197           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    3198           0 :       if (NS_SUCCEEDED(rv)) {
    3199           0 :         LOG(("Revoking Screen/windowCapture access for window %" PRIu64, windowID));
    3200           0 :         StopScreensharing(windowID);
    3201             :       }
    3202             :     } else {
    3203           0 :       uint64_t windowID = nsString(aData).ToInteger64(&rv);
    3204           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    3205           0 :       if (NS_SUCCEEDED(rv)) {
    3206           0 :         LOG(("Revoking MediaCapture access for window %" PRIu64, windowID));
    3207           0 :         OnNavigation(windowID);
    3208             :       }
    3209             :     }
    3210           0 :     return NS_OK;
    3211             :   }
    3212             : #ifdef MOZ_WIDGET_GONK
    3213             :   else if (!strcmp(aTopic, "phone-state-changed")) {
    3214             :     nsString state(aData);
    3215             :     nsresult rv;
    3216             :     uint32_t phoneState = state.ToInteger(&rv);
    3217             : 
    3218             :     if (NS_SUCCEEDED(rv) && phoneState == nsIAudioManager::PHONE_STATE_IN_CALL) {
    3219             :       StopMediaStreams();
    3220             :     }
    3221             :     return NS_OK;
    3222             :   }
    3223             : #endif
    3224             : 
    3225           0 :   return NS_OK;
    3226             : }
    3227             : 
    3228             : nsresult
    3229           0 : MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray)
    3230             : {
    3231           0 :   MOZ_ASSERT(aArray);
    3232             : 
    3233           0 :   nsCOMPtr<nsIMutableArray> array = nsArray::Create();
    3234             : 
    3235           0 :   for (auto iter = mActiveWindows.Iter(); !iter.Done(); iter.Next()) {
    3236           0 :     const uint64_t& id = iter.Key();
    3237           0 :     RefPtr<GetUserMediaWindowListener> winListener = iter.UserData();
    3238           0 :     if (!winListener) {
    3239           0 :       continue;
    3240             :     }
    3241             : 
    3242             :     nsPIDOMWindowInner* window =
    3243           0 :       nsGlobalWindow::GetInnerWindowWithId(id)->AsInner();
    3244           0 :     MOZ_ASSERT(window);
    3245             :     // XXXkhuey ...
    3246           0 :     if (!window) {
    3247           0 :       continue;
    3248             :     }
    3249             : 
    3250           0 :     if (winListener->CapturingVideo() || winListener->CapturingAudio() ||
    3251           0 :         winListener->CapturingScreen() || winListener->CapturingWindow() ||
    3252           0 :         winListener->CapturingApplication()) {
    3253           0 :       array->AppendElement(window, /*weak =*/ false);
    3254             :     }
    3255             :   }
    3256             : 
    3257           0 :   array.forget(aArray);
    3258           0 :   return NS_OK;
    3259             : }
    3260             : 
    3261             : // XXX flags might be better...
    3262             : struct CaptureWindowStateData {
    3263             :   bool *mVideo;
    3264             :   bool *mAudio;
    3265             :   bool *mScreenShare;
    3266             :   bool *mWindowShare;
    3267             :   bool *mAppShare;
    3268             :   bool *mBrowserShare;
    3269             : };
    3270             : 
    3271             : static void
    3272           0 : CaptureWindowStateCallback(MediaManager *aThis,
    3273             :                            uint64_t aWindowID,
    3274             :                            GetUserMediaWindowListener  *aListener,
    3275             :                            void *aData)
    3276             : {
    3277           0 :   struct CaptureWindowStateData *data = (struct CaptureWindowStateData *) aData;
    3278             : 
    3279           0 :   if (!aListener) {
    3280           0 :     return;
    3281             :   }
    3282             : 
    3283           0 :   if (aListener->CapturingVideo()) {
    3284           0 :     *data->mVideo = true;
    3285             :   }
    3286           0 :   if (aListener->CapturingAudio()) {
    3287           0 :     *data->mAudio = true;
    3288             :   }
    3289           0 :   if (aListener->CapturingScreen()) {
    3290           0 :     *data->mScreenShare = true;
    3291             :   }
    3292           0 :   if (aListener->CapturingWindow()) {
    3293           0 :     *data->mWindowShare = true;
    3294             :   }
    3295           0 :   if (aListener->CapturingApplication()) {
    3296           0 :     *data->mAppShare = true;
    3297             :   }
    3298           0 :   if (aListener->CapturingBrowser()) {
    3299           0 :     *data->mBrowserShare = true;
    3300             :   }
    3301             : }
    3302             : 
    3303             : NS_IMETHODIMP
    3304           0 : MediaManager::MediaCaptureWindowState(nsIDOMWindow* aWindow, bool* aVideo,
    3305             :                                       bool* aAudio, bool *aScreenShare,
    3306             :                                       bool* aWindowShare, bool *aAppShare,
    3307             :                                       bool *aBrowserShare)
    3308             : {
    3309           0 :   MOZ_ASSERT(NS_IsMainThread());
    3310             :   struct CaptureWindowStateData data;
    3311           0 :   data.mVideo = aVideo;
    3312           0 :   data.mAudio = aAudio;
    3313           0 :   data.mScreenShare = aScreenShare;
    3314           0 :   data.mWindowShare = aWindowShare;
    3315           0 :   data.mAppShare = aAppShare;
    3316           0 :   data.mBrowserShare = aBrowserShare;
    3317             : 
    3318           0 :   *aVideo = false;
    3319           0 :   *aAudio = false;
    3320           0 :   *aScreenShare = false;
    3321           0 :   *aWindowShare = false;
    3322           0 :   *aAppShare = false;
    3323           0 :   *aBrowserShare = false;
    3324             : 
    3325           0 :   nsCOMPtr<nsPIDOMWindowInner> piWin = do_QueryInterface(aWindow);
    3326           0 :   if (piWin) {
    3327           0 :     IterateWindowListeners(piWin, CaptureWindowStateCallback, &data);
    3328             :   }
    3329             : #ifdef DEBUG
    3330           0 :   LOG(("%s: window %" PRIu64 " capturing %s %s %s %s %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
    3331             :        *aVideo ? "video" : "", *aAudio ? "audio" : "",
    3332             :        *aScreenShare ? "screenshare" : "",  *aWindowShare ? "windowshare" : "",
    3333             :        *aAppShare ? "appshare" : "", *aBrowserShare ? "browsershare" : ""));
    3334             : #endif
    3335           0 :   return NS_OK;
    3336             : }
    3337             : 
    3338             : NS_IMETHODIMP
    3339           0 : MediaManager::SanitizeDeviceIds(int64_t aSinceWhen)
    3340             : {
    3341           0 :   MOZ_ASSERT(NS_IsMainThread());
    3342           0 :   LOG(("%s: sinceWhen = %" PRId64, __FUNCTION__, aSinceWhen));
    3343             : 
    3344           0 :   media::SanitizeOriginKeys(aSinceWhen, false); // we fire and forget
    3345           0 :   return NS_OK;
    3346             : }
    3347             : 
    3348             : static void
    3349           0 : StopScreensharingCallback(MediaManager *aThis,
    3350             :                           uint64_t aWindowID,
    3351             :                           GetUserMediaWindowListener *aListener,
    3352             :                           void *aData)
    3353             : {
    3354           0 :   if (!aListener) {
    3355           0 :     return;
    3356             :   }
    3357             : 
    3358           0 :   aListener->StopSharing();
    3359             : }
    3360             : 
    3361             : void
    3362           0 : MediaManager::StopScreensharing(uint64_t aWindowID)
    3363             : {
    3364             :   // We need to stop window/screensharing for all streams in all innerwindows that
    3365             :   // correspond to that outerwindow.
    3366             : 
    3367           0 :   auto* window = nsGlobalWindow::GetInnerWindowWithId(aWindowID);
    3368           0 :   if (!window) {
    3369           0 :     return;
    3370             :   }
    3371           0 :   IterateWindowListeners(window->AsInner(), &StopScreensharingCallback, nullptr);
    3372             : }
    3373             : 
    3374             : // lets us do all sorts of things to the listeners
    3375             : void
    3376           0 : MediaManager::IterateWindowListeners(nsPIDOMWindowInner* aWindow,
    3377             :                                      WindowListenerCallback aCallback,
    3378             :                                      void *aData)
    3379             : {
    3380             :   // Iterate the docshell tree to find all the child windows, and for each
    3381             :   // invoke the callback
    3382           0 :   if (aWindow) {
    3383             :     {
    3384           0 :       uint64_t windowID = aWindow->WindowID();
    3385           0 :       GetUserMediaWindowListener* listener = GetWindowListener(windowID);
    3386           0 :       (*aCallback)(this, windowID, listener, aData);
    3387             :       // NB: `listener` might have been destroyed.
    3388             :     }
    3389             : 
    3390             :     // iterate any children of *this* window (iframes, etc)
    3391           0 :     nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
    3392           0 :     if (docShell) {
    3393             :       int32_t i, count;
    3394           0 :       docShell->GetChildCount(&count);
    3395           0 :       for (i = 0; i < count; ++i) {
    3396           0 :         nsCOMPtr<nsIDocShellTreeItem> item;
    3397           0 :         docShell->GetChildAt(i, getter_AddRefs(item));
    3398           0 :         nsCOMPtr<nsPIDOMWindowOuter> winOuter = item ? item->GetWindow() : nullptr;
    3399             : 
    3400           0 :         if (winOuter) {
    3401           0 :           IterateWindowListeners(winOuter->GetCurrentInnerWindow(),
    3402           0 :                                  aCallback, aData);
    3403             :         }
    3404             :       }
    3405             :     }
    3406             :   }
    3407           0 : }
    3408             : 
    3409             : 
    3410             : void
    3411           0 : MediaManager::StopMediaStreams()
    3412             : {
    3413           0 :   nsCOMPtr<nsIArray> array;
    3414           0 :   GetActiveMediaCaptureWindows(getter_AddRefs(array));
    3415             :   uint32_t len;
    3416           0 :   array->GetLength(&len);
    3417           0 :   for (uint32_t i = 0; i < len; i++) {
    3418           0 :     nsCOMPtr<nsPIDOMWindowInner> win;
    3419           0 :     array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner),
    3420           0 :                           getter_AddRefs(win));
    3421           0 :     if (win) {
    3422           0 :       OnNavigation(win->WindowID());
    3423             :     }
    3424             :   }
    3425           0 : }
    3426             : 
    3427             : bool
    3428           0 : MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId)
    3429             : {
    3430             :   // Does page currently have a gUM stream active?
    3431             : 
    3432           0 :   nsCOMPtr<nsIArray> array;
    3433           0 :   GetActiveMediaCaptureWindows(getter_AddRefs(array));
    3434             :   uint32_t len;
    3435           0 :   array->GetLength(&len);
    3436           0 :   for (uint32_t i = 0; i < len; i++) {
    3437           0 :     nsCOMPtr<nsPIDOMWindowInner> win;
    3438           0 :     array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner),
    3439           0 :                           getter_AddRefs(win));
    3440           0 :     if (win && win->WindowID() == aWindowId) {
    3441           0 :       return true;
    3442             :     }
    3443             :   }
    3444             : 
    3445             :   // Or are persistent permissions (audio or video) granted?
    3446             : 
    3447           0 :   auto* window = nsGlobalWindow::GetInnerWindowWithId(aWindowId);
    3448           0 :   if (NS_WARN_IF(!window)) {
    3449           0 :     return false;
    3450             :   }
    3451             :   // Check if this site has persistent permissions.
    3452             :   nsresult rv;
    3453             :   nsCOMPtr<nsIPermissionManager> mgr =
    3454           0 :     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
    3455           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3456           0 :     return false; // no permission manager no permissions!
    3457             :   }
    3458             : 
    3459           0 :   uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
    3460           0 :   uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
    3461             :   {
    3462           0 :     auto* principal = window->GetExtantDoc()->NodePrincipal();
    3463           0 :     rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
    3464           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3465           0 :       return false;
    3466             :     }
    3467           0 :     rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
    3468           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3469           0 :       return false;
    3470             :     }
    3471             :   }
    3472           0 :   return audio == nsIPermissionManager::ALLOW_ACTION ||
    3473           0 :          video == nsIPermissionManager::ALLOW_ACTION;
    3474             : }
    3475             : 
    3476           0 : SourceListener::SourceListener()
    3477             :   : mActivated(false)
    3478             :   , mStopped(false)
    3479             :   , mFinished(false)
    3480             :   , mRemoved(false)
    3481             :   , mAudioStopped(false)
    3482             :   , mVideoStopped(false)
    3483             :   , mMainThreadCheck(nullptr)
    3484             :   , mPrincipalHandle(PRINCIPAL_HANDLE_NONE)
    3485           0 :   , mWindowListener(nullptr)
    3486           0 : {}
    3487             : 
    3488             : void
    3489           0 : SourceListener::Register(GetUserMediaWindowListener* aListener)
    3490             : {
    3491           0 :   LOG(("SourceListener %p registering with window listener %p", this, aListener));
    3492             : 
    3493           0 :   if (mWindowListener) {
    3494           0 :     MOZ_ASSERT(false, "Already registered");
    3495             :     return;
    3496             :   }
    3497           0 :   if (mActivated) {
    3498           0 :     MOZ_ASSERT(false, "Already activated");
    3499             :     return;
    3500             :   }
    3501           0 :   if (!aListener) {
    3502           0 :     MOZ_ASSERT(false, "No listener");
    3503             :     return;
    3504             :   }
    3505           0 :   mPrincipalHandle = aListener->GetPrincipalHandle();
    3506           0 :   mWindowListener = aListener;
    3507           0 : }
    3508             : 
    3509             : void
    3510           0 : SourceListener::Activate(SourceMediaStream* aStream,
    3511             :                          AudioDevice* aAudioDevice,
    3512             :                          VideoDevice* aVideoDevice)
    3513             : {
    3514           0 :   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
    3515             : 
    3516           0 :   LOG(("SourceListener %p activating audio=%p video=%p", this, aAudioDevice, aVideoDevice));
    3517             : 
    3518           0 :   if (mActivated) {
    3519           0 :     MOZ_ASSERT(false, "Already activated");
    3520             :     return;
    3521             :   }
    3522             : 
    3523           0 :   mActivated = true;
    3524           0 :   mMainThreadCheck = GetCurrentVirtualThread();
    3525           0 :   mStream = aStream;
    3526           0 :   mAudioDevice = aAudioDevice;
    3527           0 :   mVideoDevice = aVideoDevice;
    3528           0 :   mStream->AddListener(this);
    3529           0 : }
    3530             : 
    3531             : void
    3532           0 : SourceListener::Stop()
    3533             : {
    3534           0 :   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
    3535             : 
    3536           0 :   if (mStopped) {
    3537           0 :     return;
    3538             :   }
    3539             : 
    3540           0 :   LOG(("SourceListener %p stopping", this));
    3541             : 
    3542             :   // StopSharing() has some special logic, at least for audio capture.
    3543             :   // It must be called when all tracks have stopped, before setting mStopped.
    3544           0 :   StopSharing();
    3545             : 
    3546           0 :   mStopped = true;
    3547             : 
    3548           0 :   if (mAudioDevice && !mAudioStopped) {
    3549           0 :     StopTrack(kAudioTrack);
    3550             :   }
    3551           0 :   if (mVideoDevice && !mVideoStopped) {
    3552           0 :     StopTrack(kVideoTrack);
    3553             :   }
    3554           0 :   RefPtr<SourceMediaStream> source = GetSourceStream();
    3555           0 :   MediaManager::PostTask(NewTaskFrom([source]() {
    3556           0 :     MOZ_ASSERT(MediaManager::IsInMediaThread());
    3557           0 :     source->EndAllTrackAndFinish();
    3558           0 :   }));
    3559             : }
    3560             : 
    3561             : void
    3562           0 : SourceListener::Remove()
    3563             : {
    3564           0 :   MOZ_ASSERT(NS_IsMainThread());
    3565           0 :   if (!mStream || mRemoved) {
    3566           0 :     return;
    3567             :   }
    3568             : 
    3569           0 :   LOG(("SourceListener %p removed on purpose, mFinished = %d", this, (int) mFinished));
    3570           0 :   mRemoved = true; // RemoveListener is async, avoid races
    3571           0 :   mWindowListener = nullptr;
    3572             : 
    3573             :   // If it's destroyed, don't call - listener will be removed and we'll be notified!
    3574           0 :   if (!mStream->IsDestroyed()) {
    3575           0 :     mStream->RemoveListener(this);
    3576             :   }
    3577             : }
    3578             : 
    3579             : void
    3580           0 : SourceListener::StopTrack(TrackID aTrackID)
    3581             : {
    3582           0 :   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
    3583             : 
    3584           0 :   RefPtr<MediaDevice> device;
    3585           0 :   RefPtr<SourceMediaStream> source;
    3586             : 
    3587           0 :   switch (aTrackID) {
    3588             :     case kAudioTrack: {
    3589           0 :       LOG(("SourceListener %p stopping audio track %d", this, aTrackID));
    3590           0 :       if (!mAudioDevice) {
    3591           0 :         NS_ASSERTION(false, "Can't stop audio. No device.");
    3592           0 :         return;
    3593             :       }
    3594           0 :       if (mAudioStopped) {
    3595             :         // Audio already stopped
    3596           0 :         return;
    3597             :       }
    3598           0 :       device = mAudioDevice;
    3599           0 :       source = GetSourceStream();
    3600           0 :       mAudioStopped = true;
    3601           0 :       break;
    3602             :     }
    3603             :     case kVideoTrack: {
    3604           0 :       LOG(("SourceListener %p stopping video track %d", this, aTrackID));
    3605           0 :       if (!mVideoDevice) {
    3606           0 :         NS_ASSERTION(false, "Can't stop video. No device.");
    3607           0 :         return;
    3608             :       }
    3609           0 :       if (mVideoStopped) {
    3610             :         // Video already stopped
    3611           0 :         return;
    3612             :       }
    3613           0 :       device = mVideoDevice;
    3614           0 :       source = GetSourceStream();
    3615           0 :       mVideoStopped = true;
    3616           0 :       break;
    3617             :     }
    3618             :     default: {
    3619           0 :       MOZ_ASSERT(false, "Unknown track id");
    3620             :       return;
    3621             :     }
    3622             :   }
    3623             : 
    3624           0 :   MediaManager::PostTask(NewTaskFrom([device, source, aTrackID]() {
    3625           0 :     device->GetSource()->Stop(source, aTrackID);
    3626           0 :     device->Deallocate();
    3627           0 :   }));
    3628             : 
    3629           0 :   if ((!mAudioDevice || mAudioStopped) &&
    3630           0 :       (!mVideoDevice || mVideoStopped)) {
    3631           0 :     LOG(("SourceListener %p this was the last track stopped", this));
    3632           0 :     Stop();
    3633             :   }
    3634             : 
    3635           0 :   if (!mWindowListener) {
    3636           0 :     MOZ_ASSERT(false, "Should still have window listener");
    3637             :     return;
    3638             :   }
    3639           0 :   mWindowListener->NotifySourceTrackStopped();
    3640             : }
    3641             : 
    3642             : void
    3643           0 : SourceListener::StopSharing()
    3644             : {
    3645           0 :   MOZ_ASSERT(NS_IsMainThread());
    3646           0 :   MOZ_RELEASE_ASSERT(mWindowListener);
    3647             : 
    3648           0 :   if (mStopped) {
    3649           0 :     return;
    3650             :   }
    3651             : 
    3652           0 :   LOG(("SourceListener %p StopSharing", this));
    3653             : 
    3654           0 :   if (mVideoDevice &&
    3655           0 :       (mVideoDevice->GetMediaSource() == MediaSourceEnum::Screen ||
    3656           0 :        mVideoDevice->GetMediaSource() == MediaSourceEnum::Application ||
    3657           0 :        mVideoDevice->GetMediaSource() == MediaSourceEnum::Window)) {
    3658             :     // We want to stop the whole stream if there's no audio;
    3659             :     // just the video track if we have both.
    3660             :     // StopTrack figures this out for us.
    3661           0 :     StopTrack(kVideoTrack);
    3662             :   }
    3663           0 :   if (mAudioDevice &&
    3664           0 :       mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) {
    3665           0 :     uint64_t windowID = mWindowListener->WindowID();
    3666           0 :     nsCOMPtr<nsPIDOMWindowInner> window = nsGlobalWindow::GetInnerWindowWithId(windowID)->AsInner();
    3667           0 :     MOZ_RELEASE_ASSERT(window);
    3668           0 :     window->SetAudioCapture(false);
    3669             :     MediaStreamGraph* graph =
    3670           0 :       MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
    3671           0 :                                     dom::AudioChannel::Normal, window);
    3672           0 :     graph->UnregisterCaptureStreamForWindow(windowID);
    3673           0 :     mStream->Destroy();
    3674             :   }
    3675             : }
    3676             : 
    3677             : SourceMediaStream*
    3678           0 : SourceListener::GetSourceStream()
    3679             : {
    3680           0 :   NS_ASSERTION(mStream,"Getting stream from never-activated SourceListener");
    3681           0 :   if (!mStream) {
    3682           0 :     return nullptr;
    3683             :   }
    3684           0 :   return mStream->AsSourceStream();
    3685             : }
    3686             : 
    3687             : void
    3688           0 : SourceListener::GetSettings(dom::MediaTrackSettings& aOutSettings, TrackID aTrackID)
    3689             : {
    3690           0 :   switch (aTrackID) {
    3691             :     case kVideoTrack: {
    3692           0 :       if (mVideoDevice) {
    3693           0 :         mVideoDevice->GetSource()->GetSettings(aOutSettings);
    3694             :       }
    3695           0 :       break;
    3696             :     }
    3697             :     case kAudioTrack: {
    3698           0 :       if (mAudioDevice) {
    3699           0 :         mAudioDevice->GetSource()->GetSettings(aOutSettings);
    3700             :       }
    3701           0 :       break;
    3702             :     }
    3703             :     default: {
    3704           0 :       MOZ_ASSERT(false, "Unknown track id");
    3705             :     }
    3706             :   }
    3707           0 : }
    3708             : 
    3709             : // Proxy NotifyPull() to sources
    3710             : void
    3711           0 : SourceListener::NotifyPull(MediaStreamGraph* aGraph,
    3712             :                            StreamTime aDesiredTime)
    3713             : {
    3714             :   // Currently audio sources ignore NotifyPull, but they could
    3715             :   // watch it especially for fake audio.
    3716           0 :   if (mAudioDevice) {
    3717           0 :     mAudioDevice->GetSource()->NotifyPull(aGraph, mStream, kAudioTrack,
    3718           0 :                                           aDesiredTime, mPrincipalHandle);
    3719             :   }
    3720           0 :   if (mVideoDevice) {
    3721           0 :     mVideoDevice->GetSource()->NotifyPull(aGraph, mStream, kVideoTrack,
    3722           0 :                                           aDesiredTime, mPrincipalHandle);
    3723             :   }
    3724           0 : }
    3725             : 
    3726             : void
    3727           0 : SourceListener::NotifyEvent(MediaStreamGraph* aGraph,
    3728             :                             MediaStreamGraphEvent aEvent)
    3729             : {
    3730           0 :   nsCOMPtr<nsIEventTarget> target;
    3731             : 
    3732           0 :   switch (aEvent) {
    3733             :     case MediaStreamGraphEvent::EVENT_FINISHED:
    3734           0 :       target = GetMainThreadEventTarget();
    3735           0 :       if (NS_WARN_IF(!target)) {
    3736           0 :         NS_ASSERTION(false, "Mainthread not available; running on current thread");
    3737             :         // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
    3738           0 :         MOZ_RELEASE_ASSERT(mMainThreadCheck == GetCurrentVirtualThread());
    3739           0 :         NotifyFinished();
    3740           0 :         return;
    3741             :       }
    3742           0 :       target->Dispatch(NewRunnableMethod("SourceListener::NotifyFinished",
    3743             :                                          this,
    3744             :                                          &SourceListener::NotifyFinished),
    3745           0 :                        NS_DISPATCH_NORMAL);
    3746           0 :       break;
    3747             :     case MediaStreamGraphEvent::EVENT_REMOVED:
    3748           0 :       target = GetMainThreadEventTarget();
    3749           0 :       if (NS_WARN_IF(!target)) {
    3750           0 :         NS_ASSERTION(false, "Mainthread not available; running on current thread");
    3751             :         // Ensure this really *was* MainThread (NS_GetCurrentThread won't work)
    3752           0 :         MOZ_RELEASE_ASSERT(mMainThreadCheck == GetCurrentVirtualThread());
    3753           0 :         NotifyRemoved();
    3754           0 :         return;
    3755             :       }
    3756           0 :       target->Dispatch(NewRunnableMethod("SourceListener::NotifyRemoved",
    3757             :                                          this,
    3758             :                                          &SourceListener::NotifyRemoved),
    3759           0 :                        NS_DISPATCH_NORMAL);
    3760           0 :       break;
    3761             :     case MediaStreamGraphEvent::EVENT_HAS_DIRECT_LISTENERS:
    3762           0 :       NotifyDirectListeners(aGraph, true);
    3763           0 :       break;
    3764             :     case MediaStreamGraphEvent::EVENT_HAS_NO_DIRECT_LISTENERS:
    3765           0 :       NotifyDirectListeners(aGraph, false);
    3766           0 :       break;
    3767             :     default:
    3768           0 :       break;
    3769             :   }
    3770             : }
    3771             : 
    3772             : void
    3773           0 : SourceListener::NotifyFinished()
    3774             : {
    3775           0 :   MOZ_ASSERT(NS_IsMainThread());
    3776           0 :   mFinished = true;
    3777           0 :   if (!mWindowListener) {
    3778             :     // Removed explicitly before finished.
    3779           0 :     return;
    3780             :   }
    3781             : 
    3782           0 :   LOG(("SourceListener %p NotifyFinished", this));
    3783             : 
    3784           0 :   Stop(); // we know it's been activated
    3785           0 :   mWindowListener->Remove(this);
    3786             : }
    3787             : 
    3788             : void
    3789           0 : SourceListener::NotifyRemoved()
    3790             : {
    3791           0 :   MOZ_ASSERT(NS_IsMainThread());
    3792           0 :   LOG(("SourceListener removed, mFinished = %d", (int) mFinished));
    3793           0 :   mRemoved = true;
    3794             : 
    3795           0 :   if (!mFinished) {
    3796           0 :     NotifyFinished();
    3797             :   }
    3798             : 
    3799           0 :   mWindowListener = nullptr;
    3800           0 : }
    3801             : 
    3802             : void
    3803           0 : SourceListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
    3804             :                                       bool aHasListeners)
    3805             : {
    3806           0 :   if (!mVideoDevice) {
    3807           0 :     return;
    3808             :   }
    3809             : 
    3810           0 :   auto& videoDevice = mVideoDevice;
    3811           0 :   MediaManager::PostTask(NewTaskFrom([videoDevice, aHasListeners]() {
    3812           0 :     videoDevice->GetSource()->SetDirectListeners(aHasListeners);
    3813           0 :     return NS_OK;
    3814           0 :   }));
    3815             : }
    3816             : 
    3817             : bool
    3818           0 : SourceListener::CapturingVideo() const
    3819             : {
    3820           0 :   MOZ_ASSERT(NS_IsMainThread());
    3821           0 :   return mActivated && mVideoDevice && !mVideoStopped &&
    3822           0 :          !mVideoDevice->GetSource()->IsAvailable() &&
    3823           0 :          mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Camera &&
    3824           0 :          (!mVideoDevice->GetSource()->IsFake() ||
    3825           0 :           Preferences::GetBool("media.navigator.permission.fake"));
    3826             : }
    3827             : 
    3828             : bool
    3829           0 : SourceListener::CapturingAudio() const
    3830             : {
    3831           0 :   MOZ_ASSERT(NS_IsMainThread());
    3832           0 :   return mActivated && mAudioDevice && !mAudioStopped &&
    3833           0 :          !mAudioDevice->GetSource()->IsAvailable() &&
    3834           0 :          (!mAudioDevice->GetSource()->IsFake() ||
    3835           0 :           Preferences::GetBool("media.navigator.permission.fake"));
    3836             : }
    3837             : 
    3838             : bool
    3839           0 : SourceListener::CapturingScreen() const
    3840             : {
    3841           0 :   MOZ_ASSERT(NS_IsMainThread());
    3842           0 :   return mActivated && mVideoDevice && !mVideoStopped &&
    3843           0 :          !mVideoDevice->GetSource()->IsAvailable() &&
    3844           0 :          mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen;
    3845             : }
    3846             : 
    3847             : bool
    3848           0 : SourceListener::CapturingWindow() const
    3849             : {
    3850           0 :   MOZ_ASSERT(NS_IsMainThread());
    3851           0 :   return mActivated && mVideoDevice && !mVideoStopped &&
    3852           0 :          !mVideoDevice->GetSource()->IsAvailable() &&
    3853           0 :          mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window;
    3854             : }
    3855             : 
    3856             : bool
    3857           0 : SourceListener::CapturingApplication() const
    3858             : {
    3859           0 :   MOZ_ASSERT(NS_IsMainThread());
    3860           0 :   return mActivated && mVideoDevice && !mVideoStopped &&
    3861           0 :          !mVideoDevice->GetSource()->IsAvailable() &&
    3862           0 :          mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application;
    3863             : }
    3864             : 
    3865             : bool
    3866           0 : SourceListener::CapturingBrowser() const
    3867             : {
    3868           0 :   MOZ_ASSERT(NS_IsMainThread());
    3869           0 :   return mActivated && mVideoDevice && !mVideoStopped &&
    3870           0 :          !mVideoDevice->GetSource()->IsAvailable() &&
    3871           0 :          mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
    3872             : }
    3873             : 
    3874             : already_AddRefed<PledgeVoid>
    3875           0 : SourceListener::ApplyConstraintsToTrack(
    3876             :     nsPIDOMWindowInner* aWindow,
    3877             :     TrackID aTrackID,
    3878             :     const MediaTrackConstraints& aConstraintsPassedIn,
    3879             :     dom::CallerType aCallerType)
    3880             : {
    3881           0 :   MOZ_ASSERT(NS_IsMainThread());
    3882           0 :   RefPtr<PledgeVoid> p = new PledgeVoid();
    3883             : 
    3884             :   // XXX to support multiple tracks of a type in a stream, this should key off
    3885             :   // the TrackID and not just the type
    3886             :   RefPtr<AudioDevice> audioDevice =
    3887           0 :     aTrackID == kAudioTrack ? mAudioDevice.get() : nullptr;
    3888             :   RefPtr<VideoDevice> videoDevice =
    3889           0 :     aTrackID == kVideoTrack ? mVideoDevice.get() : nullptr;
    3890             : 
    3891           0 :   if (mStopped || (!audioDevice && !videoDevice))
    3892             :   {
    3893           0 :     LOG(("gUM track %d applyConstraints, but we don't have type %s",
    3894             :          aTrackID, aTrackID == kAudioTrack ? "audio" : "video"));
    3895           0 :     p->Resolve(false);
    3896           0 :     return p.forget();
    3897             :   }
    3898           0 :   MediaTrackConstraints c(aConstraintsPassedIn); // use a modifiable copy
    3899             : 
    3900             :   MediaConstraintsHelper::ConvertOldWithWarning(c.mMozAutoGainControl,
    3901             :                                                 c.mAutoGainControl,
    3902             :                                                 "MozAutoGainControlWarning",
    3903           0 :                                                 aWindow);
    3904             :   MediaConstraintsHelper::ConvertOldWithWarning(c.mMozNoiseSuppression,
    3905             :                                                 c.mNoiseSuppression,
    3906             :                                                 "MozNoiseSuppressionWarning",
    3907           0 :                                                 aWindow);
    3908             : 
    3909           0 :   RefPtr<MediaManager> mgr = MediaManager::GetInstance();
    3910           0 :   uint32_t id = mgr->mOutstandingVoidPledges.Append(*p);
    3911           0 :   uint64_t windowId = aWindow->WindowID();
    3912           0 :   bool isChrome = (aCallerType == dom::CallerType::System);
    3913             : 
    3914           0 :   MediaManager::PostTask(NewTaskFrom([id, windowId,
    3915             :                                       audioDevice, videoDevice,
    3916           0 :                                       c, isChrome]() mutable {
    3917           0 :     MOZ_ASSERT(MediaManager::IsInMediaThread());
    3918           0 :     RefPtr<MediaManager> mgr = MediaManager::GetInstance();
    3919           0 :     const char* badConstraint = nullptr;
    3920           0 :     nsresult rv = NS_OK;
    3921             : 
    3922           0 :     if (audioDevice) {
    3923           0 :       rv = audioDevice->Restart(c, mgr->mPrefs, &badConstraint);
    3924           0 :       if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
    3925           0 :         nsTArray<RefPtr<AudioDevice>> audios;
    3926           0 :         audios.AppendElement(audioDevice);
    3927           0 :         badConstraint = MediaConstraintsHelper::SelectSettings(
    3928           0 :             NormalizedConstraints(c), audios, isChrome);
    3929             :       }
    3930             :     } else {
    3931           0 :       rv = videoDevice->Restart(c, mgr->mPrefs, &badConstraint);
    3932           0 :       if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
    3933           0 :         nsTArray<RefPtr<VideoDevice>> videos;
    3934           0 :         videos.AppendElement(videoDevice);
    3935           0 :         badConstraint = MediaConstraintsHelper::SelectSettings(
    3936           0 :             NormalizedConstraints(c), videos, isChrome);
    3937             :       }
    3938             :     }
    3939           0 :     NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv,
    3940           0 :                                              badConstraint]() mutable {
    3941           0 :       MOZ_ASSERT(NS_IsMainThread());
    3942           0 :       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
    3943           0 :       if (!mgr) {
    3944           0 :         return NS_OK;
    3945             :       }
    3946           0 :       RefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
    3947           0 :       if (p) {
    3948           0 :         if (NS_SUCCEEDED(rv)) {
    3949           0 :           p->Resolve(false);
    3950             :         } else {
    3951           0 :           auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId);
    3952           0 :           if (window) {
    3953           0 :             if (badConstraint) {
    3954           0 :               nsString constraint;
    3955           0 :               constraint.AssignASCII(badConstraint);
    3956             :               RefPtr<MediaStreamError> error =
    3957           0 :                   new MediaStreamError(window->AsInner(),
    3958           0 :                                        NS_LITERAL_STRING("OverconstrainedError"),
    3959           0 :                                        NS_LITERAL_STRING(""),
    3960           0 :                                        constraint);
    3961           0 :               p->Reject(error);
    3962             :             } else {
    3963             :               RefPtr<MediaStreamError> error =
    3964           0 :                   new MediaStreamError(window->AsInner(),
    3965           0 :                                        NS_LITERAL_STRING("InternalError"));
    3966           0 :               p->Reject(error);
    3967             :             }
    3968             :           }
    3969             :         }
    3970             :       }
    3971           0 :       return NS_OK;
    3972           0 :     }));
    3973           0 :   }));
    3974           0 :   return p.forget();
    3975             : }
    3976             : 
    3977             : PrincipalHandle
    3978           0 : SourceListener::GetPrincipalHandle() const
    3979             : {
    3980           0 :   return mPrincipalHandle;
    3981             : }
    3982             : 
    3983             : // Doesn't kill audio
    3984             : void
    3985           0 : GetUserMediaWindowListener::StopSharing()
    3986             : {
    3987           0 :   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
    3988             : 
    3989           0 :   for (auto& source : mActiveListeners) {
    3990           0 :     source->StopSharing();
    3991             :   }
    3992           0 : }
    3993             : 
    3994             : void
    3995           0 : GetUserMediaWindowListener::NotifySourceTrackStopped()
    3996             : {
    3997           0 :   MOZ_ASSERT(NS_IsMainThread());
    3998             : 
    3999             :   // We wait until stable state before notifying chrome so chrome only does one
    4000             :   // update if more tracks are stopped in this event loop.
    4001             : 
    4002           0 :   if (mChromeNotificationTaskPosted) {
    4003           0 :     return;
    4004             :   }
    4005             : 
    4006             :   nsCOMPtr<nsIRunnable> runnable =
    4007           0 :     NewRunnableMethod("GetUserMediaWindowListener::NotifyChromeOfTrackStops",
    4008             :                       this,
    4009           0 :                       &GetUserMediaWindowListener::NotifyChromeOfTrackStops);
    4010           0 :   nsContentUtils::RunInStableState(runnable.forget());
    4011           0 :   mChromeNotificationTaskPosted = true;
    4012             : }
    4013             : 
    4014             : void
    4015           0 : GetUserMediaWindowListener::NotifyChromeOfTrackStops()
    4016             : {
    4017           0 :   MOZ_ASSERT(mChromeNotificationTaskPosted);
    4018           0 :   mChromeNotificationTaskPosted = false;
    4019             : 
    4020           0 :   NS_DispatchToMainThread(do_AddRef(new GetUserMediaNotificationEvent(
    4021           0 :     GetUserMediaNotificationEvent::STOPPING, mWindowID)));
    4022           0 : }
    4023             : 
    4024           0 : GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
    4025             :   GetUserMediaStatus aStatus,
    4026           0 :   uint64_t aWindowID)
    4027             :   : Runnable("GetUserMediaNotificationEvent")
    4028             :   , mStatus(aStatus)
    4029           0 :   , mWindowID(aWindowID)
    4030             : {
    4031           0 : }
    4032             : 
    4033           0 : GetUserMediaNotificationEvent::GetUserMediaNotificationEvent(
    4034             :   GetUserMediaStatus aStatus,
    4035             :   already_AddRefed<DOMMediaStream> aStream,
    4036             :   already_AddRefed<Refcountable<UniquePtr<OnTracksAvailableCallback>>>
    4037             :     aOnTracksAvailableCallback,
    4038             :   uint64_t aWindowID,
    4039           0 :   already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
    4040             :   : Runnable("GetUserMediaNotificationEvent")
    4041             :   , mStream(aStream)
    4042             :   , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
    4043             :   , mStatus(aStatus)
    4044             :   , mWindowID(aWindowID)
    4045           0 :   , mOnFailure(aError)
    4046             : {
    4047           0 : }
    4048           0 : GetUserMediaNotificationEvent::~GetUserMediaNotificationEvent()
    4049             : {
    4050           0 : }
    4051             : 
    4052             : NS_IMETHODIMP
    4053           0 : GetUserMediaNotificationEvent::Run()
    4054             : {
    4055           0 :   MOZ_ASSERT(NS_IsMainThread());
    4056             :   // Make sure mStream is cleared and our reference to the DOMMediaStream
    4057             :   // is dropped on the main thread, no matter what happens in this method.
    4058             :   // Otherwise this object might be destroyed off the main thread,
    4059             :   // releasing DOMMediaStream off the main thread, which is not allowed.
    4060           0 :   RefPtr<DOMMediaStream> stream = mStream.forget();
    4061             : 
    4062           0 :   nsString msg;
    4063           0 :   switch (mStatus) {
    4064             :   case STARTING:
    4065           0 :     msg = NS_LITERAL_STRING("starting");
    4066           0 :     stream->OnTracksAvailable(mOnTracksAvailableCallback->release());
    4067           0 :     break;
    4068             :   case STOPPING:
    4069           0 :     msg = NS_LITERAL_STRING("shutdown");
    4070           0 :     break;
    4071             :   }
    4072             : 
    4073           0 :   RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
    4074           0 :   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
    4075             : 
    4076           0 :   return MediaManager::NotifyRecordingStatusChange(window->AsInner(), msg);
    4077             : }
    4078             : 
    4079             : } // namespace mozilla

Generated by: LCOV version 1.13