tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaManager.cpp (191515B)


      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 "AudioCaptureTrack.h"
     10 #include "AudioDeviceInfo.h"
     11 #include "AudioStreamTrack.h"
     12 #include "CubebDeviceEnumerator.h"
     13 #include "CubebInputStream.h"
     14 #include "MediaTimer.h"
     15 #include "MediaTrackConstraints.h"
     16 #include "MediaTrackGraph.h"
     17 #include "MediaTrackListener.h"
     18 #include "Tracing.h"
     19 #include "VideoStreamTrack.h"
     20 #include "VideoUtils.h"
     21 #include "mozilla/Base64.h"
     22 #include "mozilla/EventTargetCapability.h"
     23 #include "mozilla/MozPromise.h"
     24 #include "mozilla/NullPrincipal.h"
     25 #include "mozilla/PeerIdentity.h"
     26 #include "mozilla/PermissionDelegateHandler.h"
     27 #include "mozilla/Sprintf.h"
     28 #include "mozilla/StaticPrefs_media.h"
     29 #include "mozilla/dom/BindingDeclarations.h"
     30 #include "mozilla/dom/Document.h"
     31 #include "mozilla/dom/Element.h"
     32 #include "mozilla/dom/FeaturePolicyUtils.h"
     33 #include "mozilla/dom/File.h"
     34 #include "mozilla/dom/GetUserMediaRequestBinding.h"
     35 #include "mozilla/dom/MediaDeviceInfo.h"
     36 #include "mozilla/dom/MediaDevices.h"
     37 #include "mozilla/dom/MediaDevicesBinding.h"
     38 #include "mozilla/dom/MediaStreamBinding.h"
     39 #include "mozilla/dom/MediaStreamTrackBinding.h"
     40 #include "mozilla/dom/Promise.h"
     41 #include "mozilla/dom/UserActivation.h"
     42 #include "mozilla/dom/WindowContext.h"
     43 #include "mozilla/dom/WindowGlobalChild.h"
     44 #include "mozilla/glean/DomMediaWebrtcMetrics.h"
     45 #include "mozilla/ipc/BackgroundChild.h"
     46 #include "mozilla/ipc/PBackgroundChild.h"
     47 #include "mozilla/media/CamerasTypes.h"
     48 #include "mozilla/media/MediaChild.h"
     49 #include "mozilla/media/MediaTaskUtils.h"
     50 #include "nsAppDirectoryServiceDefs.h"
     51 #include "nsArray.h"
     52 #include "nsContentUtils.h"
     53 #include "nsGlobalWindowInner.h"
     54 #include "nsHashPropertyBag.h"
     55 #include "nsIEventTarget.h"
     56 #include "nsIPermissionManager.h"
     57 #include "nsIUUIDGenerator.h"
     58 #include "nsJSUtils.h"
     59 #include "nsNetCID.h"
     60 #include "nsNetUtil.h"
     61 #include "nsProxyRelease.h"
     62 #include "nspr.h"
     63 #include "nss.h"
     64 #include "pk11pub.h"
     65 
     66 /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
     67 #include "MediaEngineFake.h"
     68 #include "MediaEngineSource.h"
     69 #if defined(MOZ_WEBRTC)
     70 #  include "MediaEngineWebRTC.h"
     71 #  include "MediaEngineWebRTCAudio.h"
     72 #  include "browser_logging/WebRtcLog.h"
     73 #  include "libwebrtcglue/WebrtcTaskQueueWrapper.h"
     74 #  include "modules/audio_processing/include/audio_processing.h"
     75 #endif
     76 
     77 #if defined(XP_WIN)
     78 #  include <objbase.h>
     79 #endif
     80 
     81 // A specialization of nsMainThreadPtrHolder for
     82 // mozilla::dom::CallbackObjectHolder.  See documentation for
     83 // nsMainThreadPtrHolder in nsProxyRelease.h.  This specialization lets us avoid
     84 // wrapping the CallbackObjectHolder into a separate refcounted object.
     85 template <class WebIDLCallbackT, class XPCOMCallbackT>
     86 class nsMainThreadPtrHolder<
     87    mozilla::dom::CallbackObjectHolder<WebIDLCallbackT, XPCOMCallbackT>>
     88    final {
     89  typedef mozilla::dom::CallbackObjectHolder<WebIDLCallbackT, XPCOMCallbackT>
     90      Holder;
     91 
     92 public:
     93  nsMainThreadPtrHolder(const char* aName, Holder&& aHolder)
     94      : mHolder(std::move(aHolder))
     95 #ifndef RELEASE_OR_BETA
     96        ,
     97        mName(aName)
     98 #endif
     99  {
    100    MOZ_ASSERT(NS_IsMainThread());
    101  }
    102 
    103 private:
    104  // We can be released on any thread.
    105  ~nsMainThreadPtrHolder() {
    106    if (NS_IsMainThread()) {
    107      mHolder.Reset();
    108    } else if (mHolder.GetISupports()) {
    109      nsCOMPtr<nsIEventTarget> target = do_GetMainThread();
    110      MOZ_ASSERT(target);
    111      NS_ProxyRelease(
    112 #ifdef RELEASE_OR_BETA
    113          nullptr,
    114 #else
    115          mName,
    116 #endif
    117          target, mHolder.Forget());
    118    }
    119  }
    120 
    121 public:
    122  Holder* get() {
    123    // Nobody should be touching the raw pointer off-main-thread.
    124    if (MOZ_UNLIKELY(!NS_IsMainThread())) {
    125      NS_ERROR("Can't dereference nsMainThreadPtrHolder off main thread");
    126      MOZ_CRASH();
    127    }
    128    return &mHolder;
    129  }
    130 
    131  bool operator!() const { return !mHolder; }
    132 
    133  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsMainThreadPtrHolder<Holder>)
    134 
    135 private:
    136  // Our holder.
    137  Holder mHolder;
    138 
    139 #ifndef RELEASE_OR_BETA
    140  const char* mName = nullptr;
    141 #endif
    142 
    143  // Copy constructor and operator= not implemented. Once constructed, the
    144  // holder is immutable.
    145  Holder& operator=(const nsMainThreadPtrHolder& aOther) = delete;
    146  nsMainThreadPtrHolder(const nsMainThreadPtrHolder& aOther) = delete;
    147 };
    148 
    149 namespace mozilla {
    150 
    151 LazyLogModule gMediaManagerLog("MediaManager");
    152 #define LOG(...) MOZ_LOG(gMediaManagerLog, LogLevel::Debug, (__VA_ARGS__))
    153 
    154 class GetUserMediaStreamTask;
    155 class LocalTrackSource;
    156 class SelectAudioOutputTask;
    157 
    158 using camera::CamerasAccessStatus;
    159 using dom::BFCacheStatus;
    160 using dom::CallerType;
    161 using dom::ConstrainDOMStringParameters;
    162 using dom::ConstrainDoubleRange;
    163 using dom::ConstrainLongRange;
    164 using dom::DisplayMediaStreamConstraints;
    165 using dom::Document;
    166 using dom::Element;
    167 using dom::FeaturePolicyUtils;
    168 using dom::File;
    169 using dom::GetUserMediaRequest;
    170 using dom::MediaDeviceKind;
    171 using dom::MediaDevices;
    172 using dom::MediaSourceEnum;
    173 using dom::MediaStreamConstraints;
    174 using dom::MediaStreamError;
    175 using dom::MediaStreamTrack;
    176 using dom::MediaStreamTrackSource;
    177 using dom::MediaTrackCapabilities;
    178 using dom::MediaTrackConstraints;
    179 using dom::MediaTrackConstraintSet;
    180 using dom::MediaTrackSettings;
    181 using dom::OwningBooleanOrMediaTrackConstraints;
    182 using dom::OwningStringOrStringSequence;
    183 using dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters;
    184 using dom::Promise;
    185 using dom::Sequence;
    186 using dom::UserActivation;
    187 using dom::VideoResizeModeEnum;
    188 using dom::WindowGlobalChild;
    189 using ConstDeviceSetPromise = MediaManager::ConstDeviceSetPromise;
    190 using DeviceSetPromise = MediaManager::DeviceSetPromise;
    191 using LocalDevicePromise = MediaManager::LocalDevicePromise;
    192 using LocalDeviceSetPromise = MediaManager::LocalDeviceSetPromise;
    193 using LocalMediaDeviceSetRefCnt = MediaManager::LocalMediaDeviceSetRefCnt;
    194 using MediaDeviceSetRefCnt = MediaManager::MediaDeviceSetRefCnt;
    195 using media::NewRunnableFrom;
    196 using media::NewTaskFrom;
    197 using media::Refcountable;
    198 
    199 // Whether main thread actions of MediaManager shutdown (except for clearing
    200 // of sSingleton) have completed.
    201 static bool sHasMainThreadShutdown;
    202 
    203 struct DeviceState {
    204  DeviceState(RefPtr<LocalMediaDevice> aDevice,
    205              RefPtr<LocalTrackSource> aTrackSource, bool aOffWhileDisabled)
    206      : mOffWhileDisabled(aOffWhileDisabled),
    207        mDevice(std::move(aDevice)),
    208        mTrackSource(std::move(aTrackSource)) {
    209    MOZ_ASSERT(mDevice);
    210    MOZ_ASSERT(mTrackSource);
    211  }
    212 
    213  // true if we have allocated mDevice. When not allocated, we may not stop or
    214  // deallocate.
    215  // MainThread only.
    216  bool mAllocated = false;
    217 
    218  // true if we have stopped mDevice, this is a terminal state.
    219  // MainThread only.
    220  bool mStopped = false;
    221 
    222  // true if mDevice is currently enabled.
    223  // A device must be both enabled and unmuted to be turned on and capturing.
    224  // MainThread only.
    225  bool mDeviceEnabled = false;
    226 
    227  // true if mDevice is currently muted.
    228  // A device that is either muted or disabled is turned off and not capturing.
    229  // MainThread only.
    230  bool mDeviceMuted;
    231 
    232  // true if the application has currently enabled mDevice.
    233  // MainThread only.
    234  bool mTrackEnabled = false;
    235 
    236  // Time when the application last enabled mDevice.
    237  // MainThread only.
    238  TimeStamp mTrackEnabledTime;
    239 
    240  // true if an operation to Start() or Stop() mDevice has been dispatched to
    241  // the media thread and is not finished yet.
    242  // MainThread only.
    243  bool mOperationInProgress = false;
    244 
    245  // true if we are allowed to turn off the underlying source while all tracks
    246  // are disabled or muted.
    247  // MainThread only.
    248  bool mOffWhileDisabled = false;
    249 
    250  // Timer triggered by a MediaStreamTrackSource signaling that all tracks got
    251  // disabled. When the timer fires we initiate Stop()ing mDevice.
    252  // If set we allow dynamically stopping and starting mDevice.
    253  // Any thread.
    254  const RefPtr<MediaTimer<TimeStamp>> mDisableTimer =
    255      new MediaTimer<TimeStamp>();
    256 
    257  // The underlying device we keep state for. Always non-null.
    258  // Threadsafe access, but see method declarations for individual constraints.
    259  const RefPtr<LocalMediaDevice> mDevice;
    260 
    261  // The MediaStreamTrackSource for any tracks (original and clones) originating
    262  // from this device. Always non-null. Threadsafe access, but see method
    263  // declarations for individual constraints.
    264  const RefPtr<LocalTrackSource> mTrackSource;
    265 };
    266 
    267 /**
    268 * This mimics the capture state from nsIMediaManagerService.
    269 */
    270 enum class CaptureState : uint16_t {
    271  Off = nsIMediaManagerService::STATE_NOCAPTURE,
    272  Enabled = nsIMediaManagerService::STATE_CAPTURE_ENABLED,
    273  Disabled = nsIMediaManagerService::STATE_CAPTURE_DISABLED,
    274 };
    275 
    276 static CaptureState CombineCaptureState(CaptureState aFirst,
    277                                        CaptureState aSecond) {
    278  if (aFirst == CaptureState::Enabled || aSecond == CaptureState::Enabled) {
    279    return CaptureState::Enabled;
    280  }
    281  if (aFirst == CaptureState::Disabled || aSecond == CaptureState::Disabled) {
    282    return CaptureState::Disabled;
    283  }
    284  MOZ_ASSERT(aFirst == CaptureState::Off);
    285  MOZ_ASSERT(aSecond == CaptureState::Off);
    286  return CaptureState::Off;
    287 }
    288 
    289 static uint16_t FromCaptureState(CaptureState aState) {
    290  MOZ_ASSERT(aState == CaptureState::Off || aState == CaptureState::Enabled ||
    291             aState == CaptureState::Disabled);
    292  return static_cast<uint16_t>(aState);
    293 }
    294 
    295 void MediaManager::CallOnError(GetUserMediaErrorCallback& aCallback,
    296                               MediaStreamError& aError) {
    297  aCallback.Call(aError);
    298 }
    299 
    300 void MediaManager::CallOnSuccess(GetUserMediaSuccessCallback& aCallback,
    301                                 DOMMediaStream& aStream) {
    302  aCallback.Call(aStream);
    303 }
    304 
    305 enum class PersistentPermissionState : uint32_t {
    306  Unknown = nsIPermissionManager::UNKNOWN_ACTION,
    307  Allow = nsIPermissionManager::ALLOW_ACTION,
    308  Deny = nsIPermissionManager::DENY_ACTION,
    309  Prompt = nsIPermissionManager::PROMPT_ACTION,
    310 };
    311 
    312 static PersistentPermissionState CheckPermission(
    313    PersistentPermissionState aPermission) {
    314  switch (aPermission) {
    315    case PersistentPermissionState::Unknown:
    316    case PersistentPermissionState::Allow:
    317    case PersistentPermissionState::Deny:
    318    case PersistentPermissionState::Prompt:
    319      return aPermission;
    320  }
    321  MOZ_CRASH("Unexpected permission value");
    322 }
    323 
    324 struct WindowPersistentPermissionState {
    325  PersistentPermissionState mCameraPermission;
    326  PersistentPermissionState mMicrophonePermission;
    327 };
    328 
    329 static Result<WindowPersistentPermissionState, nsresult>
    330 GetPersistentPermissions(uint64_t aWindowId) {
    331  auto* window = nsGlobalWindowInner::GetInnerWindowWithId(aWindowId);
    332  if (NS_WARN_IF(!window) || NS_WARN_IF(!window->GetPrincipal())) {
    333    return Err(NS_ERROR_INVALID_ARG);
    334  }
    335 
    336  Document* doc = window->GetExtantDoc();
    337  if (NS_WARN_IF(!doc)) {
    338    return Err(NS_ERROR_INVALID_ARG);
    339  }
    340 
    341  nsIPrincipal* principal = window->GetPrincipal();
    342  if (NS_WARN_IF(!principal)) {
    343    return Err(NS_ERROR_INVALID_ARG);
    344  }
    345 
    346  nsresult rv;
    347  RefPtr<PermissionDelegateHandler> permDelegate =
    348      doc->GetPermissionDelegateHandler();
    349  if (NS_WARN_IF(!permDelegate)) {
    350    return Err(NS_ERROR_INVALID_ARG);
    351  }
    352 
    353  uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
    354  uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
    355  {
    356    rv = permDelegate->GetPermission("microphone"_ns, &audio, true);
    357    if (NS_WARN_IF(NS_FAILED(rv))) {
    358      return Err(rv);
    359    }
    360    rv = permDelegate->GetPermission("camera"_ns, &video, true);
    361    if (NS_WARN_IF(NS_FAILED(rv))) {
    362      return Err(rv);
    363    }
    364  }
    365 
    366  return WindowPersistentPermissionState{
    367      CheckPermission(static_cast<PersistentPermissionState>(video)),
    368      CheckPermission(static_cast<PersistentPermissionState>(audio))};
    369 }
    370 
    371 /**
    372 * DeviceListener has threadsafe refcounting for use across the main, media and
    373 * MTG threads. But it has a non-threadsafe SupportsWeakPtr for WeakPtr usage
    374 * only from main thread, to ensure that garbage- and cycle-collected objects
    375 * don't hold a reference to it during late shutdown.
    376 */
    377 class DeviceListener : public SupportsWeakPtr {
    378 public:
    379  typedef MozPromise<bool /* aIgnored */, RefPtr<MediaMgrError>, false>
    380      DeviceListenerPromise;
    381 
    382  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
    383      DeviceListener)
    384 
    385  DeviceListener();
    386 
    387  /**
    388   * Registers this device listener as belonging to the given window listener.
    389   * Stop() must be called on registered DeviceListeners before destruction.
    390   */
    391  void Register(GetUserMediaWindowListener* aListener);
    392 
    393  /**
    394   * Marks this listener as active and creates the internal device state.
    395   */
    396  void Activate(RefPtr<LocalMediaDevice> aDevice,
    397                RefPtr<LocalTrackSource> aTrackSource, bool aStartMuted,
    398                bool aIsAllocated);
    399 
    400  /**
    401   * Posts a task to initialize and start the associated device.
    402   */
    403  RefPtr<DeviceListenerPromise> InitializeAsync();
    404 
    405 private:
    406  /**
    407   * Initializes synchronously. Must be called on the media thread.
    408   */
    409  nsresult Initialize(PrincipalHandle aPrincipal, LocalMediaDevice* aDevice,
    410                      MediaTrack* aTrack, bool aStartDevice);
    411 
    412 public:
    413  /**
    414   * Synchronously clones this device listener, setting up the device to match
    415   * our current device state asynchronously. Settings, constraints and other
    416   * main thread state starts applying immediately.
    417   */
    418  already_AddRefed<DeviceListener> Clone() const;
    419 
    420  /**
    421   * Posts a task to stop the device associated with this DeviceListener and
    422   * notifies the associated window listener that a track was stopped.
    423   *
    424   * This will also clean up the weak reference to the associated window
    425   * listener, and tell the window listener to remove its hard reference to this
    426   * DeviceListener, so any caller will need to keep its own hard ref.
    427   */
    428  void Stop();
    429 
    430  /**
    431   * Gets the main thread MediaTrackSettings from the MediaEngineSource
    432   * associated with aTrack.
    433   */
    434  void GetSettings(MediaTrackSettings& aOutSettings) const;
    435 
    436  /**
    437   * Gets the main thread MediaTrackCapabilities from the MediaEngineSource
    438   * associated with aTrack.
    439   */
    440  void GetCapabilities(MediaTrackCapabilities& aOutCapabilities) const;
    441 
    442  /**
    443   * Posts a task to set the enabled state of the device associated with this
    444   * DeviceListener to aEnabled and notifies the associated window listener that
    445   * a track's state has changed.
    446   *
    447   * Turning the hardware off while the device is disabled is supported for:
    448   * - Camera (enabled by default, controlled by pref
    449   *   "media.getusermedia.camera.off_while_disabled.enabled")
    450   * - Microphone (disabled by default, controlled by pref
    451   *   "media.getusermedia.microphone.off_while_disabled.enabled")
    452   * Screen-, app-, or windowsharing is not supported at this time.
    453   *
    454   * The behavior is also different between disabling and enabling a device.
    455   * While enabling is immediate, disabling only happens after a delay.
    456   * This is now defaulting to 3 seconds but can be overriden by prefs:
    457   * - "media.getusermedia.camera.off_while_disabled.delay_ms" and
    458   * - "media.getusermedia.microphone.off_while_disabled.delay_ms".
    459   *
    460   * The delay is in place to prevent misuse by malicious sites. If a track is
    461   * re-enabled before the delay has passed, the device will not be touched
    462   * until another disable followed by the full delay happens.
    463   */
    464  void SetDeviceEnabled(bool aEnabled);
    465 
    466  /**
    467   * Posts a task to set the muted state of the device associated with this
    468   * DeviceListener to aMuted and notifies the associated window listener that a
    469   * track's state has changed.
    470   *
    471   * Turning the hardware off while the device is muted is supported for:
    472   * - Camera (enabled by default, controlled by pref
    473   *   "media.getusermedia.camera.off_while_disabled.enabled")
    474   * - Microphone (disabled by default, controlled by pref
    475   *   "media.getusermedia.microphone.off_while_disabled.enabled")
    476   * Screen-, app-, or windowsharing is not supported at this time.
    477   */
    478  void SetDeviceMuted(bool aMuted);
    479 
    480  /**
    481   * Mutes or unmutes the associated video device if it is a camera.
    482   */
    483  void MuteOrUnmuteCamera(bool aMute);
    484  void MuteOrUnmuteMicrophone(bool aMute);
    485 
    486  LocalMediaDevice* GetDevice() const {
    487    return mDeviceState ? mDeviceState->mDevice.get() : nullptr;
    488  }
    489 
    490  LocalTrackSource* GetTrackSource() const {
    491    return mDeviceState ? mDeviceState->mTrackSource.get() : nullptr;
    492  }
    493 
    494  bool Activated() const { return static_cast<bool>(mDeviceState); }
    495 
    496  bool Stopped() const { return mStopped; }
    497 
    498  bool CapturingVideo() const;
    499 
    500  bool CapturingAudio() const;
    501 
    502  CaptureState CapturingSource(MediaSourceEnum aSource) const;
    503 
    504  RefPtr<DeviceListenerPromise> ApplyConstraints(
    505      const MediaTrackConstraints& aConstraints, CallerType aCallerType);
    506 
    507  PrincipalHandle GetPrincipalHandle() const;
    508 
    509  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    510    size_t amount = aMallocSizeOf(this);
    511    // Assume mPrincipalHandle refers to a principal owned elsewhere.
    512    // DeviceState does not have support for memory accounting.
    513    return amount;
    514  }
    515 
    516 private:
    517  virtual ~DeviceListener() {
    518    MOZ_ASSERT(mStopped);
    519    MOZ_ASSERT(!mWindowListener);
    520  }
    521 
    522  using DeviceOperationPromise =
    523      MozPromise<nsresult, bool, /* IsExclusive = */ true>;
    524 
    525  /**
    526   * Posts a task to start or stop the device associated with aTrack, based on
    527   * a passed-in boolean. Private method used by SetDeviceEnabled and
    528   * SetDeviceMuted.
    529   */
    530  RefPtr<DeviceOperationPromise> UpdateDevice(bool aOn);
    531 
    532  // true after this listener has had all devices stopped. MainThread only.
    533  bool mStopped;
    534 
    535  // never ever indirect off this; just for assertions
    536  PRThread* mMainThreadCheck;
    537 
    538  // Set in Register() on main thread, then read from any thread.
    539  PrincipalHandle mPrincipalHandle;
    540 
    541  // Weak pointer to the window listener that owns us. MainThread only.
    542  GetUserMediaWindowListener* mWindowListener;
    543 
    544  // Accessed from MediaTrackGraph thread, MediaManager thread, and MainThread
    545  // No locking needed as it's set on Activate() and never assigned to again.
    546  UniquePtr<DeviceState> mDeviceState;
    547 
    548  MediaEventListener mCaptureEndedListener;
    549 };
    550 
    551 /**
    552 * This class represents a WindowID and handles all MediaTrackListeners
    553 * (here subclassed as DeviceListeners) used to feed GetUserMedia tracks.
    554 * It proxies feedback from them into messages for browser chrome.
    555 * The DeviceListeners are used to Start() and Stop() the underlying
    556 * MediaEngineSource when MediaStreams are assigned and deassigned in content.
    557 */
    558 class GetUserMediaWindowListener {
    559  friend MediaManager;
    560 
    561 public:
    562  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GetUserMediaWindowListener)
    563 
    564  // Create in an inactive state
    565  GetUserMediaWindowListener(uint64_t aWindowID,
    566                             const PrincipalHandle& aPrincipalHandle)
    567      : mWindowID(aWindowID),
    568        mPrincipalHandle(aPrincipalHandle),
    569        mChromeNotificationTaskPosted(false) {}
    570 
    571  /**
    572   * Registers an inactive gUM device listener for this WindowListener.
    573   */
    574  void Register(RefPtr<DeviceListener> aListener) {
    575    MOZ_ASSERT(NS_IsMainThread());
    576    MOZ_ASSERT(aListener);
    577    MOZ_ASSERT(!aListener->Activated());
    578    MOZ_ASSERT(!mInactiveListeners.Contains(aListener), "Already registered");
    579    MOZ_ASSERT(!mActiveListeners.Contains(aListener), "Already activated");
    580 
    581    aListener->Register(this);
    582    mInactiveListeners.AppendElement(std::move(aListener));
    583  }
    584 
    585  /**
    586   * Activates an already registered and inactive gUM device listener for this
    587   * WindowListener.
    588   */
    589  void Activate(RefPtr<DeviceListener> aListener,
    590                RefPtr<LocalMediaDevice> aDevice,
    591                RefPtr<LocalTrackSource> aTrackSource, bool aIsAllocated) {
    592    MOZ_ASSERT(NS_IsMainThread());
    593    MOZ_ASSERT(aListener);
    594    MOZ_ASSERT(!aListener->Activated());
    595    MOZ_ASSERT(mInactiveListeners.Contains(aListener),
    596               "Must be registered to activate");
    597    MOZ_ASSERT(!mActiveListeners.Contains(aListener), "Already activated");
    598 
    599    bool muted = false;
    600    if (aDevice->Kind() == MediaDeviceKind::Videoinput) {
    601      muted = mCamerasAreMuted;
    602    } else if (aDevice->Kind() == MediaDeviceKind::Audioinput) {
    603      muted = mMicrophonesAreMuted;
    604    } else {
    605      MOZ_CRASH("Unexpected device kind");
    606    }
    607 
    608    mInactiveListeners.RemoveElement(aListener);
    609    aListener->Activate(std::move(aDevice), std::move(aTrackSource), muted,
    610                        aIsAllocated);
    611    mActiveListeners.AppendElement(std::move(aListener));
    612  }
    613 
    614  /**
    615   * Removes all DeviceListeners from this window listener.
    616   * Removes this window listener from the list of active windows, so callers
    617   * need to make sure to hold a strong reference.
    618   */
    619  void RemoveAll() {
    620    MOZ_ASSERT(NS_IsMainThread());
    621 
    622    for (auto& l : mInactiveListeners.Clone()) {
    623      Remove(l);
    624    }
    625    for (auto& l : mActiveListeners.Clone()) {
    626      Remove(l);
    627    }
    628    MOZ_ASSERT(mInactiveListeners.Length() == 0);
    629    MOZ_ASSERT(mActiveListeners.Length() == 0);
    630 
    631    MediaManager* mgr = MediaManager::GetIfExists();
    632    if (!mgr) {
    633      MOZ_ASSERT(false, "MediaManager should stay until everything is removed");
    634      return;
    635    }
    636    GetUserMediaWindowListener* windowListener =
    637        mgr->GetWindowListener(mWindowID);
    638 
    639    if (!windowListener) {
    640      nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    641      auto* globalWindow = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID);
    642      if (globalWindow) {
    643        auto req = MakeRefPtr<GetUserMediaRequest>(
    644            globalWindow, VoidString(), VoidString(),
    645            UserActivation::IsHandlingUserInput());
    646        obs->NotifyWhenScriptSafe(req, "recording-device-stopped", nullptr);
    647      }
    648      return;
    649    }
    650 
    651    MOZ_ASSERT(windowListener == this,
    652               "There should only be one window listener per window ID");
    653 
    654    LOG("GUMWindowListener %p removing windowID %" PRIu64, this, mWindowID);
    655    mgr->RemoveWindowID(mWindowID);
    656  }
    657 
    658  /**
    659   * Removes a listener from our lists. Safe to call without holding a hard
    660   * reference. That said, you'll still want to iterate on a copy of said lists,
    661   * if you end up calling this method (or methods that may call this method) in
    662   * the loop, to avoid inadvertently skipping members.
    663   *
    664   * For use only from GetUserMediaWindowListener and DeviceListener.
    665   */
    666  bool Remove(RefPtr<DeviceListener> aListener) {
    667    // We refcount aListener on entry since we're going to proxy-release it
    668    // below to prevent the refcount going to zero on callers who might be
    669    // inside the listener, but operating without a hard reference to self.
    670    MOZ_ASSERT(NS_IsMainThread());
    671 
    672    if (!mInactiveListeners.RemoveElement(aListener) &&
    673        !mActiveListeners.RemoveElement(aListener)) {
    674      return false;
    675    }
    676    MOZ_ASSERT(!mInactiveListeners.Contains(aListener),
    677               "A DeviceListener should only be once in one of "
    678               "mInactiveListeners and mActiveListeners");
    679    MOZ_ASSERT(!mActiveListeners.Contains(aListener),
    680               "A DeviceListener should only be once in one of "
    681               "mInactiveListeners and mActiveListeners");
    682 
    683    LOG("GUMWindowListener %p stopping DeviceListener %p.", this,
    684        aListener.get());
    685    aListener->Stop();
    686 
    687    if (LocalMediaDevice* removedDevice = aListener->GetDevice()) {
    688      bool revokePermission = true;
    689      nsString removedRawId;
    690      nsString removedSourceType;
    691      removedDevice->GetRawId(removedRawId);
    692      removedDevice->GetMediaSource(removedSourceType);
    693 
    694      for (const auto& l : mActiveListeners) {
    695        if (LocalMediaDevice* device = l->GetDevice()) {
    696          nsString rawId;
    697          device->GetRawId(rawId);
    698          if (removedRawId.Equals(rawId)) {
    699            revokePermission = false;
    700            break;
    701          }
    702        }
    703      }
    704 
    705      if (revokePermission) {
    706        nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    707        auto* window = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID);
    708        auto req = MakeRefPtr<GetUserMediaRequest>(
    709            window, removedRawId, removedSourceType,
    710            UserActivation::IsHandlingUserInput());
    711        obs->NotifyWhenScriptSafe(req, "recording-device-stopped", nullptr);
    712      }
    713    }
    714 
    715    if (mInactiveListeners.Length() == 0 && mActiveListeners.Length() == 0) {
    716      LOG("GUMWindowListener %p Removed last DeviceListener. Cleaning up.",
    717          this);
    718      RemoveAll();
    719    }
    720 
    721    nsCOMPtr<nsIEventTarget> mainTarget = do_GetMainThread();
    722    // To allow being invoked by callers not holding a strong reference to self,
    723    // hold the listener alive until the stack has unwound, by always
    724    // dispatching a runnable (aAlwaysProxy = true)
    725    NS_ProxyRelease(__func__, mainTarget, aListener.forget(), true);
    726    return true;
    727  }
    728 
    729  /**
    730   * Stops all screen/window/audioCapture sharing, but not camera or microphone.
    731   */
    732  void StopSharing();
    733 
    734  void StopRawID(const nsString& removedDeviceID);
    735 
    736  void MuteOrUnmuteCameras(bool aMute);
    737  void MuteOrUnmuteMicrophones(bool aMute);
    738 
    739  /**
    740   * Called by one of our DeviceListeners when one of its tracks has changed so
    741   * that chrome state is affected.
    742   * Schedules an event for the next stable state to update chrome.
    743   */
    744  void ChromeAffectingStateChanged();
    745 
    746  /**
    747   * Called in stable state to send a notification to update chrome.
    748   */
    749  void NotifyChrome();
    750 
    751  bool CapturingVideo() const {
    752    MOZ_ASSERT(NS_IsMainThread());
    753    for (auto& l : mActiveListeners) {
    754      if (l->CapturingVideo()) {
    755        return true;
    756      }
    757    }
    758    return false;
    759  }
    760 
    761  bool CapturingAudio() const {
    762    MOZ_ASSERT(NS_IsMainThread());
    763    for (auto& l : mActiveListeners) {
    764      if (l->CapturingAudio()) {
    765        return true;
    766      }
    767    }
    768    return false;
    769  }
    770 
    771  CaptureState CapturingSource(MediaSourceEnum aSource) const {
    772    MOZ_ASSERT(NS_IsMainThread());
    773    CaptureState result = CaptureState::Off;
    774    for (auto& l : mActiveListeners) {
    775      result = CombineCaptureState(result, l->CapturingSource(aSource));
    776    }
    777    return result;
    778  }
    779 
    780  RefPtr<LocalMediaDeviceSetRefCnt> GetDevices() {
    781    RefPtr devices = new LocalMediaDeviceSetRefCnt();
    782    for (auto& l : mActiveListeners) {
    783      devices->AppendElement(l->GetDevice());
    784    }
    785    return devices;
    786  }
    787 
    788  uint64_t WindowID() const { return mWindowID; }
    789 
    790  PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
    791 
    792  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
    793    size_t amount = aMallocSizeOf(this);
    794    // Assume mPrincipalHandle refers to a principal owned elsewhere.
    795    amount += mInactiveListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
    796    for (const RefPtr<DeviceListener>& listener : mInactiveListeners) {
    797      amount += listener->SizeOfIncludingThis(aMallocSizeOf);
    798    }
    799    amount += mActiveListeners.ShallowSizeOfExcludingThis(aMallocSizeOf);
    800    for (const RefPtr<DeviceListener>& listener : mActiveListeners) {
    801      amount += listener->SizeOfIncludingThis(aMallocSizeOf);
    802    }
    803    return amount;
    804  }
    805 
    806 private:
    807  ~GetUserMediaWindowListener() {
    808    MOZ_ASSERT(mInactiveListeners.Length() == 0,
    809               "Inactive listeners should already be removed");
    810    MOZ_ASSERT(mActiveListeners.Length() == 0,
    811               "Active listeners should already be removed");
    812  }
    813 
    814  uint64_t mWindowID;
    815  const PrincipalHandle mPrincipalHandle;
    816 
    817  // true if we have scheduled a task to notify chrome in the next stable state.
    818  // The task will reset this to false. MainThread only.
    819  bool mChromeNotificationTaskPosted;
    820 
    821  nsTArray<RefPtr<DeviceListener>> mInactiveListeners;
    822  nsTArray<RefPtr<DeviceListener>> mActiveListeners;
    823 
    824  // Whether camera and microphone access in this window are currently
    825  // User Agent (UA) muted. When true, new and cloned tracks must start
    826  // out muted, to avoid JS circumventing UA mute. Per-camera and
    827  // per-microphone UA muting is not supported.
    828  bool mCamerasAreMuted = false;
    829  bool mMicrophonesAreMuted = false;
    830 };
    831 
    832 class LocalTrackSource : public MediaStreamTrackSource {
    833 public:
    834  LocalTrackSource(nsIPrincipal* aPrincipal, const nsString& aLabel,
    835                   const RefPtr<DeviceListener>& aListener,
    836                   MediaSourceEnum aSource, MediaTrack* aTrack,
    837                   RefPtr<const PeerIdentity> aPeerIdentity,
    838                   TrackingId aTrackingId = TrackingId())
    839      : MediaStreamTrackSource(aPrincipal, aLabel, std::move(aTrackingId)),
    840        mSource(aSource),
    841        mTrack(aTrack),
    842        mPeerIdentity(std::move(aPeerIdentity)),
    843        mListener(aListener.get()) {}
    844 
    845  MediaSourceEnum GetMediaSource() const override { return mSource; }
    846 
    847  const PeerIdentity* GetPeerIdentity() const override { return mPeerIdentity; }
    848 
    849  RefPtr<MediaStreamTrackSource::ApplyConstraintsPromise> ApplyConstraints(
    850      const MediaTrackConstraints& aConstraints,
    851      CallerType aCallerType) override {
    852    MOZ_ASSERT(NS_IsMainThread());
    853    if (sHasMainThreadShutdown || !mListener) {
    854      // Track has been stopped, or we are in shutdown. In either case
    855      // there's no observable outcome, so pretend we succeeded.
    856      return MediaStreamTrackSource::ApplyConstraintsPromise::CreateAndResolve(
    857          false, __func__);
    858    }
    859    auto p = mListener->ApplyConstraints(aConstraints, aCallerType);
    860    p->Then(
    861        GetCurrentSerialEventTarget(), __func__,
    862        [aConstraints, this, self = RefPtr(this)] {
    863          ConstraintsChanged(aConstraints);
    864        },
    865        [] {});
    866    return p;
    867  }
    868 
    869  void GetSettings(MediaTrackSettings& aOutSettings) override {
    870    if (mListener) {
    871      mListener->GetSettings(aOutSettings);
    872    }
    873  }
    874 
    875  void GetCapabilities(MediaTrackCapabilities& aOutCapabilities) override {
    876    if (mListener) {
    877      mListener->GetCapabilities(aOutCapabilities);
    878    }
    879  }
    880 
    881  void Stop() override {
    882    if (mListener) {
    883      mListener->Stop();
    884      mListener = nullptr;
    885    }
    886    if (!mTrack->IsDestroyed()) {
    887      mTrack->Destroy();
    888    }
    889  }
    890 
    891  CloneResult Clone() override {
    892    if (!mListener) {
    893      return {};
    894    }
    895    RefPtr listener = mListener->Clone();
    896    MOZ_ASSERT(listener);
    897    if (!listener) {
    898      return {};
    899    }
    900 
    901    return {.mSource = listener->GetTrackSource(),
    902            .mInputTrack = listener->GetTrackSource()->mTrack};
    903  }
    904 
    905  void Disable() override {
    906    if (mListener) {
    907      mListener->SetDeviceEnabled(false);
    908    }
    909  }
    910 
    911  void Enable() override {
    912    if (mListener) {
    913      mListener->SetDeviceEnabled(true);
    914    }
    915  }
    916 
    917  void Mute() {
    918    MutedChanged(true);
    919    mTrack->SetDisabledTrackMode(DisabledTrackMode::SILENCE_BLACK);
    920  }
    921 
    922  void Unmute() {
    923    MutedChanged(false);
    924    mTrack->SetDisabledTrackMode(DisabledTrackMode::ENABLED);
    925  }
    926 
    927  const MediaSourceEnum mSource;
    928  const RefPtr<MediaTrack> mTrack;
    929  const RefPtr<const PeerIdentity> mPeerIdentity;
    930 
    931 protected:
    932  ~LocalTrackSource() {
    933    MOZ_ASSERT(NS_IsMainThread());
    934    MOZ_ASSERT(mTrack->IsDestroyed());
    935  }
    936 
    937  // This is a weak pointer to avoid having the DeviceListener (which may
    938  // have references to threads and threadpools) kept alive by DOM-objects
    939  // that may have ref-cycles and thus are released very late during
    940  // shutdown, even after xpcom-shutdown-threads. See bug 1351655 for what
    941  // can happen.
    942  WeakPtr<DeviceListener> mListener;
    943 };
    944 
    945 class AudioCaptureTrackSource : public LocalTrackSource {
    946 public:
    947  AudioCaptureTrackSource(nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow,
    948                          const nsString& aLabel,
    949                          AudioCaptureTrack* aAudioCaptureTrack,
    950                          RefPtr<PeerIdentity> aPeerIdentity)
    951      : LocalTrackSource(aPrincipal, aLabel, nullptr,
    952                         MediaSourceEnum::AudioCapture, aAudioCaptureTrack,
    953                         std::move(aPeerIdentity)),
    954        mWindow(aWindow),
    955        mAudioCaptureTrack(aAudioCaptureTrack) {
    956    mAudioCaptureTrack->Start();
    957    mAudioCaptureTrack->Graph()->RegisterCaptureTrackForWindow(
    958        mWindow->WindowID(), mAudioCaptureTrack);
    959    mWindow->SetAudioCapture(true);
    960  }
    961 
    962  void Stop() override {
    963    MOZ_ASSERT(NS_IsMainThread());
    964    if (!mAudioCaptureTrack->IsDestroyed()) {
    965      MOZ_ASSERT(mWindow);
    966      mWindow->SetAudioCapture(false);
    967      mAudioCaptureTrack->Graph()->UnregisterCaptureTrackForWindow(
    968          mWindow->WindowID());
    969      mWindow = nullptr;
    970    }
    971    // LocalTrackSource destroys the track.
    972    LocalTrackSource::Stop();
    973    MOZ_ASSERT(mAudioCaptureTrack->IsDestroyed());
    974  }
    975 
    976  ProcessedMediaTrack* InputTrack() const { return mAudioCaptureTrack.get(); }
    977 
    978 protected:
    979  ~AudioCaptureTrackSource() {
    980    MOZ_ASSERT(NS_IsMainThread());
    981    MOZ_ASSERT(mAudioCaptureTrack->IsDestroyed());
    982  }
    983 
    984  RefPtr<nsPIDOMWindowInner> mWindow;
    985  const RefPtr<AudioCaptureTrack> mAudioCaptureTrack;
    986 };
    987 
    988 /**
    989 * nsIMediaDevice implementation.
    990 */
    991 NS_IMPL_ISUPPORTS(LocalMediaDevice, nsIMediaDevice)
    992 
    993 MediaDevice::MediaDevice(MediaEngine* aEngine, MediaSourceEnum aMediaSource,
    994                         const nsString& aRawName, const nsString& aRawID,
    995                         const nsString& aRawGroupID, IsScary aIsScary,
    996                         const OsPromptable canRequestOsLevelPrompt)
    997    : mEngine(aEngine),
    998      mAudioDeviceInfo(nullptr),
    999      mMediaSource(aMediaSource),
   1000      mKind(MediaEngineSource::IsVideo(aMediaSource)
   1001                ? MediaDeviceKind::Videoinput
   1002                : MediaDeviceKind::Audioinput),
   1003      mScary(aIsScary == IsScary::Yes),
   1004      mCanRequestOsLevelPrompt(canRequestOsLevelPrompt == OsPromptable::Yes),
   1005      mIsFake(mEngine->IsFake()),
   1006      mType(NS_ConvertASCIItoUTF16(dom::GetEnumString(mKind))),
   1007      mRawID(aRawID),
   1008      mRawGroupID(aRawGroupID),
   1009      mRawName(aRawName) {
   1010  MOZ_ASSERT(mEngine);
   1011 }
   1012 
   1013 MediaDevice::MediaDevice(MediaEngine* aEngine,
   1014                         const RefPtr<AudioDeviceInfo>& aAudioDeviceInfo,
   1015                         const nsString& aRawID)
   1016    : mEngine(aEngine),
   1017      mAudioDeviceInfo(aAudioDeviceInfo),
   1018      mMediaSource(mAudioDeviceInfo->Type() == AudioDeviceInfo::TYPE_INPUT
   1019                       ? MediaSourceEnum::Microphone
   1020                       : MediaSourceEnum::Other),
   1021      mKind(mMediaSource == MediaSourceEnum::Microphone
   1022                ? MediaDeviceKind::Audioinput
   1023                : MediaDeviceKind::Audiooutput),
   1024      mScary(false),
   1025      mCanRequestOsLevelPrompt(false),
   1026      mIsFake(false),
   1027      mType(NS_ConvertASCIItoUTF16(dom::GetEnumString(mKind))),
   1028      mRawID(aRawID),
   1029      mRawGroupID(mAudioDeviceInfo->GroupID()),
   1030      mRawName(mAudioDeviceInfo->Name()) {}
   1031 
   1032 /* static */
   1033 RefPtr<MediaDevice> MediaDevice::CopyWithNewRawGroupId(
   1034    const RefPtr<MediaDevice>& aOther, const nsString& aRawGroupID) {
   1035  MOZ_ASSERT(!aOther->mAudioDeviceInfo, "device not supported");
   1036  return new MediaDevice(aOther->mEngine, aOther->mMediaSource,
   1037                         aOther->mRawName, aOther->mRawID, aRawGroupID,
   1038                         IsScary(aOther->mScary),
   1039                         OsPromptable(aOther->mCanRequestOsLevelPrompt));
   1040 }
   1041 
   1042 MediaDevice::~MediaDevice() = default;
   1043 
   1044 LocalMediaDevice::LocalMediaDevice(RefPtr<const MediaDevice> aRawDevice,
   1045                                   const nsString& aID,
   1046                                   const nsString& aGroupID,
   1047                                   const nsString& aName)
   1048    : mRawDevice(std::move(aRawDevice)),
   1049      mName(aName),
   1050      mID(aID),
   1051      mGroupID(aGroupID) {
   1052  MOZ_ASSERT(mRawDevice);
   1053 }
   1054 
   1055 /**
   1056 * Helper functions that implement the constraints algorithm from
   1057 * http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
   1058 */
   1059 
   1060 /* static */
   1061 bool LocalMediaDevice::StringsContain(
   1062    const OwningStringOrStringSequence& aStrings, nsString aN) {
   1063  return aStrings.IsString() ? aStrings.GetAsString() == aN
   1064                             : aStrings.GetAsStringSequence().Contains(aN);
   1065 }
   1066 
   1067 /* static */
   1068 uint32_t LocalMediaDevice::FitnessDistance(
   1069    nsString aN, const ConstrainDOMStringParameters& aParams) {
   1070  if (aParams.mExact.WasPassed() &&
   1071      !StringsContain(aParams.mExact.Value(), aN)) {
   1072    return UINT32_MAX;
   1073  }
   1074  if (aParams.mIdeal.WasPassed() &&
   1075      !StringsContain(aParams.mIdeal.Value(), aN)) {
   1076    return 1;
   1077  }
   1078  return 0;
   1079 }
   1080 
   1081 // Binding code doesn't templatize well...
   1082 
   1083 /* static */
   1084 uint32_t LocalMediaDevice::FitnessDistance(
   1085    nsString aN,
   1086    const OwningStringOrStringSequenceOrConstrainDOMStringParameters&
   1087        aConstraint) {
   1088  if (aConstraint.IsString()) {
   1089    ConstrainDOMStringParameters params;
   1090    params.mIdeal.Construct();
   1091    params.mIdeal.Value().SetAsString() = aConstraint.GetAsString();
   1092    return FitnessDistance(aN, params);
   1093  } else if (aConstraint.IsStringSequence()) {
   1094    ConstrainDOMStringParameters params;
   1095    params.mIdeal.Construct();
   1096    params.mIdeal.Value().SetAsStringSequence() =
   1097        aConstraint.GetAsStringSequence();
   1098    return FitnessDistance(aN, params);
   1099  } else {
   1100    return FitnessDistance(aN, aConstraint.GetAsConstrainDOMStringParameters());
   1101  }
   1102 }
   1103 
   1104 uint32_t LocalMediaDevice::GetBestFitnessDistance(
   1105    const nsTArray<const NormalizedConstraintSet*>& aConstraintSets,
   1106    const MediaEnginePrefs& aPrefs, CallerType aCallerType) {
   1107  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1108  MOZ_ASSERT(GetMediaSource() != MediaSourceEnum::Other);
   1109 
   1110  bool isChrome = aCallerType == CallerType::System;
   1111  const nsString& id = isChrome ? RawID() : mID;
   1112  auto type = GetMediaSource();
   1113  uint64_t distance = 0;
   1114  if (!aConstraintSets.IsEmpty()) {
   1115    if (isChrome /* For the screen/window sharing preview */ ||
   1116        type == MediaSourceEnum::Camera ||
   1117        type == MediaSourceEnum::Microphone) {
   1118      distance += uint64_t(MediaConstraintsHelper::FitnessDistance(
   1119                      Some(id), aConstraintSets[0]->mDeviceId)) +
   1120                  uint64_t(MediaConstraintsHelper::FitnessDistance(
   1121                      Some(mGroupID), aConstraintSets[0]->mGroupId));
   1122    }
   1123  }
   1124  if (distance < UINT32_MAX) {
   1125    // Forward request to underlying object to interrogate per-mode
   1126    // capabilities.
   1127    distance += Source()->GetBestFitnessDistance(aConstraintSets, aPrefs);
   1128  }
   1129  return std::min<uint64_t>(distance, UINT32_MAX);
   1130 }
   1131 
   1132 NS_IMETHODIMP
   1133 LocalMediaDevice::GetRawName(nsAString& aName) {
   1134  MOZ_ASSERT(NS_IsMainThread());
   1135  aName.Assign(mRawDevice->mRawName);
   1136  return NS_OK;
   1137 }
   1138 
   1139 NS_IMETHODIMP
   1140 LocalMediaDevice::GetType(nsAString& aType) {
   1141  MOZ_ASSERT(NS_IsMainThread());
   1142  aType.Assign(mRawDevice->mType);
   1143  return NS_OK;
   1144 }
   1145 
   1146 NS_IMETHODIMP
   1147 LocalMediaDevice::GetRawId(nsAString& aID) {
   1148  MOZ_ASSERT(NS_IsMainThread());
   1149  aID.Assign(RawID());
   1150  return NS_OK;
   1151 }
   1152 
   1153 NS_IMETHODIMP
   1154 LocalMediaDevice::GetId(nsAString& aID) {
   1155  MOZ_ASSERT(NS_IsMainThread());
   1156  aID.Assign(mID);
   1157  return NS_OK;
   1158 }
   1159 
   1160 NS_IMETHODIMP
   1161 LocalMediaDevice::GetScary(bool* aScary) {
   1162  *aScary = mRawDevice->mScary;
   1163  return NS_OK;
   1164 }
   1165 
   1166 NS_IMETHODIMP
   1167 LocalMediaDevice::GetCanRequestOsLevelPrompt(bool* aCanRequestOsLevelPrompt) {
   1168  *aCanRequestOsLevelPrompt = mRawDevice->mCanRequestOsLevelPrompt;
   1169  return NS_OK;
   1170 }
   1171 
   1172 void LocalMediaDevice::GetSettings(MediaTrackSettings& aOutSettings) {
   1173  MOZ_ASSERT(NS_IsMainThread());
   1174  Source()->GetSettings(aOutSettings);
   1175 }
   1176 
   1177 void LocalMediaDevice::GetCapabilities(
   1178    MediaTrackCapabilities& aOutCapabilities) {
   1179  MOZ_ASSERT(NS_IsMainThread());
   1180  Source()->GetCapabilities(aOutCapabilities);
   1181 }
   1182 
   1183 MediaEngineSource* LocalMediaDevice::Source() {
   1184  if (!mSource) {
   1185    mSource = mRawDevice->mEngine->CreateSource(mRawDevice);
   1186  }
   1187  return mSource;
   1188 }
   1189 
   1190 const TrackingId& LocalMediaDevice::GetTrackingId() const {
   1191  return mSource->GetTrackingId();
   1192 }
   1193 
   1194 const dom::MediaTrackConstraints& LocalMediaDevice::Constraints() const {
   1195  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1196  return mConstraints;
   1197 }
   1198 
   1199 // Threadsafe since mKind and mSource are const.
   1200 NS_IMETHODIMP
   1201 LocalMediaDevice::GetMediaSource(nsAString& aMediaSource) {
   1202  if (Kind() == MediaDeviceKind::Audiooutput) {
   1203    aMediaSource.Truncate();
   1204  } else {
   1205    aMediaSource.AssignASCII(dom::GetEnumString(GetMediaSource()));
   1206  }
   1207  return NS_OK;
   1208 }
   1209 
   1210 nsresult LocalMediaDevice::Allocate(const MediaTrackConstraints& aConstraints,
   1211                                    const MediaEnginePrefs& aPrefs,
   1212                                    uint64_t aWindowID,
   1213                                    const char** aOutBadConstraint) {
   1214  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1215 
   1216  // Mock failure for automated tests.
   1217  if (IsFake() && aConstraints.mDeviceId.WasPassed() &&
   1218      aConstraints.mDeviceId.Value().IsString() &&
   1219      aConstraints.mDeviceId.Value().GetAsString().EqualsASCII("bad device")) {
   1220    return NS_ERROR_FAILURE;
   1221  }
   1222 
   1223  nsresult rv =
   1224      Source()->Allocate(aConstraints, aPrefs, aWindowID, aOutBadConstraint);
   1225  if (NS_SUCCEEDED(rv)) {
   1226    mConstraints = aConstraints;
   1227  }
   1228  return rv;
   1229 }
   1230 
   1231 void LocalMediaDevice::SetTrack(const RefPtr<MediaTrack>& aTrack,
   1232                                const PrincipalHandle& aPrincipalHandle) {
   1233  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1234  Source()->SetTrack(aTrack, aPrincipalHandle);
   1235 }
   1236 
   1237 nsresult LocalMediaDevice::Start() {
   1238  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1239  MOZ_ASSERT(Source());
   1240  return Source()->Start();
   1241 }
   1242 
   1243 nsresult LocalMediaDevice::Reconfigure(
   1244    const MediaTrackConstraints& aConstraints, const MediaEnginePrefs& aPrefs,
   1245    const char** aOutBadConstraint) {
   1246  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1247  using H = MediaConstraintsHelper;
   1248  auto type = GetMediaSource();
   1249  if (type == MediaSourceEnum::Camera || type == MediaSourceEnum::Microphone) {
   1250    NormalizedConstraints c(aConstraints);
   1251    if (H::FitnessDistance(Some(mID), c.mDeviceId) == UINT32_MAX) {
   1252      *aOutBadConstraint = "deviceId";
   1253      return NS_ERROR_INVALID_ARG;
   1254    }
   1255    if (H::FitnessDistance(Some(mGroupID), c.mGroupId) == UINT32_MAX) {
   1256      *aOutBadConstraint = "groupId";
   1257      return NS_ERROR_INVALID_ARG;
   1258    }
   1259    if (aPrefs.mResizeModeEnabled && type == MediaSourceEnum::Camera) {
   1260      // Check invalid exact resizeMode constraint (not a device property)
   1261      nsString none =
   1262          NS_ConvertASCIItoUTF16(dom::GetEnumString(VideoResizeModeEnum::None));
   1263      nsString crop = NS_ConvertASCIItoUTF16(
   1264          dom::GetEnumString(VideoResizeModeEnum::Crop_and_scale));
   1265      if (H::FitnessDistance(Some(none), c.mResizeMode) == UINT32_MAX &&
   1266          H::FitnessDistance(Some(crop), c.mResizeMode) == UINT32_MAX) {
   1267        *aOutBadConstraint = "resizeMode";
   1268        return NS_ERROR_INVALID_ARG;
   1269      }
   1270    }
   1271  }
   1272  nsresult rv = Source()->Reconfigure(aConstraints, aPrefs, aOutBadConstraint);
   1273  if (NS_SUCCEEDED(rv)) {
   1274    mConstraints = aConstraints;
   1275  }
   1276  return rv;
   1277 }
   1278 
   1279 nsresult LocalMediaDevice::FocusOnSelectedSource() {
   1280  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1281  return Source()->FocusOnSelectedSource();
   1282 }
   1283 
   1284 nsresult LocalMediaDevice::Stop() {
   1285  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1286  MOZ_ASSERT(mSource);
   1287  return mSource->Stop();
   1288 }
   1289 
   1290 nsresult LocalMediaDevice::Deallocate() {
   1291  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1292  MOZ_ASSERT(mSource);
   1293  return mSource->Deallocate();
   1294 }
   1295 
   1296 already_AddRefed<LocalMediaDevice> LocalMediaDevice::Clone() const {
   1297  MOZ_ASSERT(NS_IsMainThread());
   1298  auto device = MakeRefPtr<LocalMediaDevice>(mRawDevice, mID, mGroupID, mName);
   1299  device->mSource =
   1300      mRawDevice->mEngine->CreateSourceFrom(mSource, device->mRawDevice);
   1301 #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
   1302  // The source is normally created on the MediaManager thread. But for cloning,
   1303  // it ends up being created on main thread. Make sure its owning event target
   1304  // is set properly.
   1305  auto* src = device->Source();
   1306  src->_mOwningThread = mSource->_mOwningThread;
   1307 #endif
   1308  return device.forget();
   1309 }
   1310 
   1311 MediaSourceEnum MediaDevice::GetMediaSource() const { return mMediaSource; }
   1312 
   1313 static const MediaTrackConstraints& GetInvariant(
   1314    const OwningBooleanOrMediaTrackConstraints& aUnion) {
   1315  static const MediaTrackConstraints empty;
   1316  return aUnion.IsMediaTrackConstraints() ? aUnion.GetAsMediaTrackConstraints()
   1317                                          : empty;
   1318 }
   1319 
   1320 // Source getter returning full list
   1321 
   1322 static void GetMediaDevices(MediaEngine* aEngine, MediaSourceEnum aSrcType,
   1323                            MediaManager::MediaDeviceSet& aResult,
   1324                            const char* aMediaDeviceName = nullptr) {
   1325  MOZ_ASSERT(MediaManager::IsInMediaThread());
   1326 
   1327  LOG("%s: aEngine=%p, aSrcType=%" PRIu8 ", aMediaDeviceName=%s", __func__,
   1328      aEngine, static_cast<uint8_t>(aSrcType),
   1329      aMediaDeviceName ? aMediaDeviceName : "null");
   1330  nsTArray<RefPtr<MediaDevice>> devices;
   1331  aEngine->EnumerateDevices(aSrcType, MediaSinkEnum::Other, &devices);
   1332 
   1333  /*
   1334   * We're allowing multiple tabs to access the same camera for parity
   1335   * with Chrome.  See bug 811757 for some of the issues surrounding
   1336   * this decision.  To disallow, we'd filter by IsAvailable() as we used
   1337   * to.
   1338   */
   1339  if (aMediaDeviceName && *aMediaDeviceName) {
   1340    for (auto& device : devices) {
   1341      if (device->mRawName.EqualsASCII(aMediaDeviceName)) {
   1342        aResult.AppendElement(device);
   1343        LOG("%s: found aMediaDeviceName=%s", __func__, aMediaDeviceName);
   1344        break;
   1345      }
   1346    }
   1347  } else {
   1348    aResult = std::move(devices);
   1349    if (MOZ_LOG_TEST(gMediaManagerLog, mozilla::LogLevel::Debug)) {
   1350      for (auto& device : aResult) {
   1351        LOG("%s: appending device=%s", __func__,
   1352            NS_ConvertUTF16toUTF8(device->mRawName).get());
   1353      }
   1354    }
   1355  }
   1356 }
   1357 
   1358 RefPtr<LocalDeviceSetPromise> MediaManager::SelectSettings(
   1359    const MediaStreamConstraints& aConstraints, CallerType aCallerType,
   1360    RefPtr<LocalMediaDeviceSetRefCnt> aDevices) {
   1361  MOZ_ASSERT(NS_IsMainThread());
   1362 
   1363  // Algorithm accesses device capabilities code and must run on media thread.
   1364  // Modifies passed-in aDevices.
   1365 
   1366  return MediaManager::Dispatch<LocalDeviceSetPromise>(
   1367      __func__, [aConstraints, devices = std::move(aDevices), prefs = mPrefs,
   1368                 aCallerType](MozPromiseHolder<LocalDeviceSetPromise>& holder) {
   1369        auto& devicesRef = *devices;
   1370 
   1371        // Since the advanced part of the constraints algorithm needs to know
   1372        // when a candidate set is overconstrained (zero members), we must split
   1373        // up the list into videos and audios, and put it back together again at
   1374        // the end.
   1375 
   1376        nsTArray<RefPtr<LocalMediaDevice>> videos;
   1377        nsTArray<RefPtr<LocalMediaDevice>> audios;
   1378 
   1379        for (const auto& device : devicesRef) {
   1380          MOZ_ASSERT(device->Kind() == MediaDeviceKind::Videoinput ||
   1381                     device->Kind() == MediaDeviceKind::Audioinput);
   1382          if (device->Kind() == MediaDeviceKind::Videoinput) {
   1383            videos.AppendElement(device);
   1384          } else if (device->Kind() == MediaDeviceKind::Audioinput) {
   1385            audios.AppendElement(device);
   1386          }
   1387        }
   1388        devicesRef.Clear();
   1389        const char* badConstraint = nullptr;
   1390        bool needVideo = IsOn(aConstraints.mVideo);
   1391        bool needAudio = IsOn(aConstraints.mAudio);
   1392 
   1393        if (needVideo && videos.Length()) {
   1394          badConstraint = MediaConstraintsHelper::SelectSettings(
   1395              NormalizedConstraints(GetInvariant(aConstraints.mVideo)), prefs,
   1396              videos, aCallerType);
   1397        }
   1398        if (!badConstraint && needAudio && audios.Length()) {
   1399          badConstraint = MediaConstraintsHelper::SelectSettings(
   1400              NormalizedConstraints(GetInvariant(aConstraints.mAudio)), prefs,
   1401              audios, aCallerType);
   1402        }
   1403        if (badConstraint) {
   1404          LOG("SelectSettings: bad constraint found! Calling error handler!");
   1405          nsString constraint;
   1406          constraint.AssignASCII(badConstraint);
   1407          holder.Reject(
   1408              new MediaMgrError(MediaMgrError::Name::OverconstrainedError, "",
   1409                                constraint),
   1410              __func__);
   1411          return;
   1412        }
   1413        if (!needVideo == !videos.Length() && !needAudio == !audios.Length()) {
   1414          for (auto& video : videos) {
   1415            devicesRef.AppendElement(video);
   1416          }
   1417          for (auto& audio : audios) {
   1418            devicesRef.AppendElement(audio);
   1419          }
   1420        }
   1421        holder.Resolve(devices, __func__);
   1422      });
   1423 }
   1424 
   1425 /**
   1426 * Describes a requested task that handles response from the UI and sends
   1427 * results back to the DOM.
   1428 */
   1429 class GetUserMediaTask {
   1430 public:
   1431  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GetUserMediaTask)
   1432  GetUserMediaTask(uint64_t aWindowID, const ipc::PrincipalInfo& aPrincipalInfo,
   1433                   CallerType aCallerType)
   1434      : mPrincipalInfo(aPrincipalInfo),
   1435        mWindowID(aWindowID),
   1436        mCallerType(aCallerType) {}
   1437 
   1438  virtual void Denied(MediaMgrError::Name aName,
   1439                      const nsCString& aMessage = ""_ns) = 0;
   1440 
   1441  virtual GetUserMediaStreamTask* AsGetUserMediaStreamTask() { return nullptr; }
   1442  virtual SelectAudioOutputTask* AsSelectAudioOutputTask() { return nullptr; }
   1443 
   1444  uint64_t GetWindowID() const { return mWindowID; }
   1445  enum CallerType CallerType() const { return mCallerType; }
   1446 
   1447  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
   1448    size_t amount = aMallocSizeOf(this);
   1449    // Assume mWindowListener is owned by MediaManager.
   1450    // Assume mAudioDeviceListener and mVideoDeviceListener are owned by
   1451    // mWindowListener.
   1452    // Assume PrincipalInfo string buffers are shared.
   1453    // Member types without support for accounting of pointees:
   1454    //   MozPromiseHolder, RefPtr<LocalMediaDevice>.
   1455    // We don't have a good way to account for lambda captures for MozPromise
   1456    // callbacks.
   1457    return amount;
   1458  }
   1459 
   1460 protected:
   1461  virtual ~GetUserMediaTask() = default;
   1462 
   1463  // Call GetPrincipalKey again, if not private browing, this time with
   1464  // persist = true, to promote deviceIds to persistent, in case they're not
   1465  // already. Fire'n'forget.
   1466  void PersistPrincipalKey() {
   1467    if (IsPrincipalInfoPrivate(mPrincipalInfo)) {
   1468      return;
   1469    }
   1470    media::GetPrincipalKey(mPrincipalInfo, true)
   1471        ->Then(
   1472            GetCurrentSerialEventTarget(), __func__,
   1473            [](const media::PrincipalKeyPromise::ResolveOrRejectValue& aValue) {
   1474              if (aValue.IsReject()) {
   1475                LOG("Failed get Principal key. Persisting of deviceIds "
   1476                    "will be broken");
   1477              }
   1478            });
   1479  }
   1480 
   1481 private:
   1482  // Thread-safe (object) principal of Window with ID mWindowID
   1483  const ipc::PrincipalInfo mPrincipalInfo;
   1484 
   1485 protected:
   1486  // The ID of the not-necessarily-toplevel inner Window relevant global
   1487  // object of the MediaDevices on which getUserMedia() was called
   1488  const uint64_t mWindowID;
   1489  // Whether the JS caller of getUserMedia() has system (subject) principal
   1490  const enum CallerType mCallerType;
   1491 };
   1492 
   1493 /**
   1494 * Describes a requested task that handles response from the UI to a
   1495 * getUserMedia() request and sends results back to content.  If the request
   1496 * is allowed and device initialization succeeds, then the MozPromise is
   1497 * resolved with a DOMMediaStream having a track or tracks for the approved
   1498 * device or devices.
   1499 */
   1500 class GetUserMediaStreamTask final : public GetUserMediaTask {
   1501 public:
   1502  GetUserMediaStreamTask(
   1503      const MediaStreamConstraints& aConstraints,
   1504      MozPromiseHolder<MediaManager::StreamPromise>&& aHolder,
   1505      uint64_t aWindowID, RefPtr<GetUserMediaWindowListener> aWindowListener,
   1506      RefPtr<DeviceListener> aAudioDeviceListener,
   1507      RefPtr<DeviceListener> aVideoDeviceListener,
   1508      const MediaEnginePrefs& aPrefs, const ipc::PrincipalInfo& aPrincipalInfo,
   1509      enum CallerType aCallerType, bool aShouldFocusSource)
   1510      : GetUserMediaTask(aWindowID, aPrincipalInfo, aCallerType),
   1511        mConstraints(aConstraints),
   1512        mHolder(std::move(aHolder)),
   1513        mWindowListener(std::move(aWindowListener)),
   1514        mAudioDeviceListener(std::move(aAudioDeviceListener)),
   1515        mVideoDeviceListener(std::move(aVideoDeviceListener)),
   1516        mPrefs(aPrefs),
   1517        mShouldFocusSource(aShouldFocusSource),
   1518        mManager(MediaManager::GetInstance()) {}
   1519 
   1520  void Allowed(RefPtr<LocalMediaDevice> aAudioDevice,
   1521               RefPtr<LocalMediaDevice> aVideoDevice) {
   1522    MOZ_ASSERT(aAudioDevice || aVideoDevice);
   1523    mAudioDevice = std::move(aAudioDevice);
   1524    mVideoDevice = std::move(aVideoDevice);
   1525    // Reuse the same thread to save memory.
   1526    MediaManager::Dispatch(
   1527        NewRunnableMethod("GetUserMediaStreamTask::AllocateDevices", this,
   1528                          &GetUserMediaStreamTask::AllocateDevices));
   1529  }
   1530 
   1531  GetUserMediaStreamTask* AsGetUserMediaStreamTask() override { return this; }
   1532 
   1533 private:
   1534  ~GetUserMediaStreamTask() override {
   1535    if (!mHolder.IsEmpty()) {
   1536      Fail(MediaMgrError::Name::NotAllowedError);
   1537    }
   1538  }
   1539 
   1540  void Fail(MediaMgrError::Name aName, const nsCString& aMessage = ""_ns,
   1541            const nsString& aConstraint = u""_ns) {
   1542    mHolder.Reject(MakeRefPtr<MediaMgrError>(aName, aMessage, aConstraint),
   1543                   __func__);
   1544    // We add a disabled listener to the StreamListeners array until accepted
   1545    // If this was the only active MediaStream, remove the window from the list.
   1546    NS_DispatchToMainThread(NS_NewRunnableFunction(
   1547        "DeviceListener::Stop",
   1548        [audio = mAudioDeviceListener, video = mVideoDeviceListener] {
   1549          if (audio) {
   1550            audio->Stop();
   1551          }
   1552          if (video) {
   1553            video->Stop();
   1554          }
   1555        }));
   1556  }
   1557 
   1558  /**
   1559   * Runs on a separate thread and is responsible for allocating devices.
   1560   *
   1561   * Do not run this on the main thread.
   1562   */
   1563  void AllocateDevices() {
   1564    MOZ_ASSERT(!NS_IsMainThread());
   1565    LOG("GetUserMediaStreamTask::AllocateDevices()");
   1566 
   1567    // Allocate a video or audio device and return a MediaStream via
   1568    // PrepareDOMStream().
   1569 
   1570    nsresult rv;
   1571    const char* errorMsg = nullptr;
   1572    const char* badConstraint = nullptr;
   1573 
   1574    if (mAudioDevice) {
   1575      auto& constraints = GetInvariant(mConstraints.mAudio);
   1576      rv = mAudioDevice->Allocate(constraints, mPrefs, mWindowID,
   1577                                  &badConstraint);
   1578      if (NS_FAILED(rv)) {
   1579        errorMsg = "Failed to allocate audiosource";
   1580        if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
   1581          nsTArray<RefPtr<LocalMediaDevice>> devices;
   1582          devices.AppendElement(mAudioDevice);
   1583          badConstraint = MediaConstraintsHelper::SelectSettings(
   1584              NormalizedConstraints(constraints), mPrefs, devices, mCallerType);
   1585        }
   1586      }
   1587    }
   1588    if (!errorMsg && mVideoDevice) {
   1589      auto& constraints = GetInvariant(mConstraints.mVideo);
   1590      rv = mVideoDevice->Allocate(constraints, mPrefs, mWindowID,
   1591                                  &badConstraint);
   1592      if (NS_FAILED(rv)) {
   1593        errorMsg = "Failed to allocate videosource";
   1594        if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
   1595          nsTArray<RefPtr<LocalMediaDevice>> devices;
   1596          devices.AppendElement(mVideoDevice);
   1597          badConstraint = MediaConstraintsHelper::SelectSettings(
   1598              NormalizedConstraints(constraints), mPrefs, devices, mCallerType);
   1599        }
   1600        if (mAudioDevice) {
   1601          mAudioDevice->Deallocate();
   1602        }
   1603      } else {
   1604        mVideoTrackingId.emplace(mVideoDevice->GetTrackingId());
   1605      }
   1606    }
   1607    if (errorMsg) {
   1608      LOG("%s %" PRIu32, errorMsg, static_cast<uint32_t>(rv));
   1609      if (badConstraint) {
   1610        Fail(MediaMgrError::Name::OverconstrainedError, ""_ns,
   1611             NS_ConvertUTF8toUTF16(badConstraint));
   1612      } else {
   1613        Fail(MediaMgrError::Name::NotReadableError, nsCString(errorMsg));
   1614      }
   1615      NS_DispatchToMainThread(
   1616          NS_NewRunnableFunction("MediaManager::SendPendingGUMRequest", []() {
   1617            if (MediaManager* manager = MediaManager::GetIfExists()) {
   1618              manager->SendPendingGUMRequest();
   1619            }
   1620          }));
   1621      return;
   1622    }
   1623    NS_DispatchToMainThread(
   1624        NewRunnableMethod("GetUserMediaStreamTask::PrepareDOMStream", this,
   1625                          &GetUserMediaStreamTask::PrepareDOMStream));
   1626  }
   1627 
   1628 public:
   1629  void Denied(MediaMgrError::Name aName, const nsCString& aMessage) override {
   1630    MOZ_ASSERT(NS_IsMainThread());
   1631    Fail(aName, aMessage);
   1632  }
   1633 
   1634  const MediaStreamConstraints& GetConstraints() { return mConstraints; }
   1635 
   1636  void PrimeVoiceProcessing() {
   1637    mPrimingStream = MakeAndAddRef<PrimingCubebVoiceInputStream>();
   1638    mPrimingStream->Init();
   1639  }
   1640 
   1641 private:
   1642  void PrepareDOMStream();
   1643 
   1644  class PrimingCubebVoiceInputStream {
   1645    class Listener final : public CubebInputStream::Listener {
   1646      NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Listener, override);
   1647 
   1648     private:
   1649      ~Listener() = default;
   1650 
   1651      long DataCallback(const void*, long) override {
   1652        MOZ_CRASH("Unexpected data callback");
   1653      }
   1654      void StateCallback(cubeb_state) override {}
   1655      void DeviceChangedCallback() override {}
   1656    };
   1657 
   1658    NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET(
   1659        PrimingCubebVoiceInputStream, mCubebThread.GetEventTarget())
   1660 
   1661   public:
   1662    void Init() {
   1663      mCubebThread.GetEventTarget()->Dispatch(
   1664          NS_NewRunnableFunction(__func__, [this, self = RefPtr(this)] {
   1665            mCubebThread.AssertOnCurrentThread();
   1666            LOG("Priming voice processing with stream %p", this);
   1667            TRACE("PrimingCubebVoiceInputStream::Init");
   1668            const cubeb_devid default_device = nullptr;
   1669            const uint32_t mono = 1;
   1670            const uint32_t rate = CubebUtils::PreferredSampleRate(false);
   1671            const bool isVoice = true;
   1672            mCubebStream =
   1673                CubebInputStream::Create(default_device, mono, rate, isVoice,
   1674                                         MakeRefPtr<Listener>().get());
   1675          }));
   1676    }
   1677 
   1678   private:
   1679    ~PrimingCubebVoiceInputStream() {
   1680      mCubebThread.AssertOnCurrentThread();
   1681      LOG("Releasing primed voice processing stream %p", this);
   1682      mCubebStream = nullptr;
   1683    }
   1684 
   1685    const EventTargetCapability<nsISerialEventTarget> mCubebThread =
   1686        EventTargetCapability<nsISerialEventTarget>(
   1687            TaskQueue::Create(CubebUtils::GetCubebOperationThread(),
   1688                              "PrimingCubebInputStream::mCubebThread")
   1689                .get());
   1690    UniquePtr<CubebInputStream> mCubebStream MOZ_GUARDED_BY(mCubebThread);
   1691  };
   1692 
   1693  // Constraints derived from those passed to getUserMedia() but adjusted for
   1694  // preferences, defaults, and security
   1695  const MediaStreamConstraints mConstraints;
   1696 
   1697  MozPromiseHolder<MediaManager::StreamPromise> mHolder;
   1698  // GetUserMediaWindowListener with which DeviceListeners are registered
   1699  const RefPtr<GetUserMediaWindowListener> mWindowListener;
   1700  const RefPtr<DeviceListener> mAudioDeviceListener;
   1701  const RefPtr<DeviceListener> mVideoDeviceListener;
   1702  // MediaDevices are set when selected and Allowed() by the UI.
   1703  RefPtr<LocalMediaDevice> mAudioDevice;
   1704  RefPtr<LocalMediaDevice> mVideoDevice;
   1705  RefPtr<PrimingCubebVoiceInputStream> mPrimingStream;
   1706  // Tracking id unique for a video frame source. Set when the corresponding
   1707  // device has been allocated.
   1708  Maybe<TrackingId> mVideoTrackingId;
   1709  // Copy of MediaManager::mPrefs
   1710  const MediaEnginePrefs mPrefs;
   1711  // media.getusermedia.window.focus_source.enabled
   1712  const bool mShouldFocusSource;
   1713  // The MediaManager is referenced at construction so that it won't be
   1714  // created after its ShutdownBlocker would run.
   1715  const RefPtr<MediaManager> mManager;
   1716 };
   1717 
   1718 /**
   1719 * Creates a MediaTrack, attaches a listener and resolves a MozPromise to
   1720 * provide the stream to the DOM.
   1721 *
   1722 * All of this must be done on the main thread!
   1723 */
   1724 void GetUserMediaStreamTask::PrepareDOMStream() {
   1725  MOZ_ASSERT(NS_IsMainThread());
   1726  LOG("GetUserMediaStreamTask::PrepareDOMStream()");
   1727  nsGlobalWindowInner* window =
   1728      nsGlobalWindowInner::GetInnerWindowWithId(mWindowID);
   1729 
   1730  // We're on main-thread, and the windowlist can only
   1731  // be invalidated from the main-thread (see OnNavigation)
   1732  if (!mManager->IsWindowListenerStillActive(mWindowListener)) {
   1733    // This window is no longer live. mListener has already been removed.
   1734    return;
   1735  }
   1736 
   1737  MediaTrackGraph::GraphDriverType graphDriverType =
   1738      mAudioDevice ? MediaTrackGraph::AUDIO_THREAD_DRIVER
   1739                   : MediaTrackGraph::SYSTEM_THREAD_DRIVER;
   1740  MediaTrackGraph* mtg = MediaTrackGraph::GetInstance(
   1741      graphDriverType, window, MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
   1742      MediaTrackGraph::DEFAULT_OUTPUT_DEVICE);
   1743 
   1744  auto domStream = MakeRefPtr<DOMMediaStream>(window);
   1745  RefPtr<LocalTrackSource> audioTrackSource;
   1746  RefPtr<LocalTrackSource> videoTrackSource;
   1747  nsCOMPtr<nsIPrincipal> principal;
   1748  RefPtr<PeerIdentity> peerIdentity = nullptr;
   1749  if (!mConstraints.mPeerIdentity.IsEmpty()) {
   1750    peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
   1751    principal = NullPrincipal::CreateWithInheritedAttributes(
   1752        window->GetExtantDoc()->NodePrincipal());
   1753  } else {
   1754    principal = window->GetExtantDoc()->NodePrincipal();
   1755  }
   1756  RefPtr<GenericNonExclusivePromise> firstFramePromise;
   1757  if (mAudioDevice) {
   1758    if (mAudioDevice->GetMediaSource() == MediaSourceEnum::AudioCapture) {
   1759      // AudioCapture is a special case, here, in the sense that we're not
   1760      // really using the audio source and the SourceMediaTrack, which acts
   1761      // as placeholders. We re-route a number of tracks internally in the
   1762      // MTG and mix them down instead.
   1763      NS_WARNING(
   1764          "MediaCaptureWindowState doesn't handle "
   1765          "MediaSourceEnum::AudioCapture. This must be fixed with UX "
   1766          "before shipping.");
   1767      auto audioCaptureSource = MakeRefPtr<AudioCaptureTrackSource>(
   1768          principal, window, u"Window audio capture"_ns,
   1769          mtg->CreateAudioCaptureTrack(), peerIdentity);
   1770      audioTrackSource = audioCaptureSource;
   1771      RefPtr<MediaStreamTrack> track = new dom::AudioStreamTrack(
   1772          window, audioCaptureSource->InputTrack(), audioCaptureSource);
   1773      domStream->AddTrackInternal(track);
   1774    } else {
   1775      const nsString& audioDeviceName = mAudioDevice->mName;
   1776      RefPtr<MediaTrack> track;
   1777 #ifdef MOZ_WEBRTC
   1778      if (mAudioDevice->IsFake()) {
   1779        track = mtg->CreateSourceTrack(MediaSegment::AUDIO);
   1780      } else {
   1781        track = AudioProcessingTrack::Create(mtg);
   1782        track->Suspend();  // Microphone source resumes in SetTrack
   1783      }
   1784 #else
   1785      track = mtg->CreateSourceTrack(MediaSegment::AUDIO);
   1786 #endif
   1787      audioTrackSource = new LocalTrackSource(
   1788          principal, audioDeviceName, mAudioDeviceListener,
   1789          mAudioDevice->GetMediaSource(), track, peerIdentity);
   1790      MOZ_ASSERT(MediaManager::IsOn(mConstraints.mAudio));
   1791      RefPtr<MediaStreamTrack> domTrack = new dom::AudioStreamTrack(
   1792          window, track, audioTrackSource, dom::MediaStreamTrackState::Live,
   1793          false, GetInvariant(mConstraints.mAudio));
   1794      domStream->AddTrackInternal(domTrack);
   1795    }
   1796  }
   1797  if (mVideoDevice) {
   1798    const nsString& videoDeviceName = mVideoDevice->mName;
   1799    RefPtr<MediaTrack> track = mtg->CreateSourceTrack(MediaSegment::VIDEO);
   1800    videoTrackSource = new LocalTrackSource(
   1801        principal, videoDeviceName, mVideoDeviceListener,
   1802        mVideoDevice->GetMediaSource(), track, peerIdentity, *mVideoTrackingId);
   1803    MOZ_ASSERT(MediaManager::IsOn(mConstraints.mVideo));
   1804    RefPtr<MediaStreamTrack> domTrack = new dom::VideoStreamTrack(
   1805        window, track, videoTrackSource, dom::MediaStreamTrackState::Live,
   1806        false, GetInvariant(mConstraints.mVideo));
   1807    domStream->AddTrackInternal(domTrack);
   1808    switch (mVideoDevice->GetMediaSource()) {
   1809      case MediaSourceEnum::Browser:
   1810      case MediaSourceEnum::Screen:
   1811      case MediaSourceEnum::Window:
   1812        // Wait for first frame for screen-sharing devices, to ensure
   1813        // with and height settings are available immediately, to pass wpt.
   1814        firstFramePromise = mVideoDevice->Source()->GetFirstFramePromise();
   1815        break;
   1816      default:
   1817        break;
   1818    }
   1819  }
   1820 
   1821  if (!domStream || (!audioTrackSource && !videoTrackSource) ||
   1822      sHasMainThreadShutdown) {
   1823    LOG("Returning error for getUserMedia() - no stream");
   1824 
   1825    mHolder.Reject(
   1826        MakeRefPtr<MediaMgrError>(
   1827            MediaMgrError::Name::AbortError,
   1828            sHasMainThreadShutdown ? "In shutdown"_ns : "No stream."_ns),
   1829        __func__);
   1830    return;
   1831  }
   1832 
   1833  // Activate our device listeners. We'll call Start() on the source when we
   1834  // get a callback that the MediaStream has started consuming. The listener
   1835  // is freed when the page is invalidated (on navigation or close).
   1836  if (mAudioDeviceListener) {
   1837    mWindowListener->Activate(mAudioDeviceListener, mAudioDevice,
   1838                              std::move(audioTrackSource),
   1839                              /*aIsAllocated=*/true);
   1840  }
   1841  if (mVideoDeviceListener) {
   1842    mWindowListener->Activate(mVideoDeviceListener, mVideoDevice,
   1843                              std::move(videoTrackSource),
   1844                              /*aIsAllocated=*/true);
   1845  }
   1846 
   1847  // Dispatch to the media thread to ask it to start the sources, because that
   1848  // can take a while.
   1849  typedef DeviceListener::DeviceListenerPromise PromiseType;
   1850  AutoTArray<RefPtr<PromiseType>, 2> promises;
   1851  if (mAudioDeviceListener) {
   1852    promises.AppendElement(mAudioDeviceListener->InitializeAsync());
   1853  }
   1854  if (mVideoDeviceListener) {
   1855    promises.AppendElement(mVideoDeviceListener->InitializeAsync());
   1856  }
   1857  PromiseType::All(GetMainThreadSerialEventTarget(), promises)
   1858      ->Then(
   1859          GetMainThreadSerialEventTarget(), __func__,
   1860          [manager = mManager, windowListener = mWindowListener,
   1861           firstFramePromise] {
   1862            LOG("GetUserMediaStreamTask::PrepareDOMStream: starting success "
   1863                "callback following InitializeAsync()");
   1864            // Initiating and starting devices succeeded.
   1865            windowListener->ChromeAffectingStateChanged();
   1866            manager->SendPendingGUMRequest();
   1867            if (!firstFramePromise) {
   1868              return DeviceListener::DeviceListenerPromise::CreateAndResolve(
   1869                  true, __func__);
   1870            }
   1871            RefPtr<DeviceListener::DeviceListenerPromise> resolvePromise =
   1872                firstFramePromise->Then(
   1873                    GetMainThreadSerialEventTarget(), __func__,
   1874                    [] {
   1875                      return DeviceListener::DeviceListenerPromise::
   1876                          CreateAndResolve(true, __func__);
   1877                    },
   1878                    [](nsresult aError) {
   1879                      MOZ_ASSERT(NS_FAILED(aError));
   1880                      if (aError == NS_ERROR_UNEXPECTED) {
   1881                        return DeviceListener::DeviceListenerPromise::
   1882                            CreateAndReject(
   1883                                MakeRefPtr<MediaMgrError>(
   1884                                    MediaMgrError::Name::NotAllowedError),
   1885                                __func__);
   1886                      }
   1887                      MOZ_ASSERT(aError == NS_ERROR_ABORT);
   1888                      return DeviceListener::DeviceListenerPromise::
   1889                          CreateAndReject(MakeRefPtr<MediaMgrError>(
   1890                                              MediaMgrError::Name::AbortError,
   1891                                              "In shutdown"),
   1892                                          __func__);
   1893                    });
   1894            return resolvePromise;
   1895          },
   1896          [audio = mAudioDeviceListener,
   1897           video = mVideoDeviceListener](const RefPtr<MediaMgrError>& aError) {
   1898            LOG("GetUserMediaStreamTask::PrepareDOMStream: starting failure "
   1899                "callback following InitializeAsync()");
   1900            if (audio) {
   1901              audio->Stop();
   1902            }
   1903            if (video) {
   1904              video->Stop();
   1905            }
   1906            return DeviceListener::DeviceListenerPromise::CreateAndReject(
   1907                aError, __func__);
   1908          })
   1909      ->Then(
   1910          GetMainThreadSerialEventTarget(), __func__,
   1911          [holder = std::move(mHolder), domStream, callerType = mCallerType,
   1912           shouldFocus = mShouldFocusSource, videoDevice = mVideoDevice](
   1913              const DeviceListener::DeviceListenerPromise::ResolveOrRejectValue&
   1914                  aValue) mutable {
   1915            if (aValue.IsResolve()) {
   1916              if (auto* mgr = MediaManager::GetIfExists();
   1917                  mgr && !sHasMainThreadShutdown && videoDevice &&
   1918                  callerType == CallerType::NonSystem && shouldFocus) {
   1919                // Device was successfully started. Attempt to focus the
   1920                // source.
   1921                MOZ_ALWAYS_SUCCEEDS(
   1922                    mgr->mMediaThread->Dispatch(NS_NewRunnableFunction(
   1923                        "GetUserMediaStreamTask::FocusOnSelectedSource",
   1924                        [videoDevice = std::move(videoDevice)] {
   1925                          nsresult rv = videoDevice->FocusOnSelectedSource();
   1926                          if (NS_FAILED(rv)) {
   1927                            LOG("FocusOnSelectedSource failed");
   1928                          }
   1929                        })));
   1930              }
   1931 
   1932              holder.Resolve(domStream, __func__);
   1933            } else {
   1934              holder.Reject(aValue.RejectValue(), __func__);
   1935            }
   1936          });
   1937 
   1938  PersistPrincipalKey();
   1939 }
   1940 
   1941 /**
   1942 * Describes a requested task that handles response from the UI to a
   1943 * selectAudioOutput() request and sends results back to content.  If the
   1944 * request is allowed, then the MozPromise is resolved with a MediaDevice
   1945 * for the approved device.
   1946 */
   1947 class SelectAudioOutputTask final : public GetUserMediaTask {
   1948 public:
   1949  SelectAudioOutputTask(MozPromiseHolder<LocalDevicePromise>&& aHolder,
   1950                        uint64_t aWindowID, enum CallerType aCallerType,
   1951                        const ipc::PrincipalInfo& aPrincipalInfo)
   1952      : GetUserMediaTask(aWindowID, aPrincipalInfo, aCallerType),
   1953        mHolder(std::move(aHolder)) {}
   1954 
   1955  void Allowed(RefPtr<LocalMediaDevice> aAudioOutput) {
   1956    MOZ_ASSERT(aAudioOutput);
   1957    mHolder.Resolve(std::move(aAudioOutput), __func__);
   1958    PersistPrincipalKey();
   1959  }
   1960 
   1961  void Denied(MediaMgrError::Name aName, const nsCString& aMessage) override {
   1962    MOZ_ASSERT(NS_IsMainThread());
   1963    Fail(aName, aMessage);
   1964  }
   1965 
   1966  SelectAudioOutputTask* AsSelectAudioOutputTask() override { return this; }
   1967 
   1968 private:
   1969  ~SelectAudioOutputTask() override {
   1970    if (!mHolder.IsEmpty()) {
   1971      Fail(MediaMgrError::Name::NotAllowedError);
   1972    }
   1973  }
   1974 
   1975  void Fail(MediaMgrError::Name aName, const nsCString& aMessage = ""_ns) {
   1976    mHolder.Reject(MakeRefPtr<MediaMgrError>(aName, aMessage), __func__);
   1977  }
   1978 
   1979 private:
   1980  MozPromiseHolder<LocalDevicePromise> mHolder;
   1981 };
   1982 
   1983 /* static */
   1984 void MediaManager::GuessVideoDeviceGroupIDs(MediaDeviceSet& aDevices,
   1985                                            const MediaDeviceSet& aAudios) {
   1986  // Run the logic in a lambda to avoid duplication.
   1987  auto updateGroupIdIfNeeded = [&](RefPtr<MediaDevice>& aVideo,
   1988                                   const MediaDeviceKind aKind) -> bool {
   1989    MOZ_ASSERT(aVideo->mKind == MediaDeviceKind::Videoinput);
   1990    MOZ_ASSERT(aKind == MediaDeviceKind::Audioinput ||
   1991               aKind == MediaDeviceKind::Audiooutput);
   1992    // This will store the new group id if a match is found.
   1993    nsString newVideoGroupID;
   1994    // If the group id needs to be updated this will become true. It is
   1995    // necessary when the new group id is an empty string. Without this extra
   1996    // variable to signal the update, we would resort to test if
   1997    // `newVideoGroupId` is empty. However,
   1998    // that check does not work when the new group id is an empty string.
   1999    bool updateGroupId = false;
   2000    for (const RefPtr<MediaDevice>& dev : aAudios) {
   2001      if (dev->mKind != aKind) {
   2002        continue;
   2003      }
   2004      if (!FindInReadable(aVideo->mRawName, dev->mRawName)) {
   2005        continue;
   2006      }
   2007      if (newVideoGroupID.IsEmpty()) {
   2008        // This is only expected on first match. If that's the only match group
   2009        // id will be updated to this one at the end of the loop.
   2010        updateGroupId = true;
   2011        newVideoGroupID = dev->mRawGroupID;
   2012      } else {
   2013        // More than one device found, it is impossible to know which group id
   2014        // is the correct one.
   2015        updateGroupId = false;
   2016        newVideoGroupID = u""_ns;
   2017        break;
   2018      }
   2019    }
   2020    if (updateGroupId) {
   2021      aVideo = MediaDevice::CopyWithNewRawGroupId(aVideo, newVideoGroupID);
   2022      return true;
   2023    }
   2024    return false;
   2025  };
   2026 
   2027  for (RefPtr<MediaDevice>& video : aDevices) {
   2028    if (video->mKind != MediaDeviceKind::Videoinput) {
   2029      continue;
   2030    }
   2031    if (updateGroupIdIfNeeded(video, MediaDeviceKind::Audioinput)) {
   2032      // GroupId has been updated, continue to the next video device
   2033      continue;
   2034    }
   2035    // GroupId has not been updated, check among the outputs
   2036    updateGroupIdIfNeeded(video, MediaDeviceKind::Audiooutput);
   2037  }
   2038 }
   2039 
   2040 namespace {
   2041 
   2042 // Class to hold the promise used to request device access and to resolve
   2043 // even if |task| does not run, either because GeckoViewPermissionProcessChild
   2044 // gets destroyed before ask-device-permission receives its
   2045 // got-device-permission reply, or because the media thread is no longer
   2046 // available.  In either case, the process is shutting down so the result is
   2047 // not important.  Reject with a dummy error so the following Then-handler can
   2048 // resolve with an empty set, so that callers do not need to handle rejection.
   2049 class DeviceAccessRequestPromiseHolderWithFallback
   2050    : public MozPromiseHolder<MozPromise<
   2051          CamerasAccessStatus, mozilla::ipc::ResponseRejectReason, true>> {
   2052 public:
   2053  DeviceAccessRequestPromiseHolderWithFallback() = default;
   2054  DeviceAccessRequestPromiseHolderWithFallback(
   2055      DeviceAccessRequestPromiseHolderWithFallback&&) = default;
   2056  ~DeviceAccessRequestPromiseHolderWithFallback() {
   2057    if (!IsEmpty()) {
   2058      Reject(ipc::ResponseRejectReason::ChannelClosed, __func__);
   2059    }
   2060  }
   2061 };
   2062 
   2063 }  // anonymous namespace
   2064 
   2065 MediaManager::DeviceEnumerationParams::DeviceEnumerationParams(
   2066    dom::MediaSourceEnum aInputType, DeviceType aType,
   2067    nsAutoCString aForcedDeviceName)
   2068    : mInputType(aInputType),
   2069      mType(aType),
   2070      mForcedDeviceName(std::move(aForcedDeviceName)) {
   2071  MOZ_ASSERT(NS_IsMainThread());
   2072  MOZ_ASSERT(mInputType != dom::MediaSourceEnum::Other);
   2073  MOZ_ASSERT_IF(!mForcedDeviceName.IsEmpty(), mType == DeviceType::Real);
   2074 }
   2075 
   2076 MediaManager::VideoDeviceEnumerationParams::VideoDeviceEnumerationParams(
   2077    dom::MediaSourceEnum aInputType, DeviceType aType,
   2078    nsAutoCString aForcedDeviceName, nsAutoCString aForcedMicrophoneName)
   2079    : DeviceEnumerationParams(aInputType, aType, std::move(aForcedDeviceName)),
   2080      mForcedMicrophoneName(std::move(aForcedMicrophoneName)) {
   2081  MOZ_ASSERT(NS_IsMainThread());
   2082  MOZ_ASSERT_IF(!mForcedMicrophoneName.IsEmpty(),
   2083                mInputType == dom::MediaSourceEnum::Camera);
   2084  MOZ_ASSERT_IF(!mForcedMicrophoneName.IsEmpty(), mType == DeviceType::Real);
   2085 }
   2086 
   2087 MediaManager::EnumerationParams::EnumerationParams(
   2088    EnumerationFlags aFlags, Maybe<VideoDeviceEnumerationParams> aVideo,
   2089    Maybe<DeviceEnumerationParams> aAudio)
   2090    : mFlags(aFlags), mVideo(std::move(aVideo)), mAudio(std::move(aAudio)) {
   2091  MOZ_ASSERT(NS_IsMainThread());
   2092  MOZ_ASSERT_IF(mVideo, MediaEngineSource::IsVideo(mVideo->mInputType));
   2093  MOZ_ASSERT_IF(mVideo && !mVideo->mForcedDeviceName.IsEmpty(),
   2094                mVideo->mInputType == dom::MediaSourceEnum::Camera);
   2095  MOZ_ASSERT_IF(mVideo && mVideo->mType == DeviceType::Fake,
   2096                mVideo->mInputType == dom::MediaSourceEnum::Camera);
   2097  MOZ_ASSERT_IF(mAudio, MediaEngineSource::IsAudio(mAudio->mInputType));
   2098  MOZ_ASSERT_IF(mAudio && !mAudio->mForcedDeviceName.IsEmpty(),
   2099                mAudio->mInputType == dom::MediaSourceEnum::Microphone);
   2100  MOZ_ASSERT_IF(mAudio && mAudio->mType == DeviceType::Fake,
   2101                mAudio->mInputType == dom::MediaSourceEnum::Microphone);
   2102 }
   2103 
   2104 bool MediaManager::EnumerationParams::HasFakeCams() const {
   2105  return mVideo
   2106      .map([](const auto& aDev) { return aDev.mType == DeviceType::Fake; })
   2107      .valueOr(false);
   2108 }
   2109 
   2110 bool MediaManager::EnumerationParams::HasFakeMics() const {
   2111  return mAudio
   2112      .map([](const auto& aDev) { return aDev.mType == DeviceType::Fake; })
   2113      .valueOr(false);
   2114 }
   2115 
   2116 bool MediaManager::EnumerationParams::RealDeviceRequested() const {
   2117  auto isReal = [](const auto& aDev) { return aDev.mType == DeviceType::Real; };
   2118  return mVideo.map(isReal).valueOr(false) ||
   2119         mAudio.map(isReal).valueOr(false) ||
   2120         mFlags.contains(EnumerationFlag::EnumerateAudioOutputs);
   2121 }
   2122 
   2123 MediaSourceEnum MediaManager::EnumerationParams::VideoInputType() const {
   2124  return mVideo.map([](const auto& aDev) { return aDev.mInputType; })
   2125      .valueOr(MediaSourceEnum::Other);
   2126 }
   2127 
   2128 MediaSourceEnum MediaManager::EnumerationParams::AudioInputType() const {
   2129  return mAudio.map([](const auto& aDev) { return aDev.mInputType; })
   2130      .valueOr(MediaSourceEnum::Other);
   2131 }
   2132 
   2133 /* static */ MediaManager::EnumerationParams
   2134 MediaManager::CreateEnumerationParams(dom::MediaSourceEnum aVideoInputType,
   2135                                      dom::MediaSourceEnum aAudioInputType,
   2136                                      EnumerationFlags aFlags) {
   2137  MOZ_ASSERT(NS_IsMainThread());
   2138  MOZ_ASSERT_IF(!MediaEngineSource::IsVideo(aVideoInputType),
   2139                aVideoInputType == dom::MediaSourceEnum::Other);
   2140  MOZ_ASSERT_IF(!MediaEngineSource::IsAudio(aAudioInputType),
   2141                aAudioInputType == dom::MediaSourceEnum::Other);
   2142  const bool forceFakes = aFlags.contains(EnumerationFlag::ForceFakes);
   2143  const bool fakeByPref = Preferences::GetBool("media.navigator.streams.fake");
   2144  Maybe<VideoDeviceEnumerationParams> videoParams;
   2145  Maybe<DeviceEnumerationParams> audioParams;
   2146  nsAutoCString audioDev;
   2147  bool audioDevRead = false;
   2148  constexpr const char* VIDEO_DEV_NAME = "media.video_loopback_dev";
   2149  constexpr const char* AUDIO_DEV_NAME = "media.audio_loopback_dev";
   2150  const auto ensureDev = [](const char* aPref, nsAutoCString* aLoopDev,
   2151                            bool* aPrefRead) {
   2152    if (aPrefRead) {
   2153      if (*aPrefRead) {
   2154        return;
   2155      }
   2156      *aPrefRead = true;
   2157    }
   2158 
   2159    if (NS_FAILED(Preferences::GetCString(aPref, *aLoopDev))) {
   2160      // Ensure we fall back to an empty string if reading the pref failed.
   2161      aLoopDev->SetIsVoid(true);
   2162    }
   2163  };
   2164  if (MediaEngineSource::IsVideo(aVideoInputType)) {
   2165    nsAutoCString videoDev;
   2166    DeviceType type = DeviceType::Real;
   2167    if (aVideoInputType == MediaSourceEnum::Camera) {
   2168      // Fake and loopback devices are supported for only Camera.
   2169      if (forceFakes) {
   2170        type = DeviceType::Fake;
   2171      } else {
   2172        ensureDev(VIDEO_DEV_NAME, &videoDev, nullptr);
   2173        // Loopback prefs take precedence over fake prefs
   2174        if (fakeByPref && videoDev.IsEmpty()) {
   2175          type = DeviceType::Fake;
   2176        } else {
   2177          // For groupId correlation we need the audio device name.
   2178          ensureDev(AUDIO_DEV_NAME, &audioDev, &audioDevRead);
   2179        }
   2180      }
   2181    }
   2182    videoParams = Some(VideoDeviceEnumerationParams(aVideoInputType, type,
   2183                                                    videoDev, audioDev));
   2184  }
   2185  if (MediaEngineSource::IsAudio(aAudioInputType)) {
   2186    nsAutoCString realAudioDev;
   2187    DeviceType type = DeviceType::Real;
   2188    if (aAudioInputType == MediaSourceEnum::Microphone) {
   2189      // Fake and loopback devices are supported for only Microphone.
   2190      if (forceFakes) {
   2191        type = DeviceType::Fake;
   2192      } else {
   2193        ensureDev(AUDIO_DEV_NAME, &audioDev, &audioDevRead);
   2194        // Loopback prefs take precedence over fake prefs
   2195        if (fakeByPref && audioDev.IsEmpty()) {
   2196          type = DeviceType::Fake;
   2197        } else {
   2198          realAudioDev = audioDev;
   2199        }
   2200      }
   2201    }
   2202    audioParams =
   2203        Some(DeviceEnumerationParams(aAudioInputType, type, realAudioDev));
   2204  }
   2205  return EnumerationParams(aFlags, videoParams, audioParams);
   2206 }
   2207 
   2208 RefPtr<DeviceSetPromise>
   2209 MediaManager::MaybeRequestPermissionAndEnumerateRawDevices(
   2210    EnumerationParams aParams) {
   2211  MOZ_ASSERT(NS_IsMainThread());
   2212  MOZ_ASSERT(aParams.mVideo.isSome() || aParams.mAudio.isSome() ||
   2213             aParams.mFlags.contains(EnumerationFlag::EnumerateAudioOutputs));
   2214 
   2215  LOG("%s: aVideoInputType=%" PRIu8 ", aAudioInputType=%" PRIu8, __func__,
   2216      static_cast<uint8_t>(aParams.VideoInputType()),
   2217      static_cast<uint8_t>(aParams.AudioInputType()));
   2218 
   2219  if (sHasMainThreadShutdown) {
   2220    // The media thread is no longer available but the result will not be
   2221    // observable.
   2222    return DeviceSetPromise::CreateAndResolve(
   2223        new MediaDeviceSetRefCnt(),
   2224        "MaybeRequestPermissionAndEnumerateRawDevices: sync shutdown");
   2225  }
   2226 
   2227  const bool hasVideo = aParams.mVideo.isSome();
   2228  const bool hasAudio = aParams.mAudio.isSome();
   2229  const bool hasAudioOutput =
   2230      aParams.mFlags.contains(EnumerationFlag::EnumerateAudioOutputs);
   2231  const bool hasFakeCams = aParams.HasFakeCams();
   2232  const bool hasFakeMics = aParams.HasFakeMics();
   2233  // True if at least one of video input or audio input is a real device
   2234  // or there is audio output.
   2235  const bool realDeviceRequested = (!hasFakeCams && hasVideo) ||
   2236                                   (!hasFakeMics && hasAudio) || hasAudioOutput;
   2237 
   2238  using NativePromise =
   2239      MozPromise<CamerasAccessStatus, mozilla::ipc::ResponseRejectReason,
   2240                 /* IsExclusive = */ true>;
   2241  RefPtr<NativePromise> deviceAccessPromise;
   2242  if (realDeviceRequested &&
   2243      aParams.mFlags.contains(EnumerationFlag::AllowPermissionRequest) &&
   2244      Preferences::GetBool("media.navigator.permission.device", false)) {
   2245    // Need to ask permission to retrieve list of all devices;
   2246    // notify frontend observer and wait for callback notification to post
   2247    // task.
   2248    const char16_t* const type =
   2249        (aParams.VideoInputType() != MediaSourceEnum::Camera)       ? u"audio"
   2250        : (aParams.AudioInputType() != MediaSourceEnum::Microphone) ? u"video"
   2251                                                                    : u"all";
   2252    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   2253    DeviceAccessRequestPromiseHolderWithFallback deviceAccessPromiseHolder;
   2254    deviceAccessPromise = deviceAccessPromiseHolder.Ensure(__func__);
   2255    RefPtr task = NS_NewRunnableFunction(
   2256        __func__, [holder = std::move(deviceAccessPromiseHolder)]() mutable {
   2257          holder.Resolve(CamerasAccessStatus::Granted,
   2258                         "getUserMedia:got-device-permission");
   2259        });
   2260    obs->NotifyObservers(static_cast<nsIRunnable*>(task),
   2261                         "getUserMedia:ask-device-permission", type);
   2262  } else if (realDeviceRequested && hasVideo &&
   2263             aParams.VideoInputType() == MediaSourceEnum::Camera) {
   2264    ipc::PBackgroundChild* backgroundChild =
   2265        ipc::BackgroundChild::GetOrCreateForCurrentThread();
   2266    deviceAccessPromise = backgroundChild->SendRequestCameraAccess(
   2267        aParams.mFlags.contains(EnumerationFlag::AllowPermissionRequest));
   2268  }
   2269 
   2270  if (!deviceAccessPromise) {
   2271    // No device access request needed. We can proceed directly, but we still
   2272    // need to update camera availability, because the camera engine is always
   2273    // created together with the WebRTC backend, which is done because
   2274    // devicechange events must work before prompting in cases where persistent
   2275    // permission has already been given. Making a request to camera access not
   2276    // allowing a permission request does exactly what we need in this case.
   2277    ipc::PBackgroundChild* backgroundChild =
   2278        ipc::BackgroundChild::GetOrCreateForCurrentThread();
   2279    deviceAccessPromise = backgroundChild->SendRequestCameraAccess(false);
   2280  }
   2281 
   2282  return deviceAccessPromise->Then(
   2283      GetCurrentSerialEventTarget(), __func__,
   2284      [this, self = RefPtr(this), aParams = std::move(aParams)](
   2285          NativePromise::ResolveOrRejectValue&& aValue) mutable {
   2286        if (sHasMainThreadShutdown) {
   2287          return DeviceSetPromise::CreateAndResolve(
   2288              new MediaDeviceSetRefCnt(),
   2289              "MaybeRequestPermissionAndEnumerateRawDevices: async shutdown");
   2290        }
   2291 
   2292        if (aValue.IsReject()) {
   2293          // IPC failure probably means we're in shutdown. Resolve with
   2294          // an empty set, so that callers do not need to handle rejection.
   2295          return DeviceSetPromise::CreateAndResolve(
   2296              new MediaDeviceSetRefCnt(),
   2297              "MaybeRequestPermissionAndEnumerateRawDevices: ipc failure");
   2298        }
   2299 
   2300        if (auto v = aValue.ResolveValue();
   2301            v == CamerasAccessStatus::Error ||
   2302            v == CamerasAccessStatus::Rejected) {
   2303          LOG("Request to camera access %s",
   2304              v == CamerasAccessStatus::Rejected ? "was rejected" : "failed");
   2305          if (v == CamerasAccessStatus::Error) {
   2306            NS_WARNING("Failed to request camera access");
   2307          }
   2308          return DeviceSetPromise::CreateAndReject(
   2309              MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
   2310              "MaybeRequestPermissionAndEnumerateRawDevices: camera access "
   2311              "rejected");
   2312        }
   2313 
   2314        // We have to nest this, unfortunately, since we have no guarantees that
   2315        // mMediaThread is alive. If we'd reject due to shutdown above, and have
   2316        // the below async operation in a Then handler on the media thread the
   2317        // Then handler would fail to dispatch and trip an assert on
   2318        // destruction, for instance.
   2319        return InvokeAsync(
   2320            mMediaThread, __func__, [aParams = std::move(aParams)]() mutable {
   2321              return DeviceSetPromise::CreateAndResolve(
   2322                  EnumerateRawDevices(std::move(aParams)),
   2323                  "MaybeRequestPermissionAndEnumerateRawDevices: success");
   2324            });
   2325      });
   2326 }
   2327 
   2328 /**
   2329 * EnumerateRawDevices - Enumerate a list of audio & video devices that
   2330 * satisfy passed-in constraints. List contains raw id's.
   2331 */
   2332 
   2333 /* static */ RefPtr<MediaManager::MediaDeviceSetRefCnt>
   2334 MediaManager::EnumerateRawDevices(EnumerationParams aParams) {
   2335  MOZ_ASSERT(IsInMediaThread());
   2336  // Only enumerate what's asked for, and only fake cams and mics.
   2337  RefPtr<MediaEngine> fakeBackend, realBackend;
   2338  if (aParams.HasFakeCams() || aParams.HasFakeMics()) {
   2339    fakeBackend = new MediaEngineFake();
   2340  }
   2341  if (aParams.RealDeviceRequested()) {
   2342    MediaManager* manager = MediaManager::GetIfExists();
   2343    MOZ_RELEASE_ASSERT(manager, "Must exist while media thread is alive");
   2344    realBackend = manager->GetBackend();
   2345  }
   2346 
   2347  RefPtr<MediaEngine> videoBackend;
   2348  RefPtr<MediaEngine> audioBackend;
   2349  Maybe<MediaDeviceSet> micsOfVideoBackend;
   2350  Maybe<MediaDeviceSet> speakers;
   2351  RefPtr devices = new MediaDeviceSetRefCnt();
   2352 
   2353  // Enumerate microphones first, then cameras, then speakers, since
   2354  // the enumerateDevices() algorithm expects them listed in that order.
   2355  if (const auto& audio = aParams.mAudio; audio.isSome()) {
   2356    audioBackend = aParams.HasFakeMics() ? fakeBackend : realBackend;
   2357    MediaDeviceSet audios;
   2358    LOG("EnumerateRawDevices: Getting audio sources with %s backend",
   2359        audioBackend == fakeBackend ? "fake" : "real");
   2360    GetMediaDevices(audioBackend, audio->mInputType, audios,
   2361                    audio->mForcedDeviceName.get());
   2362    if (audio->mInputType == MediaSourceEnum::Microphone &&
   2363        audioBackend == videoBackend) {
   2364      micsOfVideoBackend.emplace();
   2365      micsOfVideoBackend->AppendElements(audios);
   2366    }
   2367    devices->AppendElements(std::move(audios));
   2368  }
   2369  if (const auto& video = aParams.mVideo; video.isSome()) {
   2370    videoBackend = aParams.HasFakeCams() ? fakeBackend : realBackend;
   2371    MediaDeviceSet videos;
   2372    LOG("EnumerateRawDevices: Getting video sources with %s backend",
   2373        videoBackend == fakeBackend ? "fake" : "real");
   2374    GetMediaDevices(videoBackend, video->mInputType, videos,
   2375                    video->mForcedDeviceName.get());
   2376    devices->AppendElements(std::move(videos));
   2377  }
   2378  if (aParams.mFlags.contains(EnumerationFlag::EnumerateAudioOutputs)) {
   2379    MediaDeviceSet outputs;
   2380    MOZ_ASSERT(realBackend);
   2381    realBackend->EnumerateDevices(MediaSourceEnum::Other,
   2382                                  MediaSinkEnum::Speaker, &outputs);
   2383    speakers = Some(MediaDeviceSet());
   2384    speakers->AppendElements(outputs);
   2385    devices->AppendElements(std::move(outputs));
   2386  }
   2387  if (aParams.VideoInputType() == MediaSourceEnum::Camera) {
   2388    MediaDeviceSet audios;
   2389    LOG("EnumerateRawDevices: Getting audio sources with %s backend for "
   2390        "groupId correlation",
   2391        videoBackend == fakeBackend ? "fake" : "real");
   2392    // We need to correlate cameras with audio groupIds. We use the backend of
   2393    // the camera to always do correlation on devices in the same scope. If we
   2394    // don't do this, video-only getUserMedia will not apply groupId constraints
   2395    // to the same set of groupIds as gets returned by enumerateDevices.
   2396    if (micsOfVideoBackend.isSome()) {
   2397      // Microphones from the same backend used for the cameras have
   2398      // already been enumerated. Avoid doing it again.
   2399      MOZ_ASSERT(aParams.mVideo->mForcedMicrophoneName ==
   2400                 aParams.mAudio->mForcedDeviceName);
   2401      audios.AppendElements(micsOfVideoBackend.extract());
   2402    } else {
   2403      GetMediaDevices(videoBackend, MediaSourceEnum::Microphone, audios,
   2404                      aParams.mVideo->mForcedMicrophoneName.get());
   2405    }
   2406    if (videoBackend == realBackend) {
   2407      // When using the real backend for video, there could also be
   2408      // speakers to correlate with. There are no fake speakers.
   2409      if (speakers.isSome()) {
   2410        // Speakers have already been enumerated. Avoid doing it again.
   2411        audios.AppendElements(speakers.extract());
   2412      } else {
   2413        realBackend->EnumerateDevices(MediaSourceEnum::Other,
   2414                                      MediaSinkEnum::Speaker, &audios);
   2415      }
   2416    }
   2417    GuessVideoDeviceGroupIDs(*devices, audios);
   2418  }
   2419 
   2420  return devices;
   2421 }
   2422 
   2423 RefPtr<ConstDeviceSetPromise> MediaManager::GetPhysicalDevices() {
   2424  MOZ_ASSERT(NS_IsMainThread());
   2425  if (mPhysicalDevices) {
   2426    return ConstDeviceSetPromise::CreateAndResolve(mPhysicalDevices, __func__);
   2427  }
   2428  if (mPendingDevicesPromises) {
   2429    // Enumeration is already in progress.
   2430    return mPendingDevicesPromises->AppendElement()->Ensure(__func__);
   2431  }
   2432  mPendingDevicesPromises =
   2433      new Refcountable<nsTArray<MozPromiseHolder<ConstDeviceSetPromise>>>;
   2434  MaybeRequestPermissionAndEnumerateRawDevices(
   2435      CreateEnumerationParams(MediaSourceEnum::Camera,
   2436                              MediaSourceEnum::Microphone,
   2437                              EnumerationFlag::EnumerateAudioOutputs))
   2438      ->Then(
   2439          GetCurrentSerialEventTarget(), __func__,
   2440          [self = RefPtr(this), this, promises = mPendingDevicesPromises](
   2441              RefPtr<MediaDeviceSetRefCnt> aDevices) mutable {
   2442            for (auto& promiseHolder : *promises) {
   2443              promiseHolder.Resolve(aDevices, __func__);
   2444            }
   2445            // mPendingDevicesPromises may have changed if devices have changed.
   2446            if (promises == mPendingDevicesPromises) {
   2447              mPendingDevicesPromises = nullptr;
   2448              mPhysicalDevices = std::move(aDevices);
   2449            }
   2450          },
   2451          [](RefPtr<MediaMgrError>&& reason) {
   2452            MOZ_ASSERT_UNREACHABLE(
   2453                "MaybeRequestPermissionAndEnumerateRawDevices does not reject");
   2454          });
   2455 
   2456  return mPendingDevicesPromises->AppendElement()->Ensure(__func__);
   2457 }
   2458 
   2459 MediaManager::MediaManager(already_AddRefed<TaskQueue> aMediaThread)
   2460    : mMediaThread(aMediaThread), mBackend(nullptr) {
   2461  mPrefs.mFreq = 1000;  // 1KHz test tone
   2462  mPrefs.mWidth = 0;    // adaptive default
   2463  mPrefs.mHeight = 0;   // adaptive default
   2464  mPrefs.mResizeModeEnabled = false;
   2465  mPrefs.mResizeMode = VideoResizeModeEnum::None;
   2466  mPrefs.mFPS = MediaEnginePrefs::DEFAULT_VIDEO_FPS;
   2467  mPrefs.mUsePlatformProcessing = false;
   2468  mPrefs.mAecOn = false;
   2469  mPrefs.mUseAecMobile = false;
   2470  mPrefs.mAgcOn = false;
   2471  mPrefs.mHPFOn = false;
   2472  mPrefs.mNoiseOn = false;
   2473  mPrefs.mTransientOn = false;
   2474  mPrefs.mAgc2Forced = false;
   2475  mPrefs.mExpectDrift = -1;  // auto
   2476 #ifdef MOZ_WEBRTC
   2477  mPrefs.mAgc =
   2478      webrtc::AudioProcessing::Config::GainController1::Mode::kAdaptiveDigital;
   2479  mPrefs.mNoise =
   2480      webrtc::AudioProcessing::Config::NoiseSuppression::Level::kModerate;
   2481 #else
   2482  mPrefs.mAgc = 0;
   2483  mPrefs.mNoise = 0;
   2484 #endif
   2485  mPrefs.mChannels = 0;  // max channels default
   2486  nsresult rv;
   2487  nsCOMPtr<nsIPrefService> prefs =
   2488      do_GetService("@mozilla.org/preferences-service;1", &rv);
   2489  if (NS_SUCCEEDED(rv)) {
   2490    nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
   2491    if (branch) {
   2492      GetPrefs(branch, nullptr);
   2493    }
   2494  }
   2495 }
   2496 
   2497 NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIMemoryReporter,
   2498                  nsIObserver)
   2499 
   2500 /* static */
   2501 StaticRefPtr<MediaManager> MediaManager::sSingleton;
   2502 
   2503 #ifdef DEBUG
   2504 /* static */
   2505 bool MediaManager::IsInMediaThread() {
   2506  return sSingleton && sSingleton->mMediaThread->IsOnCurrentThread();
   2507 }
   2508 #endif
   2509 
   2510 template <typename Function>
   2511 static void ForeachObservedPref(const Function& aFunction) {
   2512  aFunction("media.navigator.video.default_width"_ns);
   2513  aFunction("media.navigator.video.default_height"_ns);
   2514  aFunction("media.navigator.video.default_fps"_ns);
   2515  aFunction("media.navigator.audio.fake_frequency"_ns);
   2516  aFunction("media.audio_loopback_dev"_ns);
   2517  aFunction("media.video_loopback_dev"_ns);
   2518  aFunction("media.getusermedia.fake-camera-name"_ns);
   2519 #ifdef MOZ_WEBRTC
   2520  aFunction("media.navigator.video.resize_mode.enabled"_ns);
   2521  aFunction("media.navigator.video.default_resize_mode"_ns);
   2522  aFunction("media.getusermedia.audio.processing.aec.enabled"_ns);
   2523  aFunction("media.getusermedia.audio.processing.aec"_ns);
   2524  aFunction("media.getusermedia.audio.processing.agc.enabled"_ns);
   2525  aFunction("media.getusermedia.audio.processing.agc"_ns);
   2526  aFunction("media.getusermedia.audio.processing.hpf.enabled"_ns);
   2527  aFunction("media.getusermedia.audio.processing.noise.enabled"_ns);
   2528  aFunction("media.getusermedia.audio.processing.noise"_ns);
   2529  aFunction("media.getusermedia.audio.max_channels"_ns);
   2530  aFunction("media.navigator.streams.fake"_ns);
   2531 #endif
   2532 }
   2533 
   2534 // NOTE: never NS_DispatchAndSpinEventLoopUntilComplete to the MediaManager
   2535 // thread from the MainThread, as we NS_DispatchAndSpinEventLoopUntilComplete to
   2536 // MainThread from MediaManager thread.
   2537 
   2538 // Guaranteed never to return nullptr.
   2539 /* static */
   2540 MediaManager* MediaManager::Get() {
   2541  MOZ_ASSERT(NS_IsMainThread());
   2542 
   2543  if (!sSingleton) {
   2544    static int timesCreated = 0;
   2545    timesCreated++;
   2546    MOZ_RELEASE_ASSERT(timesCreated == 1);
   2547 
   2548    constexpr bool kSupportsTailDispatch = false;
   2549    RefPtr<TaskQueue> mediaThread =
   2550 #ifdef MOZ_WEBRTC
   2551        CreateWebrtcTaskQueueWrapper(
   2552            GetMediaThreadPool(MediaThreadType::SUPERVISOR), "MediaManager"_ns,
   2553            kSupportsTailDispatch);
   2554 #else
   2555        TaskQueue::Create(GetMediaThreadPool(MediaThreadType::SUPERVISOR),
   2556                          "MediaManager", kSupportsTailDispatch);
   2557 #endif
   2558    LOG("New Media thread for gum");
   2559 
   2560    sSingleton = new MediaManager(mediaThread.forget());
   2561 
   2562    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   2563    if (obs) {
   2564      obs->AddObserver(sSingleton, "last-pb-context-exited", false);
   2565      obs->AddObserver(sSingleton, "getUserMedia:got-device-permission", false);
   2566      obs->AddObserver(sSingleton, "getUserMedia:privileged:allow", false);
   2567      obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
   2568      obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
   2569      obs->AddObserver(sSingleton, "getUserMedia:response:noOSPermission",
   2570                       false);
   2571      obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
   2572      obs->AddObserver(sSingleton, "getUserMedia:muteVideo", false);
   2573      obs->AddObserver(sSingleton, "getUserMedia:unmuteVideo", false);
   2574      obs->AddObserver(sSingleton, "getUserMedia:muteAudio", false);
   2575      obs->AddObserver(sSingleton, "getUserMedia:unmuteAudio", false);
   2576      obs->AddObserver(sSingleton, "application-background", false);
   2577      obs->AddObserver(sSingleton, "application-foreground", false);
   2578    }
   2579    // else MediaManager won't work properly and will leak (see bug 837874)
   2580    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   2581    if (prefs) {
   2582      ForeachObservedPref([&](const nsLiteralCString& aPrefName) {
   2583        prefs->AddObserver(aPrefName, sSingleton, false);
   2584      });
   2585    }
   2586    RegisterStrongMemoryReporter(sSingleton);
   2587 
   2588    // Prepare async shutdown
   2589 
   2590    class Blocker : public media::ShutdownBlocker {
   2591     public:
   2592      Blocker()
   2593          : media::ShutdownBlocker(
   2594                u"Media shutdown: blocking on media thread"_ns) {}
   2595 
   2596      NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient*) override {
   2597        MOZ_RELEASE_ASSERT(MediaManager::GetIfExists());
   2598        MediaManager::GetIfExists()->Shutdown();
   2599        return NS_OK;
   2600      }
   2601    };
   2602 
   2603    sSingleton->mShutdownBlocker = new Blocker();
   2604    nsresult rv = media::MustGetShutdownBarrier()->AddBlocker(
   2605        sSingleton->mShutdownBlocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__),
   2606        __LINE__, u""_ns);
   2607    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   2608  }
   2609  return sSingleton;
   2610 }
   2611 
   2612 /* static */
   2613 MediaManager* MediaManager::GetIfExists() {
   2614  MOZ_ASSERT(NS_IsMainThread() || IsInMediaThread());
   2615  return sSingleton;
   2616 }
   2617 
   2618 /* static */
   2619 already_AddRefed<MediaManager> MediaManager::GetInstance() {
   2620  // so we can have non-refcounted getters
   2621  RefPtr<MediaManager> service = MediaManager::Get();
   2622  return service.forget();
   2623 }
   2624 
   2625 media::Parent<media::NonE10s>* MediaManager::GetNonE10sParent() {
   2626  if (!mNonE10sParent) {
   2627    mNonE10sParent = new media::Parent<media::NonE10s>();
   2628  }
   2629  return mNonE10sParent;
   2630 }
   2631 
   2632 /* static */
   2633 void MediaManager::Dispatch(already_AddRefed<Runnable> task) {
   2634  MOZ_ASSERT(NS_IsMainThread());
   2635  if (sHasMainThreadShutdown) {
   2636    // Can't safely delete task here since it may have items with specific
   2637    // thread-release requirements.
   2638    // XXXkhuey well then who is supposed to delete it?! We don't signal
   2639    // that we failed ...
   2640    MOZ_CRASH();
   2641    return;
   2642  }
   2643  NS_ASSERTION(Get(), "MediaManager singleton?");
   2644  NS_ASSERTION(Get()->mMediaThread, "No thread yet");
   2645  MOZ_ALWAYS_SUCCEEDS(Get()->mMediaThread->Dispatch(std::move(task)));
   2646 }
   2647 
   2648 template <typename MozPromiseType, typename FunctionType>
   2649 /* static */
   2650 RefPtr<MozPromiseType> MediaManager::Dispatch(StaticString aName,
   2651                                              FunctionType&& aFunction) {
   2652  MozPromiseHolder<MozPromiseType> holder;
   2653  RefPtr<MozPromiseType> promise = holder.Ensure(aName);
   2654  MediaManager::Dispatch(NS_NewRunnableFunction(
   2655      aName, [h = std::move(holder), func = std::forward<FunctionType>(
   2656                                         aFunction)]() mutable { func(h); }));
   2657  return promise;
   2658 }
   2659 
   2660 /* static */
   2661 nsresult MediaManager::NotifyRecordingStatusChange(
   2662    nsPIDOMWindowInner* aWindow) {
   2663  NS_ENSURE_ARG(aWindow);
   2664 
   2665  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   2666  if (!obs) {
   2667    NS_WARNING(
   2668        "Could not get the Observer service for GetUserMedia recording "
   2669        "notification.");
   2670    return NS_ERROR_FAILURE;
   2671  }
   2672 
   2673  auto props = MakeRefPtr<nsHashPropertyBag>();
   2674 
   2675  nsCString pageURL;
   2676  nsCOMPtr<nsIURI> docURI = aWindow->GetDocumentURI();
   2677  NS_ENSURE_TRUE(docURI, NS_ERROR_FAILURE);
   2678 
   2679  nsresult rv = docURI->GetSpec(pageURL);
   2680  NS_ENSURE_SUCCESS(rv, rv);
   2681 
   2682  NS_ConvertUTF8toUTF16 requestURL(pageURL);
   2683 
   2684  props->SetPropertyAsAString(u"requestURL"_ns, requestURL);
   2685  props->SetPropertyAsInterface(u"window"_ns, aWindow);
   2686 
   2687  obs->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
   2688                       "recording-device-events", nullptr);
   2689  LOG("Sent recording-device-events for url '%s'", pageURL.get());
   2690 
   2691  return NS_OK;
   2692 }
   2693 
   2694 void MediaManager::DeviceListChanged() {
   2695  MOZ_ASSERT(NS_IsMainThread());
   2696  if (sHasMainThreadShutdown) {
   2697    return;
   2698  }
   2699  // Invalidate immediately to provide an up-to-date device list for future
   2700  // enumerations on platforms with sane device-list-changed events.
   2701  InvalidateDeviceCache();
   2702 
   2703  // Wait 200 ms, because
   2704  // A) on some Windows machines, if we call EnumerateRawDevices immediately
   2705  //    after receiving devicechange event, we'd get an outdated devices list.
   2706  // B) Waiting helps coalesce multiple calls on us into one, which can happen
   2707  //    if a device with both audio input and output is attached or removed.
   2708  //    We want to react & fire a devicechange event only once in that case.
   2709 
   2710  // The wait is extended if another hardware device-list-changed notification
   2711  // is received to provide the full 200ms for EnumerateRawDevices().
   2712  if (mDeviceChangeTimer) {
   2713    mDeviceChangeTimer->Cancel();
   2714  } else {
   2715    mDeviceChangeTimer = MakeRefPtr<MediaTimer<TimeStamp>>();
   2716  }
   2717  // However, if this would cause a delay of over 1000ms in handling the
   2718  // oldest unhandled event, then respond now and set the timer to run
   2719  // EnumerateRawDevices() again in 200ms.
   2720  auto now = TimeStamp::NowLoRes();
   2721  auto enumerateDelay = TimeDuration::FromMilliseconds(200);
   2722  auto coalescenceLimit = TimeDuration::FromMilliseconds(1000) - enumerateDelay;
   2723  if (!mUnhandledDeviceChangeTime) {
   2724    mUnhandledDeviceChangeTime = now;
   2725  } else if (now - mUnhandledDeviceChangeTime > coalescenceLimit) {
   2726    HandleDeviceListChanged();
   2727    mUnhandledDeviceChangeTime = now;
   2728  }
   2729  RefPtr<MediaManager> self = this;
   2730  mDeviceChangeTimer->WaitFor(enumerateDelay, __func__)
   2731      ->Then(
   2732          GetCurrentSerialEventTarget(), __func__,
   2733          [self, this] {
   2734            // Invalidate again for the sake of platforms with inconsistent
   2735            // timing between device-list-changed notification and enumeration.
   2736            InvalidateDeviceCache();
   2737 
   2738            mUnhandledDeviceChangeTime = TimeStamp();
   2739            HandleDeviceListChanged();
   2740          },
   2741          [] { /* Timer was canceled by us, or we're in shutdown. */ });
   2742 }
   2743 
   2744 void MediaManager::InvalidateDeviceCache() {
   2745  MOZ_ASSERT(NS_IsMainThread());
   2746 
   2747  mPhysicalDevices = nullptr;
   2748  // Disconnect any in-progress enumeration, which may now be out of date,
   2749  // from updating mPhysicalDevices or resolving future device request
   2750  // promises.
   2751  mPendingDevicesPromises = nullptr;
   2752 }
   2753 
   2754 void MediaManager::HandleDeviceListChanged() {
   2755  mDeviceListChangeEvent.Notify();
   2756 
   2757  GetPhysicalDevices()->Then(
   2758      GetCurrentSerialEventTarget(), __func__,
   2759      [self = RefPtr(this), this](RefPtr<const MediaDeviceSetRefCnt> aDevices) {
   2760        if (!MediaManager::GetIfExists()) {
   2761          return;
   2762        }
   2763 
   2764        nsTHashSet<nsString> deviceIDs;
   2765        for (const auto& device : *aDevices) {
   2766          deviceIDs.Insert(device->mRawID);
   2767        }
   2768        // For any real removed cameras or microphones, notify their
   2769        // listeners cleanly that the source has stopped, so JS knows and
   2770        // usage indicators update.
   2771        // First collect the listeners in an array to stop them after
   2772        // iterating the hashtable. The StopRawID() method indirectly
   2773        // modifies the mActiveWindows and would assert-crash if the
   2774        // iterator were active while the table is being enumerated.
   2775        const auto windowListeners = ToArray(mActiveWindows.Values());
   2776        for (const RefPtr<GetUserMediaWindowListener>& l : windowListeners) {
   2777          const auto activeDevices = l->GetDevices();
   2778          for (const RefPtr<LocalMediaDevice>& device : *activeDevices) {
   2779            if (device->IsFake()) {
   2780              continue;
   2781            }
   2782            MediaSourceEnum mediaSource = device->GetMediaSource();
   2783            if (mediaSource != MediaSourceEnum::Microphone &&
   2784                mediaSource != MediaSourceEnum::Camera) {
   2785              continue;
   2786            }
   2787            if (!deviceIDs.Contains(device->RawID())) {
   2788              // Device has been removed
   2789              l->StopRawID(device->RawID());
   2790            }
   2791          }
   2792        }
   2793      },
   2794      [](RefPtr<MediaMgrError>&& reason) {
   2795        MOZ_ASSERT_UNREACHABLE("EnumerateRawDevices does not reject");
   2796      });
   2797 }
   2798 
   2799 size_t MediaManager::AddTaskAndGetCount(uint64_t aWindowID,
   2800                                        const nsAString& aCallID,
   2801                                        RefPtr<GetUserMediaTask> aTask) {
   2802  // Store the task w/callbacks.
   2803  mActiveCallbacks.InsertOrUpdate(aCallID, std::move(aTask));
   2804 
   2805  // Add a WindowID cross-reference so OnNavigation can tear things down
   2806  nsTArray<nsString>* const array = mCallIds.GetOrInsertNew(aWindowID);
   2807  array->AppendElement(aCallID);
   2808 
   2809  return array->Length();
   2810 }
   2811 
   2812 RefPtr<GetUserMediaTask> MediaManager::TakeGetUserMediaTask(
   2813    const nsAString& aCallID) {
   2814  RefPtr<GetUserMediaTask> task;
   2815  mActiveCallbacks.Remove(aCallID, getter_AddRefs(task));
   2816  if (!task) {
   2817    return nullptr;
   2818  }
   2819  nsTArray<nsString>* array;
   2820  mCallIds.Get(task->GetWindowID(), &array);
   2821  MOZ_ASSERT(array);
   2822  array->RemoveElement(aCallID);
   2823  return task;
   2824 }
   2825 
   2826 void MediaManager::NotifyAllowed(const nsString& aCallID,
   2827                                 const LocalMediaDeviceSet& aDevices) {
   2828  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   2829  nsCOMPtr<nsIMutableArray> devicesCopy = nsArray::Create();
   2830  for (const auto& device : aDevices) {
   2831    nsresult rv = devicesCopy->AppendElement(device);
   2832    if (NS_WARN_IF(NS_FAILED(rv))) {
   2833      obs->NotifyObservers(nullptr, "getUserMedia:response:deny",
   2834                           aCallID.get());
   2835      return;
   2836    }
   2837  }
   2838  obs->NotifyObservers(devicesCopy, "getUserMedia:privileged:allow",
   2839                       aCallID.get());
   2840 }
   2841 
   2842 nsresult MediaManager::GenerateUUID(nsAString& aResult) {
   2843  nsresult rv;
   2844  nsCOMPtr<nsIUUIDGenerator> uuidgen =
   2845      do_GetService("@mozilla.org/uuid-generator;1", &rv);
   2846  NS_ENSURE_SUCCESS(rv, rv);
   2847 
   2848  // Generate a call ID.
   2849  nsID id;
   2850  rv = uuidgen->GenerateUUIDInPlace(&id);
   2851  NS_ENSURE_SUCCESS(rv, rv);
   2852 
   2853  char buffer[NSID_LENGTH];
   2854  id.ToProvidedString(buffer);
   2855  aResult.Assign(NS_ConvertUTF8toUTF16(buffer));
   2856  return NS_OK;
   2857 }
   2858 
   2859 enum class GetUserMediaSecurityState {
   2860  Other = 0,
   2861  HTTPS = 1,
   2862  File = 2,
   2863  App = 3,
   2864  Localhost = 4,
   2865  Loop = 5,
   2866  Privileged = 6
   2867 };
   2868 
   2869 /**
   2870 * This function is used in getUserMedia when privacy.resistFingerprinting is
   2871 * true. Only mediaSource of audio/video constraint will be kept. On mobile
   2872 * facing mode is also kept.
   2873 */
   2874 static void ReduceConstraint(
   2875    OwningBooleanOrMediaTrackConstraints& aConstraint) {
   2876  // Not requesting stream.
   2877  if (!MediaManager::IsOn(aConstraint)) {
   2878    return;
   2879  }
   2880 
   2881  // It looks like {audio: true}, do nothing.
   2882  if (!aConstraint.IsMediaTrackConstraints()) {
   2883    return;
   2884  }
   2885 
   2886  // Keep mediaSource.
   2887  Maybe<nsString> mediaSource;
   2888  if (aConstraint.GetAsMediaTrackConstraints().mMediaSource.WasPassed()) {
   2889    mediaSource =
   2890        Some(aConstraint.GetAsMediaTrackConstraints().mMediaSource.Value());
   2891  }
   2892 
   2893  Maybe<OwningStringOrStringSequenceOrConstrainDOMStringParameters> facingMode;
   2894  if (aConstraint.GetAsMediaTrackConstraints().mFacingMode.WasPassed()) {
   2895    facingMode =
   2896        Some(aConstraint.GetAsMediaTrackConstraints().mFacingMode.Value());
   2897  }
   2898 
   2899  aConstraint.Uninit();
   2900  if (mediaSource) {
   2901    aConstraint.SetAsMediaTrackConstraints().mMediaSource.Construct(
   2902        *mediaSource);
   2903  } else {
   2904    (void)aConstraint.SetAsMediaTrackConstraints();
   2905  }
   2906 
   2907 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_UIKIT)
   2908  if (facingMode) {
   2909    aConstraint.SetAsMediaTrackConstraints().mFacingMode.Construct(*facingMode);
   2910  } else {
   2911    (void)aConstraint.SetAsMediaTrackConstraints();
   2912  }
   2913 #endif
   2914 }
   2915 
   2916 /**
   2917 * The entry point for this file. A call from MediaDevices::GetUserMedia
   2918 * will end up here. MediaManager is a singleton that is responsible
   2919 * for handling all incoming getUserMedia calls from every window.
   2920 */
   2921 RefPtr<MediaManager::StreamPromise> MediaManager::GetUserMedia(
   2922    nsPIDOMWindowInner* aWindow,
   2923    const MediaStreamConstraints& aConstraintsPassedIn,
   2924    CallerType aCallerType) {
   2925  MOZ_ASSERT(NS_IsMainThread());
   2926  MOZ_ASSERT(aWindow);
   2927  uint64_t windowID = aWindow->WindowID();
   2928 
   2929  MediaStreamConstraints c(aConstraintsPassedIn);  // use a modifiable copy
   2930 
   2931  if (sHasMainThreadShutdown) {
   2932    return StreamPromise::CreateAndReject(
   2933        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError,
   2934                                  "In shutdown"),
   2935        __func__);
   2936  }
   2937 
   2938  // Determine permissions early (while we still have a stack).
   2939 
   2940  nsIURI* docURI = aWindow->GetDocumentURI();
   2941  if (!docURI) {
   2942    return StreamPromise::CreateAndReject(
   2943        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError), __func__);
   2944  }
   2945  bool isChrome = (aCallerType == CallerType::System);
   2946  bool privileged =
   2947      isChrome ||
   2948      Preferences::GetBool("media.navigator.permission.disabled", false);
   2949  bool isSecure = aWindow->IsSecureContext();
   2950  bool isHandlingUserInput = UserActivation::IsHandlingUserInput();
   2951  nsCString host;
   2952  nsresult rv = docURI->GetHost(host);
   2953 
   2954  nsCOMPtr<nsIPrincipal> principal =
   2955      nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
   2956  if (NS_WARN_IF(!principal)) {
   2957    return StreamPromise::CreateAndReject(
   2958        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::SecurityError),
   2959        __func__);
   2960  }
   2961 
   2962  Document* doc = aWindow->GetExtantDoc();
   2963  if (NS_WARN_IF(!doc)) {
   2964    return StreamPromise::CreateAndReject(
   2965        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::SecurityError),
   2966        __func__);
   2967  }
   2968 
   2969  // Disallow access to null principal pages and http pages (unless pref)
   2970  if (principal->GetIsNullPrincipal() ||
   2971      !(isSecure || StaticPrefs::media_getusermedia_insecure_enabled())) {
   2972    return StreamPromise::CreateAndReject(
   2973        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
   2974        __func__);
   2975  }
   2976 
   2977  // This principal needs to be sent to different threads and so via IPC.
   2978  // For this reason it's better to convert it to PrincipalInfo right now.
   2979  ipc::PrincipalInfo principalInfo;
   2980  rv = PrincipalToPrincipalInfo(principal, &principalInfo);
   2981  if (NS_WARN_IF(NS_FAILED(rv))) {
   2982    return StreamPromise::CreateAndReject(
   2983        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::SecurityError),
   2984        __func__);
   2985  }
   2986 
   2987  const bool resistFingerprinting =
   2988      !isChrome && doc->ShouldResistFingerprinting(RFPTarget::MediaDevices);
   2989  if (resistFingerprinting) {
   2990    ReduceConstraint(c.mVideo);
   2991    ReduceConstraint(c.mAudio);
   2992  }
   2993 
   2994  if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
   2995    c.mVideo.SetAsBoolean() = false;
   2996  }
   2997 
   2998  MediaSourceEnum videoType = MediaSourceEnum::Other;  // none
   2999  MediaSourceEnum audioType = MediaSourceEnum::Other;  // none
   3000 
   3001  if (c.mVideo.IsMediaTrackConstraints()) {
   3002    auto& vc = c.mVideo.GetAsMediaTrackConstraints();
   3003    if (!vc.mMediaSource.WasPassed()) {
   3004      vc.mMediaSource.Construct().AssignASCII(
   3005          dom::GetEnumString(MediaSourceEnum::Camera));
   3006    }
   3007    videoType = dom::StringToEnum<MediaSourceEnum>(vc.mMediaSource.Value())
   3008                    .valueOr(MediaSourceEnum::Other);
   3009    glean::webrtc::get_user_media_type.AccumulateSingleSample(
   3010        (uint32_t)videoType);
   3011    switch (videoType) {
   3012      case MediaSourceEnum::Camera:
   3013        break;
   3014 
   3015      case MediaSourceEnum::Browser:
   3016        // If no window id is passed in then default to the caller's window.
   3017        // Functional defaults are helpful in tests, but also a natural outcome
   3018        // of the constraints API's limited semantics for requiring input.
   3019        if (!vc.mBrowserWindow.WasPassed()) {
   3020          nsPIDOMWindowOuter* outer = aWindow->GetOuterWindow();
   3021          vc.mBrowserWindow.Construct(outer->WindowID());
   3022        }
   3023        [[fallthrough]];
   3024      case MediaSourceEnum::Screen:
   3025      case MediaSourceEnum::Window:
   3026        // Deny screensharing request if support is disabled, or
   3027        // the requesting document is not from a host on the whitelist.
   3028        if (!Preferences::GetBool(
   3029                ((videoType == MediaSourceEnum::Browser)
   3030                     ? "media.getusermedia.browser.enabled"
   3031                     : "media.getusermedia.screensharing.enabled"),
   3032                false) ||
   3033            (!privileged && !aWindow->IsSecureContext())) {
   3034          return StreamPromise::CreateAndReject(
   3035              MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
   3036              __func__);
   3037        }
   3038        break;
   3039 
   3040      case MediaSourceEnum::Microphone:
   3041      case MediaSourceEnum::Other:
   3042      default: {
   3043        return StreamPromise::CreateAndReject(
   3044            MakeRefPtr<MediaMgrError>(MediaMgrError::Name::OverconstrainedError,
   3045                                      "", u"mediaSource"_ns),
   3046            __func__);
   3047      }
   3048    }
   3049 
   3050    if (!privileged) {
   3051      // Only allow privileged content to explicitly pick full-screen,
   3052      // application or tabsharing, since these modes are still available for
   3053      // testing. All others get "Window" (*) sharing.
   3054      //
   3055      // *) We overload "Window" with the new default getDisplayMedia spec-
   3056      // mandated behavior of not influencing user-choice, which we currently
   3057      // implement as a list containing BOTH windows AND screen(s).
   3058      //
   3059      // Notes on why we chose "Window" as the one to overload. Two reasons:
   3060      //
   3061      // 1. It's the closest logically & behaviorally (multi-choice, no default)
   3062      // 2. Screen is still useful in tests (implicit default is entire screen)
   3063      //
   3064      // For UX reasons we don't want "Entire Screen" to be the first/default
   3065      // choice (in our code first=default). It's a "scary" source that comes
   3066      // with complicated warnings on-top that would be confusing as the first
   3067      // thing people see, and also deserves to be listed as last resort for
   3068      // privacy reasons.
   3069 
   3070      if (videoType == MediaSourceEnum::Screen ||
   3071          videoType == MediaSourceEnum::Browser) {
   3072        videoType = MediaSourceEnum::Window;
   3073        vc.mMediaSource.Value().AssignASCII(dom::GetEnumString(videoType));
   3074      }
   3075      // only allow privileged content to set the window id
   3076      if (vc.mBrowserWindow.WasPassed()) {
   3077        vc.mBrowserWindow.Value() = -1;
   3078      }
   3079      if (vc.mAdvanced.WasPassed()) {
   3080        for (MediaTrackConstraintSet& cs : vc.mAdvanced.Value()) {
   3081          if (cs.mBrowserWindow.WasPassed()) {
   3082            cs.mBrowserWindow.Value() = -1;
   3083          }
   3084        }
   3085      }
   3086    }
   3087  } else if (IsOn(c.mVideo)) {
   3088    videoType = MediaSourceEnum::Camera;
   3089    glean::webrtc::get_user_media_type.AccumulateSingleSample(
   3090        (uint32_t)videoType);
   3091  }
   3092 
   3093  if (c.mAudio.IsMediaTrackConstraints()) {
   3094    auto& ac = c.mAudio.GetAsMediaTrackConstraints();
   3095    if (!ac.mMediaSource.WasPassed()) {
   3096      ac.mMediaSource.Construct(NS_ConvertASCIItoUTF16(
   3097          dom::GetEnumString(MediaSourceEnum::Microphone)));
   3098    }
   3099    audioType = dom::StringToEnum<MediaSourceEnum>(ac.mMediaSource.Value())
   3100                    .valueOr(MediaSourceEnum::Other);
   3101    glean::webrtc::get_user_media_type.AccumulateSingleSample(
   3102        (uint32_t)audioType);
   3103 
   3104    switch (audioType) {
   3105      case MediaSourceEnum::Microphone:
   3106        break;
   3107 
   3108      case MediaSourceEnum::AudioCapture:
   3109        // Only enable AudioCapture if the pref is enabled. If it's not, we can
   3110        // deny right away.
   3111        if (!Preferences::GetBool("media.getusermedia.audio.capture.enabled")) {
   3112          return StreamPromise::CreateAndReject(
   3113              MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
   3114              __func__);
   3115        }
   3116        break;
   3117 
   3118      case MediaSourceEnum::Other:
   3119      default: {
   3120        return StreamPromise::CreateAndReject(
   3121            MakeRefPtr<MediaMgrError>(MediaMgrError::Name::OverconstrainedError,
   3122                                      "", u"mediaSource"_ns),
   3123            __func__);
   3124      }
   3125    }
   3126  } else if (IsOn(c.mAudio)) {
   3127    audioType = MediaSourceEnum::Microphone;
   3128    glean::webrtc::get_user_media_type.AccumulateSingleSample(
   3129        (uint32_t)audioType);
   3130  }
   3131 
   3132  // Create a window listener if it doesn't already exist.
   3133  RefPtr<GetUserMediaWindowListener> windowListener =
   3134      GetOrMakeWindowListener(aWindow);
   3135  MOZ_ASSERT(windowListener);
   3136  // Create an inactive DeviceListener to act as a placeholder, so the
   3137  // window listener doesn't clean itself up until we're done.
   3138  auto placeholderListener = MakeRefPtr<DeviceListener>();
   3139  windowListener->Register(placeholderListener);
   3140 
   3141  {  // Check Permissions Policy.  Reject if a requested feature is disabled.
   3142    bool disabled = !IsOn(c.mAudio) && !IsOn(c.mVideo);
   3143    if (IsOn(c.mAudio)) {
   3144      if (audioType == MediaSourceEnum::Microphone) {
   3145        if (Preferences::GetBool("media.getusermedia.microphone.deny", false) ||
   3146            !FeaturePolicyUtils::IsFeatureAllowed(doc, u"microphone"_ns)) {
   3147          disabled = true;
   3148        }
   3149      } else if (!FeaturePolicyUtils::IsFeatureAllowed(doc,
   3150                                                       u"display-capture"_ns)) {
   3151        disabled = true;
   3152      }
   3153    }
   3154    if (IsOn(c.mVideo)) {
   3155      if (videoType == MediaSourceEnum::Camera) {
   3156        if (Preferences::GetBool("media.getusermedia.camera.deny", false) ||
   3157            !FeaturePolicyUtils::IsFeatureAllowed(doc, u"camera"_ns)) {
   3158          disabled = true;
   3159        }
   3160      } else if (!FeaturePolicyUtils::IsFeatureAllowed(doc,
   3161                                                       u"display-capture"_ns)) {
   3162        disabled = true;
   3163      }
   3164    }
   3165 
   3166    if (disabled) {
   3167      placeholderListener->Stop();
   3168      return StreamPromise::CreateAndReject(
   3169          MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
   3170          __func__);
   3171    }
   3172  }
   3173 
   3174  // Get list of all devices, with origin-specific device ids.
   3175 
   3176  MediaEnginePrefs prefs = mPrefs;
   3177 
   3178  nsString callID;
   3179  rv = GenerateUUID(callID);
   3180  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   3181 
   3182  bool hasVideo = videoType != MediaSourceEnum::Other;
   3183  bool hasAudio = audioType != MediaSourceEnum::Other;
   3184 
   3185  // Handle fake requests from content. For gUM we don't consider resist
   3186  // fingerprinting as users should be prompted anyway.
   3187  bool forceFakes = c.mFake.WasPassed() && c.mFake.Value();
   3188  // fake:true is effective only for microphone and camera devices, so
   3189  // permission must be requested for screen capture even if fake:true is set.
   3190  bool hasOnlyForcedFakes =
   3191      forceFakes && (!hasVideo || videoType == MediaSourceEnum::Camera) &&
   3192      (!hasAudio || audioType == MediaSourceEnum::Microphone);
   3193  bool askPermission =
   3194      (!privileged ||
   3195       Preferences::GetBool("media.navigator.permission.force")) &&
   3196      (!hasOnlyForcedFakes ||
   3197       Preferences::GetBool("media.navigator.permission.fake"));
   3198 
   3199  LOG("%s: Preparing to enumerate devices. windowId=%" PRIu64
   3200      ", videoType=%" PRIu8 ", audioType=%" PRIu8
   3201      ", forceFakes=%s, askPermission=%s",
   3202      __func__, windowID, static_cast<uint8_t>(videoType),
   3203      static_cast<uint8_t>(audioType), forceFakes ? "true" : "false",
   3204      askPermission ? "true" : "false");
   3205 
   3206  EnumerationFlags flags = EnumerationFlag::AllowPermissionRequest;
   3207  if (forceFakes) {
   3208    flags += EnumerationFlag::ForceFakes;
   3209  }
   3210  RefPtr<MediaManager> self = this;
   3211  return EnumerateDevicesImpl(
   3212             aWindow, CreateEnumerationParams(videoType, audioType, flags))
   3213      ->Then(
   3214          GetCurrentSerialEventTarget(), __func__,
   3215          [self, windowID, c, windowListener,
   3216           aCallerType](RefPtr<LocalMediaDeviceSetRefCnt> aDevices) {
   3217            LOG("GetUserMedia: post enumeration promise success callback "
   3218                "starting");
   3219            // Ensure that our windowID is still good.
   3220            RefPtr<nsPIDOMWindowInner> window =
   3221                nsGlobalWindowInner::GetInnerWindowWithId(windowID);
   3222            if (!window || !self->IsWindowListenerStillActive(windowListener)) {
   3223              LOG("GetUserMedia: bad window (%" PRIu64
   3224                  ") in post enumeration success callback!",
   3225                  windowID);
   3226              return LocalDeviceSetPromise::CreateAndReject(
   3227                  MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
   3228                  __func__);
   3229            }
   3230            // Apply any constraints. This modifies the passed-in list.
   3231            return self->SelectSettings(c, aCallerType, std::move(aDevices));
   3232          },
   3233          [](RefPtr<MediaMgrError>&& aError) {
   3234            LOG("GetUserMedia: post enumeration EnumerateDevicesImpl "
   3235                "failure callback called!");
   3236            return LocalDeviceSetPromise::CreateAndReject(std::move(aError),
   3237                                                          __func__);
   3238          })
   3239      ->Then(
   3240          GetCurrentSerialEventTarget(), __func__,
   3241          [self, windowID, c, windowListener, placeholderListener, hasAudio,
   3242           hasVideo, askPermission, prefs, isSecure, isHandlingUserInput,
   3243           callID, principalInfo, aCallerType, resistFingerprinting, audioType,
   3244           forceFakes](RefPtr<LocalMediaDeviceSetRefCnt> aDevices) mutable {
   3245            LOG("GetUserMedia: starting post enumeration promise2 success "
   3246                "callback!");
   3247 
   3248            // Ensure that the window is still good.
   3249            RefPtr<nsPIDOMWindowInner> window =
   3250                nsGlobalWindowInner::GetInnerWindowWithId(windowID);
   3251            if (!window || !self->IsWindowListenerStillActive(windowListener)) {
   3252              LOG("GetUserMedia: bad window (%" PRIu64
   3253                  ") in post enumeration success callback 2!",
   3254                  windowID);
   3255              placeholderListener->Stop();
   3256              return StreamPromise::CreateAndReject(
   3257                  MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
   3258                  __func__);
   3259            }
   3260            if (!aDevices->Length()) {
   3261              LOG("GetUserMedia: no devices found in post enumeration promise2 "
   3262                  "success callback! Calling error handler!");
   3263              placeholderListener->Stop();
   3264              // When privacy.resistFingerprinting = true, no
   3265              // available device implies content script is requesting
   3266              // a fake device, so report NotAllowedError.
   3267              auto error = resistFingerprinting
   3268                               ? MediaMgrError::Name::NotAllowedError
   3269                               : MediaMgrError::Name::NotFoundError;
   3270              return StreamPromise::CreateAndReject(
   3271                  MakeRefPtr<MediaMgrError>(error), __func__);
   3272            }
   3273 
   3274            // Time to start devices. Create the necessary device listeners and
   3275            // remove the placeholder.
   3276            RefPtr<DeviceListener> audioListener;
   3277            RefPtr<DeviceListener> videoListener;
   3278            if (hasAudio) {
   3279              audioListener = MakeRefPtr<DeviceListener>();
   3280              windowListener->Register(audioListener);
   3281            }
   3282            if (hasVideo) {
   3283              videoListener = MakeRefPtr<DeviceListener>();
   3284              windowListener->Register(videoListener);
   3285            }
   3286            placeholderListener->Stop();
   3287 
   3288            bool focusSource = mozilla::Preferences::GetBool(
   3289                "media.getusermedia.window.focus_source.enabled", true);
   3290 
   3291            // Incremental hack to compile. To be replaced by deeper
   3292            // refactoring. MediaManager allows
   3293            // "neither-resolve-nor-reject" semantics, so we cannot
   3294            // use MozPromiseHolder here.
   3295            MozPromiseHolder<StreamPromise> holder;
   3296            RefPtr<StreamPromise> p = holder.Ensure(__func__);
   3297 
   3298            // Pass callbacks and listeners along to GetUserMediaStreamTask.
   3299            auto task = MakeRefPtr<GetUserMediaStreamTask>(
   3300                c, std::move(holder), windowID, std::move(windowListener),
   3301                std::move(audioListener), std::move(videoListener), prefs,
   3302                principalInfo, aCallerType, focusSource);
   3303 
   3304            // It is time to ask for user permission, prime voice processing
   3305            // now. Use a local lambda to enable a guard pattern.
   3306            [&] {
   3307              if (forceFakes) {
   3308                return;
   3309              }
   3310 
   3311              if (audioType != MediaSourceEnum::Microphone) {
   3312                return;
   3313              }
   3314 
   3315              if (!StaticPrefs::
   3316                      media_getusermedia_microphone_voice_stream_priming_enabled() ||
   3317                  !StaticPrefs::
   3318                      media_getusermedia_microphone_prefer_voice_stream_with_processing_enabled()) {
   3319                return;
   3320              }
   3321 
   3322              if (const auto fc = FlattenedConstraints(
   3323                      NormalizedConstraints(GetInvariant(c.mAudio)));
   3324                  !fc.mEchoCancellation.Get(prefs.mAecOn) &&
   3325                  !fc.mAutoGainControl.Get(prefs.mAgcOn && prefs.mAecOn) &&
   3326                  !fc.mNoiseSuppression.Get(prefs.mNoiseOn && prefs.mAecOn)) {
   3327                return;
   3328              }
   3329 
   3330              if (GetPersistentPermissions(windowID)
   3331                      .map([](auto&& aState) {
   3332                        return aState.mMicrophonePermission ==
   3333                               PersistentPermissionState::Deny;
   3334                      })
   3335                      .unwrapOr(true)) {
   3336                return;
   3337              }
   3338 
   3339              task->PrimeVoiceProcessing();
   3340            }();
   3341 
   3342            size_t taskCount =
   3343                self->AddTaskAndGetCount(windowID, callID, std::move(task));
   3344 
   3345            if (!askPermission) {
   3346              self->NotifyAllowed(callID, *aDevices);
   3347            } else {
   3348              auto req = MakeRefPtr<GetUserMediaRequest>(
   3349                  window, callID, std::move(aDevices), c, isSecure,
   3350                  isHandlingUserInput);
   3351              if (!Preferences::GetBool("media.navigator.permission.force") &&
   3352                  taskCount > 1) {
   3353                // there is at least 1 pending gUM request
   3354                // For the scarySources test case, always send the
   3355                // request
   3356                self->mPendingGUMRequest.AppendElement(req.forget());
   3357              } else {
   3358                nsCOMPtr<nsIObserverService> obs =
   3359                    services::GetObserverService();
   3360                obs->NotifyObservers(req, "getUserMedia:request", nullptr);
   3361              }
   3362            }
   3363 #ifdef MOZ_WEBRTC
   3364            self->mLogHandle = EnsureWebrtcLogging();
   3365 #endif
   3366            return p;
   3367          },
   3368          [placeholderListener](RefPtr<MediaMgrError>&& aError) {
   3369            LOG("GetUserMedia: post enumeration SelectSettings failure "
   3370                "callback called!");
   3371            placeholderListener->Stop();
   3372            return StreamPromise::CreateAndReject(std::move(aError), __func__);
   3373          });
   3374 };
   3375 
   3376 RefPtr<LocalDeviceSetPromise> MediaManager::AnonymizeDevices(
   3377    nsPIDOMWindowInner* aWindow, RefPtr<const MediaDeviceSetRefCnt> aDevices) {
   3378  // Get an origin-key (for either regular or private browsing).
   3379  MOZ_ASSERT(NS_IsMainThread());
   3380  uint64_t windowId = aWindow->WindowID();
   3381  nsCOMPtr<nsIPrincipal> principal =
   3382      nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
   3383  MOZ_ASSERT(principal);
   3384  ipc::PrincipalInfo principalInfo;
   3385  nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
   3386  if (NS_WARN_IF(NS_FAILED(rv))) {
   3387    return LocalDeviceSetPromise::CreateAndReject(
   3388        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
   3389        __func__);
   3390  }
   3391  bool resistFingerprinting =
   3392      aWindow->AsGlobal()->ShouldResistFingerprinting(RFPTarget::MediaDevices);
   3393  bool persist =
   3394      IsActivelyCapturingOrHasAPermission(windowId) && !resistFingerprinting;
   3395  return media::GetPrincipalKey(principalInfo, persist)
   3396      ->Then(
   3397          GetMainThreadSerialEventTarget(), __func__,
   3398          [rawDevices = std::move(aDevices), windowId,
   3399           resistFingerprinting](const nsCString& aOriginKey) {
   3400            MOZ_ASSERT(!aOriginKey.IsEmpty());
   3401            RefPtr anonymized = new LocalMediaDeviceSetRefCnt();
   3402            for (const RefPtr<MediaDevice>& device : *rawDevices) {
   3403              nsString name = device->mRawName;
   3404              if (name.Find(u"AirPods"_ns) != -1) {
   3405                name = u"AirPods"_ns;
   3406              }
   3407 
   3408              nsString id = device->mRawID;
   3409              if (resistFingerprinting) {
   3410                nsRFPService::GetMediaDeviceName(name, device->mKind);
   3411                id = name;
   3412                id.AppendInt(windowId);
   3413              }
   3414              // An empty id represents a virtual default device, for which
   3415              // the exposed deviceId is the empty string.
   3416              if (!id.IsEmpty()) {
   3417                nsContentUtils::AnonymizeId(id, aOriginKey);
   3418              }
   3419 
   3420              nsString groupId = device->mRawGroupID;
   3421              if (resistFingerprinting) {
   3422                nsRFPService::GetMediaDeviceGroup(groupId, device->mKind);
   3423              }
   3424              // Use window id to salt group id in order to make it session
   3425              // based as required by the spec. This does not provide unique
   3426              // group ids through out a browser restart. However, this is not
   3427              // against the spec.  Furthermore, since device ids are the same
   3428              // after a browser restart the fingerprint is not bigger.
   3429              groupId.AppendInt(windowId);
   3430              nsContentUtils::AnonymizeId(groupId, aOriginKey);
   3431              anonymized->EmplaceBack(
   3432                  new LocalMediaDevice(device, id, groupId, name));
   3433            }
   3434            return LocalDeviceSetPromise::CreateAndResolve(anonymized,
   3435                                                           __func__);
   3436          },
   3437          [](nsresult rs) {
   3438            NS_WARNING("AnonymizeDevices failed to get Principal Key");
   3439            return LocalDeviceSetPromise::CreateAndReject(
   3440                MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
   3441                __func__);
   3442          });
   3443 }
   3444 
   3445 RefPtr<LocalDeviceSetPromise> MediaManager::EnumerateDevicesImpl(
   3446    nsPIDOMWindowInner* aWindow, EnumerationParams aParams) {
   3447  MOZ_ASSERT(NS_IsMainThread());
   3448 
   3449  uint64_t windowId = aWindow->WindowID();
   3450  LOG("%s: windowId=%" PRIu64 ", aVideoInputType=%" PRIu8
   3451      ", aAudioInputType=%" PRIu8,
   3452      __func__, windowId, static_cast<uint8_t>(aParams.VideoInputType()),
   3453      static_cast<uint8_t>(aParams.AudioInputType()));
   3454 
   3455  // To get a device list anonymized for a particular origin, we must:
   3456  // 1. Get the raw devices list
   3457  // 2. Anonymize the raw list with an origin-key.
   3458 
   3459  // Add the window id here to check for that and abort silently if no longer
   3460  // exists.
   3461  RefPtr<GetUserMediaWindowListener> windowListener =
   3462      GetOrMakeWindowListener(aWindow);
   3463  MOZ_ASSERT(windowListener);
   3464  // Create an inactive DeviceListener to act as a placeholder, so the
   3465  // window listener doesn't clean itself up until we're done.
   3466  auto placeholderListener = MakeRefPtr<DeviceListener>();
   3467  windowListener->Register(placeholderListener);
   3468 
   3469  return MaybeRequestPermissionAndEnumerateRawDevices(std::move(aParams))
   3470      ->Then(
   3471          GetMainThreadSerialEventTarget(), __func__,
   3472          [self = RefPtr(this), this, window = nsCOMPtr(aWindow),
   3473           placeholderListener](RefPtr<MediaDeviceSetRefCnt> aDevices) mutable {
   3474            // Only run if window is still on our active list.
   3475            MediaManager* mgr = MediaManager::GetIfExists();
   3476            if (!mgr || placeholderListener->Stopped()) {
   3477              // The listener has already been removed if the window is no
   3478              // longer active.
   3479              return LocalDeviceSetPromise::CreateAndReject(
   3480                  MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
   3481                  __func__);
   3482            }
   3483            MOZ_ASSERT(mgr->IsWindowStillActive(window->WindowID()));
   3484            placeholderListener->Stop();
   3485            return AnonymizeDevices(window, aDevices);
   3486          },
   3487          [placeholderListener](RefPtr<MediaMgrError>&& aError) {
   3488            // EnumerateDevicesImpl may fail if a new doc has been set, in which
   3489            // case the OnNavigation() method should have removed all previous
   3490            // active listeners, or if a platform device access request was not
   3491            // granted.
   3492            placeholderListener->Stop();
   3493            return LocalDeviceSetPromise::CreateAndReject(std::move(aError),
   3494                                                          __func__);
   3495          });
   3496 }
   3497 
   3498 RefPtr<LocalDevicePromise> MediaManager::SelectAudioOutput(
   3499    nsPIDOMWindowInner* aWindow, const dom::AudioOutputOptions& aOptions,
   3500    CallerType aCallerType) {
   3501  bool isHandlingUserInput = UserActivation::IsHandlingUserInput();
   3502  nsCOMPtr<nsIPrincipal> principal =
   3503      nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
   3504  if (!FeaturePolicyUtils::IsFeatureAllowed(aWindow->GetExtantDoc(),
   3505                                            u"speaker-selection"_ns)) {
   3506    return LocalDevicePromise::CreateAndReject(
   3507        MakeRefPtr<MediaMgrError>(
   3508            MediaMgrError::Name::NotAllowedError,
   3509            "Document's Permissions Policy does not allow selectAudioOutput()"),
   3510        __func__);
   3511  }
   3512  if (NS_WARN_IF(!principal)) {
   3513    return LocalDevicePromise::CreateAndReject(
   3514        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::SecurityError),
   3515        __func__);
   3516  }
   3517  // Disallow access to null principal.
   3518  if (principal->GetIsNullPrincipal()) {
   3519    return LocalDevicePromise::CreateAndReject(
   3520        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::NotAllowedError),
   3521        __func__);
   3522  }
   3523  ipc::PrincipalInfo principalInfo;
   3524  nsresult rv = PrincipalToPrincipalInfo(principal, &principalInfo);
   3525  if (NS_WARN_IF(NS_FAILED(rv))) {
   3526    return LocalDevicePromise::CreateAndReject(
   3527        MakeRefPtr<MediaMgrError>(MediaMgrError::Name::SecurityError),
   3528        __func__);
   3529  }
   3530  uint64_t windowID = aWindow->WindowID();
   3531  const bool resistFingerprinting =
   3532      aWindow->AsGlobal()->ShouldResistFingerprinting(aCallerType,
   3533                                                      RFPTarget::MediaDevices);
   3534  return EnumerateDevicesImpl(
   3535             aWindow, CreateEnumerationParams(
   3536                          MediaSourceEnum::Other, MediaSourceEnum::Other,
   3537                          {EnumerationFlag::EnumerateAudioOutputs,
   3538                           EnumerationFlag::AllowPermissionRequest}))
   3539      ->Then(
   3540          GetCurrentSerialEventTarget(), __func__,
   3541          [self = RefPtr<MediaManager>(this), windowID, aOptions, aCallerType,
   3542           resistFingerprinting, isHandlingUserInput,
   3543           principalInfo](RefPtr<LocalMediaDeviceSetRefCnt> aDevices) mutable {
   3544            // Ensure that the window is still good.
   3545            RefPtr<nsPIDOMWindowInner> window =
   3546                nsGlobalWindowInner::GetInnerWindowWithId(windowID);
   3547            if (!window) {
   3548              LOG("SelectAudioOutput: bad window (%" PRIu64
   3549                  ") in post enumeration success callback!",
   3550                  windowID);
   3551              return LocalDevicePromise::CreateAndReject(
   3552                  MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError),
   3553                  __func__);
   3554            }
   3555            if (aDevices->IsEmpty()) {
   3556              LOG("SelectAudioOutput: no devices found");
   3557              auto error = resistFingerprinting
   3558                               ? MediaMgrError::Name::NotAllowedError
   3559                               : MediaMgrError::Name::NotFoundError;
   3560              return LocalDevicePromise::CreateAndReject(
   3561                  MakeRefPtr<MediaMgrError>(error), __func__);
   3562            }
   3563            MozPromiseHolder<LocalDevicePromise> holder;
   3564            RefPtr<LocalDevicePromise> p = holder.Ensure(__func__);
   3565            auto task = MakeRefPtr<SelectAudioOutputTask>(
   3566                std::move(holder), windowID, aCallerType, principalInfo);
   3567            nsString callID;
   3568            nsresult rv = GenerateUUID(callID);
   3569            MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   3570            size_t taskCount =
   3571                self->AddTaskAndGetCount(windowID, callID, std::move(task));
   3572            bool askPermission =
   3573                !Preferences::GetBool("media.navigator.permission.disabled") ||
   3574                Preferences::GetBool("media.navigator.permission.force");
   3575            if (!askPermission) {
   3576              self->NotifyAllowed(callID, *aDevices);
   3577            } else {
   3578              MOZ_ASSERT(window->IsSecureContext());
   3579              auto req = MakeRefPtr<GetUserMediaRequest>(
   3580                  window, callID, std::move(aDevices), aOptions, true,
   3581                  isHandlingUserInput);
   3582              if (taskCount > 1) {
   3583                // there is at least 1 pending gUM request
   3584                self->mPendingGUMRequest.AppendElement(req.forget());
   3585              } else {
   3586                nsCOMPtr<nsIObserverService> obs =
   3587                    services::GetObserverService();
   3588                obs->NotifyObservers(req, "getUserMedia:request", nullptr);
   3589              }
   3590            }
   3591            return p;
   3592          },
   3593          [](RefPtr<MediaMgrError> aError) {
   3594            LOG("SelectAudioOutput: EnumerateDevicesImpl "
   3595                "failure callback called!");
   3596            return LocalDevicePromise::CreateAndReject(std::move(aError),
   3597                                                       __func__);
   3598          });
   3599 }
   3600 
   3601 MediaEngine* MediaManager::GetBackend() {
   3602  MOZ_ASSERT(MediaManager::IsInMediaThread());
   3603  // Plugin backends as appropriate. The default engine also currently
   3604  // includes picture support for Android.
   3605  // This IS called off main-thread.
   3606  if (!mBackend) {
   3607 #if defined(MOZ_WEBRTC)
   3608    mBackend = new MediaEngineWebRTC();
   3609 #else
   3610    mBackend = new MediaEngineFake();
   3611 #endif
   3612    mDeviceListChangeListener = mBackend->DeviceListChangeEvent().Connect(
   3613        AbstractThread::MainThread(), this, &MediaManager::DeviceListChanged);
   3614  }
   3615  return mBackend;
   3616 }
   3617 
   3618 void MediaManager::OnNavigation(uint64_t aWindowID) {
   3619  MOZ_ASSERT(NS_IsMainThread());
   3620  LOG("OnNavigation for %" PRIu64, aWindowID);
   3621 
   3622  // Stop the streams for this window. The runnables check this value before
   3623  // making a call to content.
   3624 
   3625  nsTArray<nsString>* callIDs;
   3626  if (mCallIds.Get(aWindowID, &callIDs)) {
   3627    for (auto& callID : *callIDs) {
   3628      mActiveCallbacks.Remove(callID);
   3629      for (auto& request : mPendingGUMRequest.Clone()) {
   3630        nsString id;
   3631        request->GetCallID(id);
   3632        if (id == callID) {
   3633          mPendingGUMRequest.RemoveElement(request);
   3634        }
   3635      }
   3636    }
   3637    mCallIds.Remove(aWindowID);
   3638  }
   3639 
   3640  if (RefPtr<GetUserMediaWindowListener> listener =
   3641          GetWindowListener(aWindowID)) {
   3642    listener->RemoveAll();
   3643  }
   3644  MOZ_ASSERT(!GetWindowListener(aWindowID));
   3645 }
   3646 
   3647 void MediaManager::OnCameraMute(bool aMute) {
   3648  MOZ_ASSERT(NS_IsMainThread());
   3649  LOG("OnCameraMute for all windows");
   3650  mCamerasMuted = aMute;
   3651  // This is safe since we're on main-thread, and the windowlist can only
   3652  // be added to from the main-thread
   3653  for (const auto& window :
   3654       ToTArray<AutoTArray<RefPtr<GetUserMediaWindowListener>, 2>>(
   3655           mActiveWindows.Values())) {
   3656    window->MuteOrUnmuteCameras(aMute);
   3657  }
   3658 }
   3659 
   3660 void MediaManager::OnMicrophoneMute(bool aMute) {
   3661  MOZ_ASSERT(NS_IsMainThread());
   3662  LOG("OnMicrophoneMute for all windows");
   3663  mMicrophonesMuted = aMute;
   3664  // This is safe since we're on main-thread, and the windowlist can only
   3665  // be added to from the main-thread
   3666  for (const auto& window :
   3667       ToTArray<AutoTArray<RefPtr<GetUserMediaWindowListener>, 2>>(
   3668           mActiveWindows.Values())) {
   3669    window->MuteOrUnmuteMicrophones(aMute);
   3670  }
   3671 }
   3672 
   3673 RefPtr<GetUserMediaWindowListener> MediaManager::GetOrMakeWindowListener(
   3674    nsPIDOMWindowInner* aWindow) {
   3675  Document* doc = aWindow->GetExtantDoc();
   3676  if (!doc) {
   3677    // The window has been destroyed. Destroyed windows don't have listeners.
   3678    return nullptr;
   3679  }
   3680  nsIPrincipal* principal = doc->NodePrincipal();
   3681  uint64_t windowId = aWindow->WindowID();
   3682  RefPtr<GetUserMediaWindowListener> windowListener =
   3683      GetWindowListener(windowId);
   3684  if (windowListener) {
   3685    MOZ_ASSERT(PrincipalHandleMatches(windowListener->GetPrincipalHandle(),
   3686                                      principal));
   3687  } else {
   3688    windowListener = new GetUserMediaWindowListener(
   3689        windowId, MakePrincipalHandle(principal));
   3690    AddWindowID(windowId, windowListener);
   3691  }
   3692  return windowListener;
   3693 }
   3694 
   3695 void MediaManager::AddWindowID(uint64_t aWindowId,
   3696                               RefPtr<GetUserMediaWindowListener> aListener) {
   3697  MOZ_ASSERT(NS_IsMainThread());
   3698  // Store the WindowID in a hash table and mark as active. The entry is removed
   3699  // when this window is closed or navigated away from.
   3700  // This is safe since we're on main-thread, and the windowlist can only
   3701  // be invalidated from the main-thread (see OnNavigation)
   3702  if (IsWindowStillActive(aWindowId)) {
   3703    MOZ_ASSERT(false, "Window already added");
   3704    return;
   3705  }
   3706 
   3707  aListener->MuteOrUnmuteCameras(mCamerasMuted);
   3708  aListener->MuteOrUnmuteMicrophones(mMicrophonesMuted);
   3709  GetActiveWindows()->InsertOrUpdate(aWindowId, std::move(aListener));
   3710 
   3711  RefPtr<WindowGlobalChild> wgc =
   3712      WindowGlobalChild::GetByInnerWindowId(aWindowId);
   3713  if (wgc) {
   3714    wgc->BlockBFCacheFor(BFCacheStatus::ACTIVE_GET_USER_MEDIA);
   3715  }
   3716 }
   3717 
   3718 void MediaManager::RemoveWindowID(uint64_t aWindowId) {
   3719  RefPtr<WindowGlobalChild> wgc =
   3720      WindowGlobalChild::GetByInnerWindowId(aWindowId);
   3721  if (wgc) {
   3722    wgc->UnblockBFCacheFor(BFCacheStatus::ACTIVE_GET_USER_MEDIA);
   3723  }
   3724 
   3725  mActiveWindows.Remove(aWindowId);
   3726 
   3727  // get outer windowID
   3728  auto* window = nsGlobalWindowInner::GetInnerWindowWithId(aWindowId);
   3729  if (!window) {
   3730    LOG("No inner window for %" PRIu64, aWindowId);
   3731    return;
   3732  }
   3733 
   3734  auto* outer = window->GetOuterWindow();
   3735  if (!outer) {
   3736    LOG("No outer window for inner %" PRIu64, aWindowId);
   3737    return;
   3738  }
   3739 
   3740  uint64_t outerID = outer->WindowID();
   3741 
   3742  // Notify the UI that this window no longer has gUM active
   3743  char windowBuffer[32];
   3744  SprintfLiteral(windowBuffer, "%" PRIu64, outerID);
   3745  nsString data = NS_ConvertUTF8toUTF16(windowBuffer);
   3746 
   3747  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   3748  obs->NotifyWhenScriptSafe(nullptr, "recording-window-ended", data.get());
   3749  LOG("Sent recording-window-ended for window %" PRIu64 " (outer %" PRIu64 ")",
   3750      aWindowId, outerID);
   3751 }
   3752 
   3753 bool MediaManager::IsWindowListenerStillActive(
   3754    const RefPtr<GetUserMediaWindowListener>& aListener) {
   3755  MOZ_DIAGNOSTIC_ASSERT(aListener);
   3756  return aListener && aListener == GetWindowListener(aListener->WindowID());
   3757 }
   3758 
   3759 nsresult MediaManager::GetPref(nsIPrefBranch* aBranch, const char* aPref,
   3760                               const char* aData, int32_t* aVal) {
   3761  if (aData && strcmp(aPref, aData) != 0) {
   3762    return NS_ERROR_INVALID_ARG;
   3763  }
   3764 
   3765  int32_t temp;
   3766  nsresult rv = aBranch->GetIntPref(aPref, &temp);
   3767  if (NS_SUCCEEDED(rv)) {
   3768    *aVal = temp;
   3769  }
   3770 
   3771  return rv;
   3772 }
   3773 
   3774 nsresult MediaManager::GetPrefBool(nsIPrefBranch* aBranch, const char* aPref,
   3775                                   const char* aData, bool* aVal) {
   3776  if (aData && strcmp(aPref, aData) != 0) {
   3777    return NS_ERROR_INVALID_ARG;
   3778  }
   3779 
   3780  bool temp;
   3781  nsresult rv = aBranch->GetBoolPref(aPref, &temp);
   3782  if (NS_SUCCEEDED(rv)) {
   3783    *aVal = temp;
   3784  }
   3785 
   3786  return rv;
   3787 }
   3788 
   3789 #ifdef MOZ_WEBRTC
   3790 template <class Enum, class Int>
   3791 constexpr Enum ClampEnum(Int v) {
   3792  return std::clamp(
   3793      static_cast<Enum>(SaturatingCast<std::underlying_type_t<Enum>>(v)),
   3794      ContiguousEnumValues<Enum>::min, ContiguousEnumValues<Enum>::max);
   3795 }
   3796 #endif
   3797 
   3798 void MediaManager::GetPrefs(nsIPrefBranch* aBranch, const char* aData) {
   3799  GetPref(aBranch, "media.navigator.video.default_width", aData,
   3800          &mPrefs.mWidth);
   3801  GetPref(aBranch, "media.navigator.video.default_height", aData,
   3802          &mPrefs.mHeight);
   3803  GetPref(aBranch, "media.navigator.video.default_fps", aData, &mPrefs.mFPS);
   3804  GetPref(aBranch, "media.navigator.audio.fake_frequency", aData,
   3805          &mPrefs.mFreq);
   3806 #ifdef MOZ_WEBRTC
   3807  GetPrefBool(aBranch, "media.navigator.video.resize_mode.enabled", aData,
   3808              &mPrefs.mResizeModeEnabled);
   3809  int32_t resizeMode{};
   3810  if (NS_SUCCEEDED(GetPref(aBranch, "media.navigator.video.default_resize_mode",
   3811                           aData, &resizeMode))) {
   3812    mPrefs.mResizeMode = ClampEnum<VideoResizeModeEnum>(resizeMode);
   3813  }
   3814  GetPrefBool(aBranch, "media.getusermedia.audio.processing.platform.enabled",
   3815              aData, &mPrefs.mUsePlatformProcessing);
   3816  GetPrefBool(aBranch, "media.getusermedia.audio.processing.aec.enabled", aData,
   3817              &mPrefs.mAecOn);
   3818  GetPrefBool(aBranch, "media.getusermedia.audio.processing.agc.enabled", aData,
   3819              &mPrefs.mAgcOn);
   3820  GetPrefBool(aBranch, "media.getusermedia.audio.processing.hpf.enabled", aData,
   3821              &mPrefs.mHPFOn);
   3822  GetPrefBool(aBranch, "media.getusermedia.audio.processing.noise.enabled",
   3823              aData, &mPrefs.mNoiseOn);
   3824  GetPrefBool(aBranch, "media.getusermedia.audio.processing.transient.enabled",
   3825              aData, &mPrefs.mTransientOn);
   3826  GetPrefBool(aBranch, "media.getusermedia.audio.processing.agc2.forced", aData,
   3827              &mPrefs.mAgc2Forced);
   3828  // Use 0 or 1 to force to false or true
   3829  // EchoCanceller3Config::echo_removal_control.has_clock_drift.
   3830  // -1 is the default, which means automatically set has_clock_drift as
   3831  // deemed appropriate.
   3832  GetPref(aBranch, "media.getusermedia.audio.processing.aec.expect_drift",
   3833          aData, &mPrefs.mExpectDrift);
   3834  GetPref(aBranch, "media.getusermedia.audio.processing.agc", aData,
   3835          &mPrefs.mAgc);
   3836  GetPref(aBranch, "media.getusermedia.audio.processing.noise", aData,
   3837          &mPrefs.mNoise);
   3838  GetPref(aBranch, "media.getusermedia.audio.max_channels", aData,
   3839          &mPrefs.mChannels);
   3840 #endif
   3841  LOG("%s: default prefs: %dx%d @%dfps, %dHz test tones, "
   3842      "resize mode: %s, platform processing: %s, "
   3843      "aec: %s, agc: %s, hpf: %s, noise: %s, drift: %s, agc level: %d, agc "
   3844      "version: "
   3845      "%s, noise level: %d, transient: %s, channels %d",
   3846      __FUNCTION__, mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mFreq,
   3847      mPrefs.mResizeModeEnabled ? dom::GetEnumString(mPrefs.mResizeMode).get()
   3848                                : "disabled",
   3849      mPrefs.mUsePlatformProcessing ? "on" : "off",
   3850      mPrefs.mAecOn ? "on" : "off", mPrefs.mAgcOn ? "on" : "off",
   3851      mPrefs.mHPFOn ? "on" : "off", mPrefs.mNoiseOn ? "on" : "off",
   3852      mPrefs.mExpectDrift < 0 ? "auto"
   3853      : mPrefs.mExpectDrift   ? "on"
   3854                              : "off",
   3855      mPrefs.mAgc, mPrefs.mAgc2Forced ? "2" : "1", mPrefs.mNoise,
   3856      mPrefs.mTransientOn ? "on" : "off", mPrefs.mChannels);
   3857 }
   3858 
   3859 void MediaManager::Shutdown() {
   3860  MOZ_ASSERT(NS_IsMainThread());
   3861  if (sHasMainThreadShutdown) {
   3862    return;
   3863  }
   3864 
   3865  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   3866 
   3867  obs->RemoveObserver(this, "last-pb-context-exited");
   3868  obs->RemoveObserver(this, "getUserMedia:privileged:allow");
   3869  obs->RemoveObserver(this, "getUserMedia:response:allow");
   3870  obs->RemoveObserver(this, "getUserMedia:response:deny");
   3871  obs->RemoveObserver(this, "getUserMedia:response:noOSPermission");
   3872  obs->RemoveObserver(this, "getUserMedia:revoke");
   3873  obs->RemoveObserver(this, "getUserMedia:muteVideo");
   3874  obs->RemoveObserver(this, "getUserMedia:unmuteVideo");
   3875  obs->RemoveObserver(this, "getUserMedia:muteAudio");
   3876  obs->RemoveObserver(this, "getUserMedia:unmuteAudio");
   3877  obs->RemoveObserver(this, "application-background");
   3878  obs->RemoveObserver(this, "application-foreground");
   3879 
   3880  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   3881  if (prefs) {
   3882    ForeachObservedPref([&](const nsLiteralCString& aPrefName) {
   3883      prefs->RemoveObserver(aPrefName, this);
   3884    });
   3885  }
   3886 
   3887  if (mDeviceChangeTimer) {
   3888    mDeviceChangeTimer->Cancel();
   3889    // Drop ref to MediaTimer early to avoid blocking SharedThreadPool shutdown
   3890    mDeviceChangeTimer = nullptr;
   3891  }
   3892 
   3893  {
   3894    // Close off any remaining active windows.
   3895 
   3896    // Live capture at this point is rare but can happen. Stopping it will make
   3897    // the window listeners attempt to remove themselves from the active windows
   3898    // table. We cannot touch the table at point so we grab a copy of the window
   3899    // listeners first.
   3900    const auto listeners = ToArray(GetActiveWindows()->Values());
   3901    for (const auto& listener : listeners) {
   3902      listener->RemoveAll();
   3903    }
   3904  }
   3905  MOZ_ASSERT(GetActiveWindows()->Count() == 0);
   3906 
   3907  GetActiveWindows()->Clear();
   3908  mActiveCallbacks.Clear();
   3909  mCallIds.Clear();
   3910  mPendingGUMRequest.Clear();
   3911 #ifdef MOZ_WEBRTC
   3912  mLogHandle = nullptr;
   3913 #endif
   3914 
   3915  // From main thread's point of view, shutdown is now done.
   3916  // All that remains is shutting down the media thread.
   3917  sHasMainThreadShutdown = true;
   3918 
   3919  // Release the backend (and call Shutdown()) from within mMediaThread.
   3920  // Don't use MediaManager::Dispatch() because we're
   3921  // sHasMainThreadShutdown == true here!
   3922  MOZ_ALWAYS_SUCCEEDS(mMediaThread->Dispatch(
   3923      NS_NewRunnableFunction(__func__, [self = RefPtr(this), this]() {
   3924        LOG("MediaManager Thread Shutdown");
   3925        MOZ_ASSERT(IsInMediaThread());
   3926        // Must shutdown backend on MediaManager thread, since that's
   3927        // where we started it from!
   3928        if (mBackend) {
   3929          mBackend->Shutdown();  // idempotent
   3930          mDeviceListChangeListener.DisconnectIfExists();
   3931        }
   3932        // last reference, will invoke Shutdown() again
   3933        mBackend = nullptr;
   3934      })));
   3935 
   3936  // note that this == sSingleton
   3937  MOZ_ASSERT(this == sSingleton);
   3938 
   3939  // Explicitly shut down the TaskQueue so that it releases its
   3940  // SharedThreadPool when all tasks have completed.  SharedThreadPool blocks
   3941  // XPCOM shutdown from proceeding beyond "xpcom-shutdown-threads" until all
   3942  // SharedThreadPools are released, but the nsComponentManager keeps a
   3943  // reference to the MediaManager for the nsIMediaManagerService until much
   3944  // later in shutdown.  This also provides additional assurance that no
   3945  // further tasks will be queued.
   3946  mMediaThread->BeginShutdown()->Then(
   3947      GetMainThreadSerialEventTarget(), __func__, [] {
   3948        LOG("MediaManager shutdown lambda running, releasing MediaManager "
   3949            "singleton");
   3950        // Remove async shutdown blocker
   3951        media::MustGetShutdownBarrier()->RemoveBlocker(
   3952            sSingleton->mShutdownBlocker);
   3953 
   3954        sSingleton = nullptr;
   3955      });
   3956 }
   3957 
   3958 void MediaManager::SendPendingGUMRequest() {
   3959  if (mPendingGUMRequest.Length() > 0) {
   3960    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   3961    obs->NotifyObservers(mPendingGUMRequest[0], "getUserMedia:request",
   3962                         nullptr);
   3963    mPendingGUMRequest.RemoveElementAt(0);
   3964  }
   3965 }
   3966 
   3967 bool IsGUMResponseNoAccess(const char* aTopic,
   3968                           MediaMgrError::Name& aErrorName) {
   3969  if (!strcmp(aTopic, "getUserMedia:response:deny")) {
   3970    aErrorName = MediaMgrError::Name::NotAllowedError;
   3971    return true;
   3972  }
   3973 
   3974  if (!strcmp(aTopic, "getUserMedia:response:noOSPermission")) {
   3975    aErrorName = MediaMgrError::Name::NotFoundError;
   3976    return true;
   3977  }
   3978 
   3979  return false;
   3980 }
   3981 
   3982 static MediaSourceEnum ParseScreenColonWindowID(const char16_t* aData,
   3983                                                uint64_t* aWindowIDOut) {
   3984  MOZ_ASSERT(aWindowIDOut);
   3985  // may be windowid or screen:windowid
   3986  const nsDependentString data(aData);
   3987  if (Substring(data, 0, strlen("screen:")).EqualsLiteral("screen:")) {
   3988    nsresult rv;
   3989    *aWindowIDOut = Substring(data, strlen("screen:")).ToInteger64(&rv);
   3990    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   3991    return MediaSourceEnum::Screen;
   3992  }
   3993  nsresult rv;
   3994  *aWindowIDOut = data.ToInteger64(&rv);
   3995  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   3996  return MediaSourceEnum::Camera;
   3997 }
   3998 
   3999 nsresult MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
   4000                               const char16_t* aData) {
   4001  MOZ_ASSERT(NS_IsMainThread());
   4002 
   4003  MediaMgrError::Name gumNoAccessError = MediaMgrError::Name::NotAllowedError;
   4004 
   4005  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   4006    nsCOMPtr<nsIPrefBranch> branch(do_QueryInterface(aSubject));
   4007    if (branch) {
   4008      GetPrefs(branch, NS_ConvertUTF16toUTF8(aData).get());
   4009      DeviceListChanged();
   4010    }
   4011  } else if (!strcmp(aTopic, "last-pb-context-exited")) {
   4012    // Clear memory of private-browsing-specific deviceIds. Fire and forget.
   4013    media::SanitizeOriginKeys(0, true);
   4014    return NS_OK;
   4015  } else if (!strcmp(aTopic, "getUserMedia:got-device-permission")) {
   4016    MOZ_ASSERT(aSubject);
   4017    nsCOMPtr<nsIRunnable> task = do_QueryInterface(aSubject);
   4018    MediaManager::Dispatch(NewTaskFrom([task] { task->Run(); }));
   4019    return NS_OK;
   4020  } else if (!strcmp(aTopic, "getUserMedia:privileged:allow") ||
   4021             !strcmp(aTopic, "getUserMedia:response:allow")) {
   4022    nsString key(aData);
   4023    RefPtr<GetUserMediaTask> task = TakeGetUserMediaTask(key);
   4024    if (!task) {
   4025      return NS_OK;
   4026    }
   4027 
   4028    if (sHasMainThreadShutdown) {
   4029      task->Denied(MediaMgrError::Name::AbortError, "In shutdown"_ns);
   4030      return NS_OK;
   4031    }
   4032    if (NS_WARN_IF(!aSubject)) {
   4033      return NS_ERROR_FAILURE;  // ignored
   4034    }
   4035    // Permission has been granted.  aSubject contains the particular device
   4036    // or devices selected and approved by the user, if any.
   4037    nsCOMPtr<nsIArray> array(do_QueryInterface(aSubject));
   4038    MOZ_ASSERT(array);
   4039    uint32_t len = 0;
   4040    array->GetLength(&len);
   4041    RefPtr<LocalMediaDevice> audioInput;
   4042    RefPtr<LocalMediaDevice> videoInput;
   4043    RefPtr<LocalMediaDevice> audioOutput;
   4044    for (uint32_t i = 0; i < len; i++) {
   4045      nsCOMPtr<nsIMediaDevice> device;
   4046      array->QueryElementAt(i, NS_GET_IID(nsIMediaDevice),
   4047                            getter_AddRefs(device));
   4048      MOZ_ASSERT(device);  // shouldn't be returning anything else...
   4049      if (!device) {
   4050        continue;
   4051      }
   4052 
   4053      // Casting here is safe because a LocalMediaDevice is created
   4054      // only in Gecko side, JS can only query for an instance.
   4055      auto* dev = static_cast<LocalMediaDevice*>(device.get());
   4056      switch (dev->Kind()) {
   4057        case MediaDeviceKind::Videoinput:
   4058          if (!videoInput) {
   4059            videoInput = dev;
   4060          }
   4061          break;
   4062        case MediaDeviceKind::Audioinput:
   4063          if (!audioInput) {
   4064            audioInput = dev;
   4065          }
   4066          break;
   4067        case MediaDeviceKind::Audiooutput:
   4068          if (!audioOutput) {
   4069            audioOutput = dev;
   4070          }
   4071          break;
   4072        default:
   4073          MOZ_CRASH("Unexpected device kind");
   4074      }
   4075    }
   4076 
   4077    if (GetUserMediaStreamTask* streamTask = task->AsGetUserMediaStreamTask()) {
   4078      bool needVideo = IsOn(streamTask->GetConstraints().mVideo);
   4079      bool needAudio = IsOn(streamTask->GetConstraints().mAudio);
   4080      MOZ_ASSERT(needVideo || needAudio);
   4081 
   4082      if ((needVideo && !videoInput) || (needAudio && !audioInput)) {
   4083        task->Denied(MediaMgrError::Name::NotAllowedError);
   4084        return NS_OK;
   4085      }
   4086      streamTask->Allowed(std::move(audioInput), std::move(videoInput));
   4087      return NS_OK;
   4088    }
   4089    if (SelectAudioOutputTask* outputTask = task->AsSelectAudioOutputTask()) {
   4090      if (!audioOutput) {
   4091        task->Denied(MediaMgrError::Name::NotAllowedError);
   4092        return NS_OK;
   4093      }
   4094      outputTask->Allowed(std::move(audioOutput));
   4095      return NS_OK;
   4096    }
   4097 
   4098    NS_WARNING("Unknown task type in getUserMedia");
   4099    return NS_ERROR_FAILURE;
   4100 
   4101  } else if (IsGUMResponseNoAccess(aTopic, gumNoAccessError)) {
   4102    nsString key(aData);
   4103    RefPtr<GetUserMediaTask> task = TakeGetUserMediaTask(key);
   4104    if (task) {
   4105      task->Denied(gumNoAccessError);
   4106      SendPendingGUMRequest();
   4107    }
   4108    return NS_OK;
   4109 
   4110  } else if (!strcmp(aTopic, "getUserMedia:revoke")) {
   4111    uint64_t windowID;
   4112    if (ParseScreenColonWindowID(aData, &windowID) == MediaSourceEnum::Screen) {
   4113      LOG("Revoking ScreenCapture access for window %" PRIu64, windowID);
   4114      StopScreensharing(windowID);
   4115    } else {
   4116      LOG("Revoking MediaCapture access for window %" PRIu64, windowID);
   4117      OnNavigation(windowID);
   4118    }
   4119    return NS_OK;
   4120  } else if (!strcmp(aTopic, "getUserMedia:muteVideo") ||
   4121             !strcmp(aTopic, "getUserMedia:unmuteVideo")) {
   4122    OnCameraMute(!strcmp(aTopic, "getUserMedia:muteVideo"));
   4123    return NS_OK;
   4124  } else if (!strcmp(aTopic, "getUserMedia:muteAudio") ||
   4125             !strcmp(aTopic, "getUserMedia:unmuteAudio")) {
   4126    OnMicrophoneMute(!strcmp(aTopic, "getUserMedia:muteAudio"));
   4127    return NS_OK;
   4128  } else if ((!strcmp(aTopic, "application-background") ||
   4129              !strcmp(aTopic, "application-foreground")) &&
   4130             StaticPrefs::media_getusermedia_camera_background_mute_enabled()) {
   4131    // On mobile we turn off any cameras (but not mics) while in the background.
   4132    // Keeping things simple for now by duplicating test-covered code above.
   4133    //
   4134    // NOTE: If a mobile device ever wants to implement "getUserMedia:muteVideo"
   4135    // as well, it'd need to update this code to handle & test the combinations.
   4136    OnCameraMute(!strcmp(aTopic, "application-background"));
   4137  }
   4138 
   4139  return NS_OK;
   4140 }
   4141 
   4142 NS_IMETHODIMP
   4143 MediaManager::CollectReports(nsIHandleReportCallback* aHandleReport,
   4144                             nsISupports* aData, bool aAnonymize) {
   4145  size_t amount = 0;
   4146  amount += mActiveWindows.ShallowSizeOfExcludingThis(MallocSizeOf);
   4147  for (const GetUserMediaWindowListener* listener : mActiveWindows.Values()) {
   4148    amount += listener->SizeOfIncludingThis(MallocSizeOf);
   4149  }
   4150  amount += mActiveCallbacks.ShallowSizeOfExcludingThis(MallocSizeOf);
   4151  for (const GetUserMediaTask* task : mActiveCallbacks.Values()) {
   4152    // Assume nsString buffers for keys are accounted in mCallIds.
   4153    amount += task->SizeOfIncludingThis(MallocSizeOf);
   4154  }
   4155  amount += mCallIds.ShallowSizeOfExcludingThis(MallocSizeOf);
   4156  for (const auto& array : mCallIds.Values()) {
   4157    amount += array->ShallowSizeOfExcludingThis(MallocSizeOf);
   4158    for (const nsString& callID : *array) {
   4159      amount += callID.SizeOfExcludingThisEvenIfShared(MallocSizeOf);
   4160    }
   4161  }
   4162  amount += mPendingGUMRequest.ShallowSizeOfExcludingThis(MallocSizeOf);
   4163  // GetUserMediaRequest pointees of mPendingGUMRequest do not have support
   4164  // for memory accounting.  mPendingGUMRequest logic should probably be moved
   4165  // to the front end (bug 1691625).
   4166  MOZ_COLLECT_REPORT("explicit/media/media-manager-aggregates", KIND_HEAP,
   4167                     UNITS_BYTES, amount,
   4168                     "Memory used by MediaManager variable length members.");
   4169  return NS_OK;
   4170 }
   4171 
   4172 nsresult MediaManager::GetActiveMediaCaptureWindows(nsIArray** aArray) {
   4173  MOZ_ASSERT(aArray);
   4174 
   4175  nsCOMPtr<nsIMutableArray> array = nsArray::Create();
   4176 
   4177  for (const auto& entry : mActiveWindows) {
   4178    const uint64_t& id = entry.GetKey();
   4179    RefPtr<GetUserMediaWindowListener> winListener = entry.GetData();
   4180    if (!winListener) {
   4181      continue;
   4182    }
   4183 
   4184    auto* window = nsGlobalWindowInner::GetInnerWindowWithId(id);
   4185    MOZ_ASSERT(window);
   4186    // XXXkhuey ...
   4187    if (!window) {
   4188      continue;
   4189    }
   4190 
   4191    if (winListener->CapturingVideo() || winListener->CapturingAudio()) {
   4192      array->AppendElement(ToSupports(window));
   4193    }
   4194  }
   4195 
   4196  array.forget(aArray);
   4197  return NS_OK;
   4198 }
   4199 
   4200 struct CaptureWindowStateData {
   4201  uint16_t* mCamera;
   4202  uint16_t* mMicrophone;
   4203  uint16_t* mScreenShare;
   4204  uint16_t* mWindowShare;
   4205  uint16_t* mAppShare;
   4206  uint16_t* mBrowserShare;
   4207 };
   4208 
   4209 NS_IMETHODIMP
   4210 MediaManager::MediaCaptureWindowState(
   4211    nsIDOMWindow* aCapturedWindow, uint16_t* aCamera, uint16_t* aMicrophone,
   4212    uint16_t* aScreen, uint16_t* aWindow, uint16_t* aBrowser,
   4213    nsTArray<RefPtr<nsIMediaDevice>>& aDevices) {
   4214  MOZ_ASSERT(NS_IsMainThread());
   4215 
   4216  CaptureState camera = CaptureState::Off;
   4217  CaptureState microphone = CaptureState::Off;
   4218  CaptureState screen = CaptureState::Off;
   4219  CaptureState window = CaptureState::Off;
   4220  CaptureState browser = CaptureState::Off;
   4221  RefPtr<LocalMediaDeviceSetRefCnt> devices;
   4222 
   4223  nsCOMPtr<nsPIDOMWindowInner> piWin = do_QueryInterface(aCapturedWindow);
   4224  if (piWin) {
   4225    if (RefPtr<GetUserMediaWindowListener> listener =
   4226            GetWindowListener(piWin->WindowID())) {
   4227      camera = listener->CapturingSource(MediaSourceEnum::Camera);
   4228      microphone = listener->CapturingSource(MediaSourceEnum::Microphone);
   4229      screen = listener->CapturingSource(MediaSourceEnum::Screen);
   4230      window = listener->CapturingSource(MediaSourceEnum::Window);
   4231      browser = listener->CapturingSource(MediaSourceEnum::Browser);
   4232      devices = listener->GetDevices();
   4233    }
   4234  }
   4235 
   4236  *aCamera = FromCaptureState(camera);
   4237  *aMicrophone = FromCaptureState(microphone);
   4238  *aScreen = FromCaptureState(screen);
   4239  *aWindow = FromCaptureState(window);
   4240  *aBrowser = FromCaptureState(browser);
   4241  if (devices) {
   4242    for (auto& device : *devices) {
   4243      aDevices.AppendElement(device);
   4244    }
   4245  }
   4246 
   4247  LOG("%s: window %" PRIu64 " capturing %s %s %s %s %s", __FUNCTION__,
   4248      piWin ? piWin->WindowID() : -1,
   4249      *aCamera == nsIMediaManagerService::STATE_CAPTURE_ENABLED
   4250          ? "camera (enabled)"
   4251          : (*aCamera == nsIMediaManagerService::STATE_CAPTURE_DISABLED
   4252                 ? "camera (disabled)"
   4253                 : ""),
   4254      *aMicrophone == nsIMediaManagerService::STATE_CAPTURE_ENABLED
   4255          ? "microphone (enabled)"
   4256          : (*aMicrophone == nsIMediaManagerService::STATE_CAPTURE_DISABLED
   4257                 ? "microphone (disabled)"
   4258                 : ""),
   4259      *aScreen ? "screenshare" : "", *aWindow ? "windowshare" : "",
   4260      *aBrowser ? "browsershare" : "");
   4261 
   4262  return NS_OK;
   4263 }
   4264 
   4265 NS_IMETHODIMP
   4266 MediaManager::SanitizeDeviceIds(int64_t aSinceWhen) {
   4267  MOZ_ASSERT(NS_IsMainThread());
   4268  LOG("%s: sinceWhen = %" PRId64, __FUNCTION__, aSinceWhen);
   4269 
   4270  media::SanitizeOriginKeys(aSinceWhen, false);  // we fire and forget
   4271  return NS_OK;
   4272 }
   4273 
   4274 void MediaManager::StopScreensharing(uint64_t aWindowID) {
   4275  // We need to stop window/screensharing for all streams in this innerwindow.
   4276 
   4277  if (RefPtr<GetUserMediaWindowListener> listener =
   4278          GetWindowListener(aWindowID)) {
   4279    listener->StopSharing();
   4280  }
   4281 }
   4282 
   4283 bool MediaManager::IsActivelyCapturingOrHasAPermission(uint64_t aWindowId) {
   4284  // Does page currently have a gUM stream active?
   4285 
   4286  nsCOMPtr<nsIArray> array;
   4287  GetActiveMediaCaptureWindows(getter_AddRefs(array));
   4288  uint32_t len;
   4289  array->GetLength(&len);
   4290  for (uint32_t i = 0; i < len; i++) {
   4291    nsCOMPtr<nsPIDOMWindowInner> win;
   4292    array->QueryElementAt(i, NS_GET_IID(nsPIDOMWindowInner),
   4293                          getter_AddRefs(win));
   4294    if (win && win->WindowID() == aWindowId) {
   4295      return true;
   4296    }
   4297  }
   4298 
   4299  // Or are persistent permissions (audio or video) granted?
   4300 
   4301  return GetPersistentPermissions(aWindowId)
   4302      .map([](auto&& aState) {
   4303        return aState.mMicrophonePermission ==
   4304                   PersistentPermissionState::Allow ||
   4305               aState.mCameraPermission == PersistentPermissionState::Allow;
   4306      })
   4307      .unwrapOr(false);
   4308 }
   4309 
   4310 DeviceListener::DeviceListener()
   4311    : mStopped(false),
   4312      mMainThreadCheck(nullptr),
   4313      mPrincipalHandle(PRINCIPAL_HANDLE_NONE),
   4314      mWindowListener(nullptr) {}
   4315 
   4316 void DeviceListener::Register(GetUserMediaWindowListener* aListener) {
   4317  LOG("DeviceListener %p registering with window listener %p", this, aListener);
   4318 
   4319  MOZ_ASSERT(aListener, "No listener");
   4320  MOZ_ASSERT(!mWindowListener, "Already registered");
   4321  MOZ_ASSERT(!Activated(), "Already activated");
   4322 
   4323  mPrincipalHandle = aListener->GetPrincipalHandle();
   4324  mWindowListener = aListener;
   4325 }
   4326 
   4327 void DeviceListener::Activate(RefPtr<LocalMediaDevice> aDevice,
   4328                              RefPtr<LocalTrackSource> aTrackSource,
   4329                              bool aStartMuted, bool aIsAllocated) {
   4330  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4331 
   4332  LOG("DeviceListener %p activating %s device %p", this,
   4333      dom::GetEnumString(aDevice->Kind()).get(), aDevice.get());
   4334 
   4335  MOZ_ASSERT(!mStopped, "Cannot activate stopped device listener");
   4336  MOZ_ASSERT(!Activated(), "Already activated");
   4337 
   4338  mMainThreadCheck = PR_GetCurrentThread();
   4339  bool offWhileDisabled =
   4340      (aDevice->GetMediaSource() == MediaSourceEnum::Microphone &&
   4341       Preferences::GetBool(
   4342           "media.getusermedia.microphone.off_while_disabled.enabled", true)) ||
   4343      (aDevice->GetMediaSource() == MediaSourceEnum::Camera &&
   4344       Preferences::GetBool(
   4345           "media.getusermedia.camera.off_while_disabled.enabled", true));
   4346 
   4347  if (MediaEventSource<void>* event = aDevice->Source()->CaptureEndedEvent()) {
   4348    mCaptureEndedListener = event->Connect(AbstractThread::MainThread(), this,
   4349                                           &DeviceListener::Stop);
   4350  }
   4351 
   4352  mDeviceState = MakeUnique<DeviceState>(
   4353      std::move(aDevice), std::move(aTrackSource), offWhileDisabled);
   4354  mDeviceState->mDeviceMuted = aStartMuted;
   4355  mDeviceState->mAllocated = aIsAllocated;
   4356  if (aStartMuted) {
   4357    mDeviceState->mTrackSource->Mute();
   4358  }
   4359 }
   4360 
   4361 RefPtr<DeviceListener::DeviceListenerPromise>
   4362 DeviceListener::InitializeAsync() {
   4363  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4364  MOZ_DIAGNOSTIC_ASSERT(!mStopped);
   4365 
   4366  return InvokeAsync(
   4367             MediaManager::Get()->mMediaThread, __func__,
   4368             [this, self = RefPtr(this), principal = GetPrincipalHandle(),
   4369              device = mDeviceState->mDevice,
   4370              track = mDeviceState->mTrackSource->mTrack] {
   4371               // Start the device if not muted, or if mOffWhileDisabled is
   4372               // false (the device should remain on even when muted).
   4373               bool startDevice = !mDeviceState->mDeviceMuted ||
   4374                                  !mDeviceState->mOffWhileDisabled;
   4375               nsresult rv = Initialize(principal, device, track, startDevice);
   4376               if (NS_SUCCEEDED(rv)) {
   4377                 return GenericPromise::CreateAndResolve(
   4378                     true, "DeviceListener::InitializeAsync success");
   4379               }
   4380               return GenericPromise::CreateAndReject(
   4381                   rv, "DeviceListener::InitializeAsync failure");
   4382             })
   4383      ->Then(
   4384          GetMainThreadSerialEventTarget(), __func__,
   4385          [self = RefPtr<DeviceListener>(this), this](bool) {
   4386            if (mStopped) {
   4387              // We were shut down during the async init
   4388              return DeviceListenerPromise::CreateAndResolve(true, __func__);
   4389            }
   4390 
   4391            MOZ_DIAGNOSTIC_ASSERT(!mDeviceState->mTrackEnabled);
   4392            MOZ_DIAGNOSTIC_ASSERT(!mDeviceState->mDeviceEnabled);
   4393            MOZ_DIAGNOSTIC_ASSERT(!mDeviceState->mStopped);
   4394 
   4395            mDeviceState->mDeviceEnabled = true;
   4396            mDeviceState->mTrackEnabled = true;
   4397            mDeviceState->mTrackEnabledTime = TimeStamp::Now();
   4398            return DeviceListenerPromise::CreateAndResolve(true, __func__);
   4399          },
   4400          [self = RefPtr<DeviceListener>(this), this](nsresult aRv) {
   4401            auto kind = mDeviceState->mDevice->Kind();
   4402            RefPtr<MediaMgrError> err;
   4403            if (aRv == NS_ERROR_NOT_AVAILABLE &&
   4404                kind == MediaDeviceKind::Audioinput) {
   4405              nsCString log;
   4406              log.AssignLiteral("Concurrent mic process limit.");
   4407              err = MakeRefPtr<MediaMgrError>(
   4408                  MediaMgrError::Name::NotReadableError, std::move(log));
   4409            } else if (NS_FAILED(aRv)) {
   4410              nsCString log;
   4411              log.AppendPrintf("Starting %s failed",
   4412                               dom::GetEnumString(kind).get());
   4413              err = MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError,
   4414                                              std::move(log));
   4415            }
   4416 
   4417            if (mStopped) {
   4418              return DeviceListenerPromise::CreateAndReject(err, __func__);
   4419            }
   4420 
   4421            MOZ_DIAGNOSTIC_ASSERT(!mDeviceState->mTrackEnabled);
   4422            MOZ_DIAGNOSTIC_ASSERT(!mDeviceState->mDeviceEnabled);
   4423            MOZ_DIAGNOSTIC_ASSERT(!mDeviceState->mStopped);
   4424 
   4425            Stop();
   4426 
   4427            return DeviceListenerPromise::CreateAndReject(err, __func__);
   4428          });
   4429 }
   4430 
   4431 nsresult DeviceListener::Initialize(PrincipalHandle aPrincipal,
   4432                                    LocalMediaDevice* aDevice,
   4433                                    MediaTrack* aTrack, bool aStartDevice) {
   4434  MOZ_ASSERT(MediaManager::IsInMediaThread());
   4435 
   4436  auto kind = aDevice->Kind();
   4437  aDevice->SetTrack(aTrack, aPrincipal);
   4438  nsresult rv = aStartDevice ? aDevice->Start() : NS_OK;
   4439  if (kind == MediaDeviceKind::Audioinput ||
   4440      kind == MediaDeviceKind::Videoinput) {
   4441    if ((rv == NS_ERROR_NOT_AVAILABLE && kind == MediaDeviceKind::Audioinput) ||
   4442        (NS_FAILED(rv) && kind == MediaDeviceKind::Videoinput)) {
   4443      PR_Sleep(200);
   4444      rv = aDevice->Start();
   4445    }
   4446  }
   4447  LOG("started %s device %p", dom::GetEnumString(kind).get(), aDevice);
   4448  return rv;
   4449 }
   4450 
   4451 already_AddRefed<DeviceListener> DeviceListener::Clone() const {
   4452  MOZ_ASSERT(NS_IsMainThread());
   4453  MediaManager* mgr = MediaManager::GetIfExists();
   4454  if (!mgr) {
   4455    return nullptr;
   4456  }
   4457  if (!mWindowListener) {
   4458    return nullptr;
   4459  }
   4460  auto* thisDevice = GetDevice();
   4461  if (!thisDevice) {
   4462    return nullptr;
   4463  }
   4464 
   4465  auto* thisTrackSource = GetTrackSource();
   4466  if (!thisTrackSource) {
   4467    return nullptr;
   4468  }
   4469 
   4470  // See PrepareDOMStream for how a gUM/gDM track is created.
   4471  RefPtr<MediaTrack> track;
   4472  MediaTrackGraph* mtg = thisTrackSource->mTrack->Graph();
   4473  if (const auto source = thisDevice->GetMediaSource();
   4474      source == dom::MediaSourceEnum::Microphone) {
   4475 #ifdef MOZ_WEBRTC
   4476    if (thisDevice->IsFake()) {
   4477      track = mtg->CreateSourceTrack(MediaSegment::AUDIO);
   4478    } else {
   4479      track = AudioProcessingTrack::Create(mtg);
   4480      track->Suspend();  // Microphone source resumes in SetTrack
   4481    }
   4482 #else
   4483    track = mtg->CreateSourceTrack(MediaSegment::AUDIO);
   4484 #endif
   4485  } else if (source == dom::MediaSourceEnum::Camera ||
   4486             source == dom::MediaSourceEnum::Screen ||
   4487             source == dom::MediaSourceEnum::Window ||
   4488             source == dom::MediaSourceEnum::Browser) {
   4489    track = mtg->CreateSourceTrack(MediaSegment::VIDEO);
   4490  }
   4491 
   4492  if (!track) {
   4493    return nullptr;
   4494  }
   4495 
   4496  RefPtr device = thisDevice->Clone();
   4497  auto listener = MakeRefPtr<DeviceListener>();
   4498  auto trackSource = MakeRefPtr<LocalTrackSource>(
   4499      thisTrackSource->GetPrincipal(), thisTrackSource->mLabel, listener,
   4500      thisTrackSource->mSource, track, thisTrackSource->mPeerIdentity,
   4501      thisTrackSource->mTrackingId);
   4502 
   4503  LOG("DeviceListener %p registering clone", this);
   4504  mWindowListener->Register(listener);
   4505  LOG("DeviceListener %p activating clone", this);
   4506  mWindowListener->Activate(listener, device, trackSource,
   4507                            /*aIsAllocated=*/false);
   4508 
   4509  listener->mDeviceState->mDeviceEnabled = mDeviceState->mDeviceEnabled;
   4510  listener->mDeviceState->mDeviceMuted = mDeviceState->mDeviceMuted;
   4511  listener->mDeviceState->mTrackEnabled = mDeviceState->mTrackEnabled;
   4512  listener->mDeviceState->mTrackEnabledTime = TimeStamp::Now();
   4513 
   4514  // We have to do an async operation here, even though Clone() is sync.
   4515  // This is fine because JS will not be able to trigger any operation to run
   4516  // async on the media thread.
   4517  LOG("DeviceListener %p allocating clone device %p async", this, device.get());
   4518  InvokeAsync(
   4519      mgr->mMediaThread, __func__,
   4520      [thisDevice = RefPtr(thisDevice), device, prefs = mgr->mPrefs,
   4521       windowId = mWindowListener->WindowID(), listener,
   4522       principal = GetPrincipalHandle(), track,
   4523       startDevice = !listener->mDeviceState->mOffWhileDisabled ||
   4524                     (!listener->mDeviceState->mDeviceMuted &&
   4525                      listener->mDeviceState->mDeviceEnabled)] {
   4526        const char* outBadConstraint{};
   4527        nsresult rv = device->Source()->Allocate(
   4528            thisDevice->Constraints(), prefs, windowId, &outBadConstraint);
   4529        LOG("Allocated clone device %p. rv=%s", device.get(),
   4530            GetStaticErrorName(rv));
   4531        if (NS_FAILED(rv)) {
   4532          return GenericPromise::CreateAndReject(
   4533              rv, "DeviceListener::Clone failure #1");
   4534        }
   4535        rv = listener->Initialize(principal, device, track, startDevice);
   4536        if (NS_SUCCEEDED(rv)) {
   4537          return GenericPromise::CreateAndResolve(
   4538              true, "DeviceListener::Clone success");
   4539        }
   4540        return GenericPromise::CreateAndReject(
   4541            rv, "DeviceListener::Clone failure #2");
   4542      })
   4543      ->Then(GetMainThreadSerialEventTarget(), __func__,
   4544             [listener, device,
   4545              trackSource](GenericPromise::ResolveOrRejectValue&& aValue) {
   4546               if (aValue.IsReject()) {
   4547                 // Allocating/initializing failed. Stopping the device listener
   4548                 // will destroy the MediaStreamTrackSource's MediaTrack, which
   4549                 // will make the MediaStreamTrack's mTrack MediaTrack auto-end
   4550                 // due to lack of inputs. This makes the MediaStreamTrack's
   4551                 // readyState transition to "ended" as expected.
   4552                 LOG("Allocating clone device %p failed. Stopping.",
   4553                     device.get());
   4554                 listener->Stop();
   4555                 return;
   4556               }
   4557               listener->mDeviceState->mAllocated = true;
   4558               if (listener->mDeviceState->mStopped) {
   4559                 MediaManager::Dispatch(NS_NewRunnableFunction(
   4560                     "DeviceListener::Clone::Stop",
   4561                     [device = listener->mDeviceState->mDevice]() {
   4562                       device->Stop();
   4563                       device->Deallocate();
   4564                     }));
   4565               }
   4566             });
   4567 
   4568  return listener.forget();
   4569 }
   4570 
   4571 void DeviceListener::Stop() {
   4572  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4573 
   4574  if (mStopped) {
   4575    return;
   4576  }
   4577  mStopped = true;
   4578 
   4579  LOG("DeviceListener %p stopping", this);
   4580 
   4581  if (mDeviceState) {
   4582    mDeviceState->mDisableTimer->Cancel();
   4583 
   4584    if (mDeviceState->mStopped) {
   4585      // device already stopped.
   4586      return;
   4587    }
   4588    mDeviceState->mStopped = true;
   4589 
   4590    mDeviceState->mTrackSource->Stop();
   4591 
   4592    if (mDeviceState->mAllocated) {
   4593      MediaManager::Dispatch(NewTaskFrom([device = mDeviceState->mDevice]() {
   4594        device->Stop();
   4595        device->Deallocate();
   4596      }));
   4597    }
   4598 
   4599    mWindowListener->ChromeAffectingStateChanged();
   4600  }
   4601 
   4602  mCaptureEndedListener.DisconnectIfExists();
   4603 
   4604  // Keep a strong ref to the removed window listener.
   4605  RefPtr<GetUserMediaWindowListener> windowListener = mWindowListener;
   4606  mWindowListener = nullptr;
   4607  windowListener->Remove(this);
   4608 }
   4609 
   4610 void DeviceListener::GetSettings(MediaTrackSettings& aOutSettings) const {
   4611  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4612  LocalMediaDevice* device = GetDevice();
   4613  device->GetSettings(aOutSettings);
   4614 
   4615  MediaSourceEnum mediaSource = device->GetMediaSource();
   4616  if (mediaSource == MediaSourceEnum::Camera ||
   4617      mediaSource == MediaSourceEnum::Microphone) {
   4618    aOutSettings.mDeviceId.Construct(device->mID);
   4619    aOutSettings.mGroupId.Construct(device->mGroupID);
   4620  }
   4621 }
   4622 
   4623 void DeviceListener::GetCapabilities(
   4624    MediaTrackCapabilities& aOutCapabilities) const {
   4625  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4626  LocalMediaDevice* device = GetDevice();
   4627  device->GetCapabilities(aOutCapabilities);
   4628 
   4629  MediaSourceEnum mediaSource = device->GetMediaSource();
   4630  if (mediaSource == MediaSourceEnum::Camera ||
   4631      mediaSource == MediaSourceEnum::Microphone) {
   4632    aOutCapabilities.mDeviceId.Construct(device->mID);
   4633    aOutCapabilities.mGroupId.Construct(device->mGroupID);
   4634  }
   4635 }
   4636 
   4637 auto DeviceListener::UpdateDevice(bool aOn) -> RefPtr<DeviceOperationPromise> {
   4638  MOZ_ASSERT(NS_IsMainThread());
   4639  RefPtr<DeviceListener> self = this;
   4640  DeviceState& state = *mDeviceState;
   4641  return MediaManager::Dispatch<DeviceOperationPromise>(
   4642             __func__,
   4643             [self, device = state.mDevice,
   4644              aOn](MozPromiseHolder<DeviceOperationPromise>& h) {
   4645               LOG("Turning %s device (%s)", aOn ? "on" : "off",
   4646                   NS_ConvertUTF16toUTF8(device->mName).get());
   4647               h.Resolve(aOn ? device->Start() : device->Stop(), __func__);
   4648             })
   4649      ->Then(
   4650          GetMainThreadSerialEventTarget(), __func__,
   4651          [self, this, &state, aOn](nsresult aResult) {
   4652            if (state.mStopped) {
   4653              // Device was stopped on main thread during the operation. Done.
   4654              return DeviceOperationPromise::CreateAndResolve(aResult,
   4655                                                              __func__);
   4656            }
   4657            LOG("DeviceListener %p turning %s %s input device %s", this,
   4658                aOn ? "on" : "off",
   4659                dom::GetEnumString(GetDevice()->Kind()).get(),
   4660                NS_SUCCEEDED(aResult) ? "succeeded" : "failed");
   4661 
   4662            if (NS_FAILED(aResult) && aResult != NS_ERROR_ABORT) {
   4663              // This path handles errors from starting or stopping the
   4664              // device. NS_ERROR_ABORT are for cases where *we* aborted. They
   4665              // need graceful handling.
   4666              if (aOn) {
   4667                // Starting the device failed. Stopping the track here will
   4668                // make the MediaStreamTrack end after a pass through the
   4669                // MediaTrackGraph.
   4670                Stop();
   4671              } else {
   4672                // Stopping the device failed. This is odd, but not fatal.
   4673                MOZ_ASSERT_UNREACHABLE("The device should be stoppable");
   4674              }
   4675            }
   4676            return DeviceOperationPromise::CreateAndResolve(aResult, __func__);
   4677          },
   4678          []() {
   4679            MOZ_ASSERT_UNREACHABLE("Unexpected and unhandled reject");
   4680            return DeviceOperationPromise::CreateAndReject(false, __func__);
   4681          });
   4682 }
   4683 
   4684 void DeviceListener::SetDeviceEnabled(bool aEnable) {
   4685  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4686  MOZ_ASSERT(Activated(), "No device to set enabled state for");
   4687 
   4688  DeviceState& state = *mDeviceState;
   4689 
   4690  LOG("DeviceListener %p %s %s device", this,
   4691      aEnable ? "enabling" : "disabling",
   4692      dom::GetEnumString(GetDevice()->Kind()).get());
   4693 
   4694  state.mTrackEnabled = aEnable;
   4695 
   4696  if (state.mStopped) {
   4697    // Device terminally stopped. Updating device state is pointless.
   4698    return;
   4699  }
   4700 
   4701  if (state.mOperationInProgress) {
   4702    // If a timer is in progress, it needs to be canceled now so the next
   4703    // DisableTrack() gets a fresh start. Canceling will trigger another
   4704    // operation.
   4705    state.mDisableTimer->Cancel();
   4706    return;
   4707  }
   4708 
   4709  if (state.mDeviceEnabled == aEnable) {
   4710    // Device is already in the desired state.
   4711    return;
   4712  }
   4713 
   4714  // All paths from here on must end in setting
   4715  // `state.mOperationInProgress` to false.
   4716  state.mOperationInProgress = true;
   4717 
   4718  RefPtr<MediaTimerPromise> timerPromise;
   4719  if (aEnable) {
   4720    timerPromise = MediaTimerPromise::CreateAndResolve(true, __func__);
   4721    state.mTrackEnabledTime = TimeStamp::Now();
   4722  } else {
   4723    const TimeDuration maxDelay =
   4724        TimeDuration::FromMilliseconds(Preferences::GetUint(
   4725            GetDevice()->Kind() == MediaDeviceKind::Audioinput
   4726                ? "media.getusermedia.microphone.off_while_disabled.delay_ms"
   4727                : "media.getusermedia.camera.off_while_disabled.delay_ms",
   4728            3000));
   4729    const TimeDuration durationEnabled =
   4730        TimeStamp::Now() - state.mTrackEnabledTime;
   4731    const TimeDuration delay = TimeDuration::Max(
   4732        TimeDuration::FromMilliseconds(0), maxDelay - durationEnabled);
   4733    timerPromise = state.mDisableTimer->WaitFor(delay, __func__);
   4734  }
   4735 
   4736  RefPtr<DeviceListener> self = this;
   4737  timerPromise
   4738      ->Then(
   4739          GetMainThreadSerialEventTarget(), __func__,
   4740          [self, this, &state, aEnable]() mutable {
   4741            MOZ_ASSERT(state.mDeviceEnabled != aEnable,
   4742                       "Device operation hasn't started");
   4743            MOZ_ASSERT(state.mOperationInProgress,
   4744                       "It's our responsibility to reset the inProgress state");
   4745 
   4746            LOG("DeviceListener %p %s %s device - starting device operation",
   4747                this, aEnable ? "enabling" : "disabling",
   4748                dom::GetEnumString(GetDevice()->Kind()).get());
   4749 
   4750            if (state.mStopped) {
   4751              // Source was stopped between timer resolving and this runnable.
   4752              return DeviceOperationPromise::CreateAndResolve(NS_ERROR_ABORT,
   4753                                                              __func__);
   4754            }
   4755 
   4756            state.mDeviceEnabled = aEnable;
   4757 
   4758            if (mWindowListener) {
   4759              mWindowListener->ChromeAffectingStateChanged();
   4760            }
   4761            if (!state.mOffWhileDisabled || state.mDeviceMuted) {
   4762              // If the feature to turn a device off while disabled is itself
   4763              // disabled, or the device is currently user agent muted, then
   4764              // we shortcut the device operation and tell the
   4765              // ux-updating code that everything went fine.
   4766              return DeviceOperationPromise::CreateAndResolve(NS_OK, __func__);
   4767            }
   4768            return UpdateDevice(aEnable);
   4769          },
   4770          []() {
   4771            // Timer was canceled by us. We signal this with NS_ERROR_ABORT.
   4772            return DeviceOperationPromise::CreateAndResolve(NS_ERROR_ABORT,
   4773                                                            __func__);
   4774          })
   4775      ->Then(
   4776          GetMainThreadSerialEventTarget(), __func__,
   4777          [self, this, &state, aEnable](nsresult aResult) mutable {
   4778            MOZ_ASSERT_IF(aResult != NS_ERROR_ABORT,
   4779                          state.mDeviceEnabled == aEnable);
   4780            MOZ_ASSERT(state.mOperationInProgress);
   4781            state.mOperationInProgress = false;
   4782 
   4783            if (state.mStopped) {
   4784              // Device was stopped on main thread during the operation.
   4785              // Nothing to do.
   4786              return;
   4787            }
   4788 
   4789            if (NS_FAILED(aResult) && aResult != NS_ERROR_ABORT && !aEnable) {
   4790              // To keep our internal state sane in this case, we disallow
   4791              // future stops due to disable.
   4792              state.mOffWhileDisabled = false;
   4793              return;
   4794            }
   4795 
   4796            // This path is for a device operation aResult that was success or
   4797            // NS_ERROR_ABORT (*we* canceled the operation).
   4798            // At this point we have to follow up on the intended state, i.e.,
   4799            // update the device state if the track state changed in the
   4800            // meantime.
   4801 
   4802            if (state.mTrackEnabled != state.mDeviceEnabled) {
   4803              // Track state changed during this operation. We'll start over.
   4804              SetDeviceEnabled(state.mTrackEnabled);
   4805            }
   4806          },
   4807          []() { MOZ_ASSERT_UNREACHABLE("Unexpected and unhandled reject"); });
   4808 }
   4809 
   4810 void DeviceListener::SetDeviceMuted(bool aMute) {
   4811  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4812  MOZ_ASSERT(Activated(), "No device to set muted state for");
   4813 
   4814  DeviceState& state = *mDeviceState;
   4815 
   4816  LOG("DeviceListener %p %s %s device", this, aMute ? "muting" : "unmuting",
   4817      dom::GetEnumString(GetDevice()->Kind()).get());
   4818 
   4819  if (state.mStopped) {
   4820    // Device terminally stopped. Updating device state is pointless.
   4821    return;
   4822  }
   4823 
   4824  if (state.mDeviceMuted == aMute) {
   4825    // Device is already in the desired state.
   4826    return;
   4827  }
   4828 
   4829  LOG("DeviceListener %p %s %s device - starting device operation", this,
   4830      aMute ? "muting" : "unmuting",
   4831      dom::GetEnumString(GetDevice()->Kind()).get());
   4832 
   4833  state.mDeviceMuted = aMute;
   4834 
   4835  if (mWindowListener) {
   4836    mWindowListener->ChromeAffectingStateChanged();
   4837  }
   4838  // Update trackSource to fire mute/unmute events on all its tracks
   4839  if (aMute) {
   4840    state.mTrackSource->Mute();
   4841  } else {
   4842    state.mTrackSource->Unmute();
   4843  }
   4844  if (!state.mOffWhileDisabled || !state.mDeviceEnabled) {
   4845    // If the pref to turn the underlying device is itself off, or the device
   4846    // is already off, it's unecessary to do anything else.
   4847    return;
   4848  }
   4849  UpdateDevice(!aMute);
   4850 }
   4851 
   4852 void DeviceListener::MuteOrUnmuteCamera(bool aMute) {
   4853  MOZ_ASSERT(NS_IsMainThread());
   4854 
   4855  if (mStopped) {
   4856    return;
   4857  }
   4858 
   4859  MOZ_RELEASE_ASSERT(mWindowListener);
   4860  LOG("DeviceListener %p MuteOrUnmuteCamera: %s", this,
   4861      aMute ? "mute" : "unmute");
   4862 
   4863  if (GetDevice() &&
   4864      (GetDevice()->GetMediaSource() == MediaSourceEnum::Camera)) {
   4865    SetDeviceMuted(aMute);
   4866  }
   4867 }
   4868 
   4869 void DeviceListener::MuteOrUnmuteMicrophone(bool aMute) {
   4870  MOZ_ASSERT(NS_IsMainThread());
   4871 
   4872  if (mStopped) {
   4873    return;
   4874  }
   4875 
   4876  MOZ_RELEASE_ASSERT(mWindowListener);
   4877  LOG("DeviceListener %p MuteOrUnmuteMicrophone: %s", this,
   4878      aMute ? "mute" : "unmute");
   4879 
   4880  if (GetDevice() &&
   4881      (GetDevice()->GetMediaSource() == MediaSourceEnum::Microphone)) {
   4882    SetDeviceMuted(aMute);
   4883  }
   4884 }
   4885 
   4886 bool DeviceListener::CapturingVideo() const {
   4887  MOZ_ASSERT(NS_IsMainThread());
   4888  return Activated() && mDeviceState && !mDeviceState->mStopped &&
   4889         MediaEngineSource::IsVideo(GetDevice()->GetMediaSource()) &&
   4890         (!GetDevice()->IsFake() ||
   4891          Preferences::GetBool("media.navigator.permission.fake"));
   4892 }
   4893 
   4894 bool DeviceListener::CapturingAudio() const {
   4895  MOZ_ASSERT(NS_IsMainThread());
   4896  return Activated() && mDeviceState && !mDeviceState->mStopped &&
   4897         MediaEngineSource::IsAudio(GetDevice()->GetMediaSource()) &&
   4898         (!GetDevice()->IsFake() ||
   4899          Preferences::GetBool("media.navigator.permission.fake"));
   4900 }
   4901 
   4902 CaptureState DeviceListener::CapturingSource(MediaSourceEnum aSource) const {
   4903  MOZ_ASSERT(NS_IsMainThread());
   4904  if (GetDevice()->GetMediaSource() != aSource) {
   4905    // This DeviceListener doesn't capture a matching source
   4906    return CaptureState::Off;
   4907  }
   4908 
   4909  if (mDeviceState->mStopped) {
   4910    // The source is a match but has been permanently stopped
   4911    return CaptureState::Off;
   4912  }
   4913 
   4914  if ((aSource == MediaSourceEnum::Camera ||
   4915       aSource == MediaSourceEnum::Microphone) &&
   4916      GetDevice()->IsFake() &&
   4917      !Preferences::GetBool("media.navigator.permission.fake")) {
   4918    // Fake Camera and Microphone only count if there is no fake permission
   4919    return CaptureState::Off;
   4920  }
   4921 
   4922  // Source is a match and is active and unmuted
   4923 
   4924  if (mDeviceState->mDeviceEnabled && !mDeviceState->mDeviceMuted) {
   4925    return CaptureState::Enabled;
   4926  }
   4927 
   4928  return CaptureState::Disabled;
   4929 }
   4930 
   4931 RefPtr<DeviceListener::DeviceListenerPromise> DeviceListener::ApplyConstraints(
   4932    const MediaTrackConstraints& aConstraints, CallerType aCallerType) {
   4933  MOZ_ASSERT(NS_IsMainThread());
   4934 
   4935  if (mStopped || mDeviceState->mStopped) {
   4936    LOG("DeviceListener %p %s device applyConstraints, but device is stopped",
   4937        this, dom::GetEnumString(GetDevice()->Kind()).get());
   4938    return DeviceListenerPromise::CreateAndResolve(false, __func__);
   4939  }
   4940 
   4941  MediaManager* mgr = MediaManager::GetIfExists();
   4942  if (!mgr) {
   4943    return DeviceListenerPromise::CreateAndResolve(false, __func__);
   4944  }
   4945 
   4946  return InvokeAsync(
   4947      mgr->mMediaThread, __func__,
   4948      [device = mDeviceState->mDevice, aConstraints, prefs = mgr->mPrefs,
   4949       aCallerType]() mutable -> RefPtr<DeviceListenerPromise> {
   4950        MOZ_ASSERT(MediaManager::IsInMediaThread());
   4951        MediaManager* mgr = MediaManager::GetIfExists();
   4952        MOZ_RELEASE_ASSERT(mgr);  // Must exist while media thread is alive
   4953        const char* badConstraint = nullptr;
   4954        nsresult rv =
   4955            device->Reconfigure(aConstraints, mgr->mPrefs, &badConstraint);
   4956        if (NS_FAILED(rv)) {
   4957          if (rv == NS_ERROR_INVALID_ARG) {
   4958            // Reconfigure failed due to constraints
   4959            if (!badConstraint) {
   4960              nsTArray<RefPtr<LocalMediaDevice>> devices;
   4961              devices.AppendElement(device);
   4962              badConstraint = MediaConstraintsHelper::SelectSettings(
   4963                  NormalizedConstraints(aConstraints), prefs, devices,
   4964                  aCallerType);
   4965            }
   4966          } else {
   4967            // Unexpected. ApplyConstraints* cannot fail with any other error.
   4968            badConstraint = "";
   4969            LOG("ApplyConstraints-Task: Unexpected fail %" PRIx32,
   4970                static_cast<uint32_t>(rv));
   4971          }
   4972 
   4973          return DeviceListenerPromise::CreateAndReject(
   4974              MakeRefPtr<MediaMgrError>(
   4975                  MediaMgrError::Name::OverconstrainedError, "",
   4976                  NS_ConvertASCIItoUTF16(badConstraint)),
   4977              __func__);
   4978        }
   4979        // Reconfigure was successful
   4980        return DeviceListenerPromise::CreateAndResolve(false, __func__);
   4981      });
   4982 }
   4983 
   4984 PrincipalHandle DeviceListener::GetPrincipalHandle() const {
   4985  return mPrincipalHandle;
   4986 }
   4987 
   4988 void GetUserMediaWindowListener::StopSharing() {
   4989  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   4990 
   4991  for (auto& l : mActiveListeners.Clone()) {
   4992    MediaSourceEnum source = l->GetDevice()->GetMediaSource();
   4993    if (source == MediaSourceEnum::Screen ||
   4994        source == MediaSourceEnum::Window ||
   4995        source == MediaSourceEnum::AudioCapture ||
   4996        source == MediaSourceEnum::Browser) {
   4997      l->Stop();
   4998    }
   4999  }
   5000 }
   5001 
   5002 void GetUserMediaWindowListener::StopRawID(const nsString& removedDeviceID) {
   5003  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   5004 
   5005  for (auto& l : mActiveListeners.Clone()) {
   5006    if (removedDeviceID.Equals(l->GetDevice()->RawID())) {
   5007      l->Stop();
   5008    }
   5009  }
   5010 }
   5011 
   5012 void GetUserMediaWindowListener::MuteOrUnmuteCameras(bool aMute) {
   5013  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   5014 
   5015  if (mCamerasAreMuted == aMute) {
   5016    return;
   5017  }
   5018  mCamerasAreMuted = aMute;
   5019 
   5020  for (auto& l : mActiveListeners.Clone()) {
   5021    if (l->GetDevice()->Kind() == MediaDeviceKind::Videoinput) {
   5022      l->MuteOrUnmuteCamera(aMute);
   5023    }
   5024  }
   5025 }
   5026 
   5027 void GetUserMediaWindowListener::MuteOrUnmuteMicrophones(bool aMute) {
   5028  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   5029 
   5030  if (mMicrophonesAreMuted == aMute) {
   5031    return;
   5032  }
   5033  mMicrophonesAreMuted = aMute;
   5034 
   5035  for (auto& l : mActiveListeners.Clone()) {
   5036    if (l->GetDevice()->Kind() == MediaDeviceKind::Audioinput) {
   5037      l->MuteOrUnmuteMicrophone(aMute);
   5038    }
   5039  }
   5040 }
   5041 
   5042 void GetUserMediaWindowListener::ChromeAffectingStateChanged() {
   5043  MOZ_ASSERT(NS_IsMainThread());
   5044 
   5045  // We wait until stable state before notifying chrome so chrome only does
   5046  // one update if more updates happen in this event loop.
   5047 
   5048  if (mChromeNotificationTaskPosted) {
   5049    return;
   5050  }
   5051 
   5052  nsCOMPtr<nsIRunnable> runnable =
   5053      NewRunnableMethod("GetUserMediaWindowListener::NotifyChrome", this,
   5054                        &GetUserMediaWindowListener::NotifyChrome);
   5055  nsContentUtils::RunInStableState(runnable.forget());
   5056  mChromeNotificationTaskPosted = true;
   5057 }
   5058 
   5059 void GetUserMediaWindowListener::NotifyChrome() {
   5060  MOZ_ASSERT(mChromeNotificationTaskPosted);
   5061  mChromeNotificationTaskPosted = false;
   5062 
   5063  NS_DispatchToMainThread(NS_NewRunnableFunction(
   5064      "MediaManager::NotifyChrome", [windowID = mWindowID]() {
   5065        auto* window = nsGlobalWindowInner::GetInnerWindowWithId(windowID);
   5066        if (!window) {
   5067          MOZ_ASSERT_UNREACHABLE("Should have window");
   5068          return;
   5069        }
   5070 
   5071        nsresult rv = MediaManager::NotifyRecordingStatusChange(window);
   5072        if (NS_FAILED(rv)) {
   5073          MOZ_ASSERT_UNREACHABLE("Should be able to notify chrome");
   5074          return;
   5075        }
   5076      }));
   5077 }
   5078 
   5079 #undef LOG
   5080 
   5081 }  // namespace mozilla