tor-browser

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

MediaStreamTrack.cpp (20369B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "MediaStreamTrack.h"
      7 
      8 #include "DOMMediaStream.h"
      9 #include "MediaSegment.h"
     10 #include "MediaStreamError.h"
     11 #include "MediaTrackGraphImpl.h"
     12 #include "MediaTrackListener.h"
     13 #include "mozilla/BasePrincipal.h"
     14 #include "mozilla/dom/Promise.h"
     15 #include "nsContentUtils.h"
     16 #include "nsGlobalWindowInner.h"
     17 #include "nsIUUIDGenerator.h"
     18 #include "nsServiceManagerUtils.h"
     19 #include "systemservices/MediaUtils.h"
     20 
     21 mozilla::LazyLogModule gMediaStreamTrackLog("MediaStreamTrack");
     22 #define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg)
     23 
     24 using namespace mozilla::media;
     25 
     26 namespace mozilla::dom {
     27 
     28 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSource)
     29 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSource)
     30 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSource)
     31  NS_INTERFACE_MAP_ENTRY(nsISupports)
     32 NS_INTERFACE_MAP_END
     33 
     34 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)
     35 
     36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamTrackSource)
     37  tmp->Destroy();
     38  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
     39 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     40 
     41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaStreamTrackSource)
     42  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
     43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     44 
     45 auto MediaStreamTrackSource::Clone() -> CloneResult { return {}; }
     46 
     47 auto MediaStreamTrackSource::ApplyConstraints(
     48    const dom::MediaTrackConstraints& aConstraints, CallerType aCallerType)
     49    -> RefPtr<ApplyConstraintsPromise> {
     50  return ApplyConstraintsPromise::CreateAndReject(
     51      MakeRefPtr<MediaMgrError>(MediaMgrError::Name::OverconstrainedError, ""),
     52      __func__);
     53 }
     54 
     55 /**
     56 * MTGListener monitors state changes of the media flowing through the
     57 * MediaTrackGraph.
     58 *
     59 *
     60 * For changes to PrincipalHandle the following applies:
     61 *
     62 * When the main thread principal for a MediaStreamTrack changes, its principal
     63 * will be set to the combination of the previous principal and the new one.
     64 *
     65 * As a PrincipalHandle change later happens on the MediaTrackGraph thread, we
     66 * will be notified. If the latest principal on main thread matches the
     67 * PrincipalHandle we just saw on MTG thread, we will set the track's principal
     68 * to the new one.
     69 *
     70 * We know at this point that the old principal has been flushed out and data
     71 * under it cannot leak to consumers.
     72 *
     73 * In case of multiple changes to the main thread state, the track's principal
     74 * will be a combination of its old principal and all the new ones until the
     75 * latest main thread principal matches the PrincipalHandle on the MTG thread.
     76 */
     77 class MediaStreamTrack::MTGListener : public MediaTrackListener {
     78 public:
     79  explicit MTGListener(MediaStreamTrack* aTrack) : mTrack(aTrack) {}
     80 
     81  void DoNotifyPrincipalHandleChanged(
     82      const PrincipalHandle& aNewPrincipalHandle) {
     83    MOZ_ASSERT(NS_IsMainThread());
     84 
     85    if (!mTrack) {
     86      return;
     87    }
     88 
     89    mTrack->NotifyPrincipalHandleChanged(aNewPrincipalHandle);
     90  }
     91 
     92  void NotifyPrincipalHandleChanged(
     93      MediaTrackGraph* aGraph,
     94      const PrincipalHandle& aNewPrincipalHandle) override {
     95    aGraph->DispatchToMainThreadStableState(
     96        NewRunnableMethod<StoreCopyPassByConstLRef<PrincipalHandle>>(
     97            "dom::MediaStreamTrack::MTGListener::"
     98            "DoNotifyPrincipalHandleChanged",
     99            this, &MTGListener::DoNotifyPrincipalHandleChanged,
    100            aNewPrincipalHandle));
    101  }
    102 
    103  void NotifyRemoved(MediaTrackGraph* aGraph) override {
    104    // `mTrack` is a WeakPtr and must be destroyed on main thread.
    105    // We dispatch ourselves to main thread here in case the MediaTrackGraph
    106    // is holding the last reference to us.
    107    aGraph->DispatchToMainThreadStableState(
    108        NS_NewRunnableFunction("MediaStreamTrack::MTGListener::mTrackReleaser",
    109                               [self = RefPtr<MTGListener>(this)]() {}));
    110  }
    111 
    112  void DoNotifyEnded() {
    113    MOZ_ASSERT(NS_IsMainThread());
    114 
    115    if (!mTrack) {
    116      return;
    117    }
    118 
    119    if (!mTrack->GetParentObject()) {
    120      return;
    121    }
    122 
    123    AbstractThread::MainThread()->Dispatch(
    124        NewRunnableMethod("MediaStreamTrack::OverrideEnded", mTrack.get(),
    125                          &MediaStreamTrack::OverrideEnded));
    126  }
    127 
    128  void NotifyEnded(MediaTrackGraph* aGraph) override {
    129    aGraph->DispatchToMainThreadStableState(
    130        NewRunnableMethod("MediaStreamTrack::MTGListener::DoNotifyEnded", this,
    131                          &MTGListener::DoNotifyEnded));
    132  }
    133 
    134 protected:
    135  // Main thread only.
    136  WeakPtr<MediaStreamTrack> mTrack;
    137 };
    138 
    139 class MediaStreamTrack::TrackSink : public MediaStreamTrackSource::Sink {
    140 public:
    141  explicit TrackSink(MediaStreamTrack* aTrack) : mTrack(aTrack) {}
    142 
    143  /**
    144   * Keep the track source alive. This track and any clones are controlling the
    145   * lifetime of the source by being registered as its sinks.
    146   */
    147  bool KeepsSourceAlive() const override { return true; }
    148 
    149  bool Enabled() const override {
    150    if (!mTrack) {
    151      return false;
    152    }
    153    return mTrack->Enabled();
    154  }
    155 
    156  void PrincipalChanged() override {
    157    if (mTrack) {
    158      mTrack->PrincipalChanged();
    159    }
    160  }
    161 
    162  void MutedChanged(bool aNewState) override {
    163    if (mTrack) {
    164      mTrack->MutedChanged(aNewState);
    165    }
    166  }
    167 
    168  void ConstraintsChanged(const MediaTrackConstraints& aConstraints) override {
    169    if (mTrack) {
    170      mTrack->ConstraintsChanged(aConstraints);
    171    }
    172  }
    173 
    174  void OverrideEnded() override {
    175    if (mTrack) {
    176      mTrack->OverrideEnded();
    177    }
    178  }
    179 
    180 private:
    181  WeakPtr<MediaStreamTrack> mTrack;
    182 };
    183 
    184 MediaStreamTrack::MediaStreamTrack(nsPIDOMWindowInner* aWindow,
    185                                   mozilla::MediaTrack* aInputTrack,
    186                                   MediaStreamTrackSource* aSource,
    187                                   MediaStreamTrackState aReadyState,
    188                                   bool aMuted,
    189                                   const MediaTrackConstraints& aConstraints)
    190    : mWindow(aWindow),
    191      mInputTrack(aInputTrack),
    192      mSource(aSource),
    193      mSink(MakeUnique<TrackSink>(this)),
    194      mPrincipal(aSource->GetPrincipal()),
    195      mReadyState(aReadyState),
    196      mEnabled(true),
    197      mMuted(aMuted),
    198      mConstraints(aConstraints) {
    199  if (!Ended()) {
    200    GetSource().RegisterSink(mSink.get());
    201 
    202    // Even if the input track is destroyed we need mTrack so that methods
    203    // like AddListener still work. Keeping the number of paths to a minimum
    204    // also helps prevent bugs elsewhere. We'll be ended through the
    205    // MediaStreamTrackSource soon enough.
    206    auto graph = mInputTrack->IsDestroyed()
    207                     ? MediaTrackGraph::GetInstanceIfExists(
    208                           mWindow, mInputTrack->mSampleRate,
    209                           MediaTrackGraph::DEFAULT_OUTPUT_DEVICE)
    210                     : mInputTrack->Graph();
    211    MOZ_DIAGNOSTIC_ASSERT(graph,
    212                          "A destroyed input track is only expected when "
    213                          "cloning, but since we're live there must be another "
    214                          "live track that is keeping the graph alive");
    215 
    216    mTrack = graph->CreateForwardedInputTrack(mInputTrack->mType);
    217    mPort = mTrack->AllocateInputPort(mInputTrack);
    218    mMTGListener = new MTGListener(this);
    219    AddListener(mMTGListener);
    220  }
    221 
    222  nsresult rv;
    223  nsCOMPtr<nsIUUIDGenerator> uuidgen =
    224      do_GetService("@mozilla.org/uuid-generator;1", &rv);
    225 
    226  nsID uuid;
    227  memset(&uuid, 0, sizeof(uuid));
    228  if (uuidgen) {
    229    uuidgen->GenerateUUIDInPlace(&uuid);
    230  }
    231 
    232  char chars[NSID_LENGTH];
    233  uuid.ToProvidedString(chars);
    234  mID = NS_ConvertASCIItoUTF16(chars);
    235 }
    236 
    237 MediaStreamTrack::~MediaStreamTrack() { Destroy(); }
    238 
    239 void MediaStreamTrack::Destroy() {
    240  SetReadyState(MediaStreamTrackState::Ended);
    241  // Remove all listeners -- avoid iterating over the list we're removing from
    242  for (const auto& listener : mTrackListeners.Clone()) {
    243    RemoveListener(listener);
    244  }
    245  // Do the same as above for direct listeners
    246  for (const auto& listener : mDirectTrackListeners.Clone()) {
    247    RemoveDirectListener(listener);
    248  }
    249 }
    250 
    251 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack)
    252 
    253 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack,
    254                                                DOMEventTargetHelper)
    255  tmp->Destroy();
    256  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
    257  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
    258  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
    259  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPrincipal)
    260  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
    261 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    262 
    263 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamTrack,
    264                                                  DOMEventTargetHelper)
    265  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
    266  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
    267  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
    268  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPrincipal)
    269 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    270 
    271 NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
    272 NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
    273 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrack)
    274 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    275 
    276 JSObject* MediaStreamTrack::WrapObject(JSContext* aCx,
    277                                       JS::Handle<JSObject*> aGivenProto) {
    278  return MediaStreamTrack_Binding::Wrap(aCx, this, aGivenProto);
    279 }
    280 
    281 void MediaStreamTrack::GetId(nsAString& aID) const { aID = mID; }
    282 
    283 void MediaStreamTrack::SetEnabled(bool aEnabled) {
    284  LOG(LogLevel::Info,
    285      ("MediaStreamTrack %p %s", this, aEnabled ? "Enabled" : "Disabled"));
    286 
    287  if (mEnabled == aEnabled) {
    288    return;
    289  }
    290 
    291  mEnabled = aEnabled;
    292 
    293  if (Ended()) {
    294    return;
    295  }
    296 
    297  mTrack->SetDisabledTrackMode(mEnabled ? DisabledTrackMode::ENABLED
    298                                        : DisabledTrackMode::SILENCE_BLACK);
    299  NotifyEnabledChanged();
    300 }
    301 
    302 void MediaStreamTrack::Stop() {
    303  LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this));
    304 
    305  if (Ended()) {
    306    LOG(LogLevel::Warning, ("MediaStreamTrack %p Already ended", this));
    307    return;
    308  }
    309 
    310  SetReadyState(MediaStreamTrackState::Ended);
    311 
    312  NotifyEnded();
    313 }
    314 
    315 void MediaStreamTrack::GetCapabilities(MediaTrackCapabilities& aResult,
    316                                       CallerType aCallerType) {
    317  GetSource().GetCapabilities(aResult);
    318 }
    319 
    320 void MediaStreamTrack::GetConstraints(dom::MediaTrackConstraints& aResult) {
    321  aResult = mConstraints;
    322 }
    323 
    324 void MediaStreamTrack::GetSettings(dom::MediaTrackSettings& aResult,
    325                                   CallerType aCallerType) {
    326  GetSource().GetSettings(aResult);
    327 
    328  // Spoof values when privacy.resistFingerprinting is true.
    329  nsIGlobalObject* global = mWindow ? mWindow->AsGlobal() : nullptr;
    330  if (!nsContentUtils::ShouldResistFingerprinting(
    331          aCallerType, global, RFPTarget::StreamVideoFacingMode)) {
    332    return;
    333  }
    334  if (aResult.mFacingMode.WasPassed()) {
    335    aResult.mFacingMode.Value().AssignASCII(
    336        GetEnumString(VideoFacingModeEnum::User));
    337  }
    338 }
    339 
    340 already_AddRefed<Promise> MediaStreamTrack::ApplyConstraints(
    341    const MediaTrackConstraints& aConstraints, CallerType aCallerType,
    342    ErrorResult& aRv) {
    343  if (MOZ_LOG_TEST(gMediaStreamTrackLog, LogLevel::Info)) {
    344    nsString str;
    345    aConstraints.ToJSON(str);
    346 
    347    LOG(LogLevel::Info, ("MediaStreamTrack %p ApplyConstraints() with "
    348                         "constraints %s",
    349                         this, NS_ConvertUTF16toUTF8(str).get()));
    350  }
    351 
    352  nsIGlobalObject* go = mWindow ? mWindow->AsGlobal() : nullptr;
    353 
    354  RefPtr<Promise> promise = Promise::Create(go, aRv);
    355  if (aRv.Failed()) {
    356    return nullptr;
    357  }
    358 
    359  // Forward constraints to the source.
    360  //
    361  // After GetSource().ApplyConstraints succeeds (after it's been to
    362  // media-thread and back), and no sooner, do we set mConstraints to the newly
    363  // applied values.
    364 
    365  // Keep a reference to this, to make sure it's still here when we get back.
    366  RefPtr<MediaStreamTrack> self(this);
    367  GetSource()
    368      .ApplyConstraints(aConstraints, aCallerType)
    369      ->Then(
    370          GetCurrentSerialEventTarget(), __func__,
    371          [this, self, promise, aConstraints](bool aDummy) {
    372            if (!mWindow || !mWindow->IsCurrentInnerWindow()) {
    373              return;  // Leave Promise pending after navigation by design.
    374            }
    375            promise->MaybeResolve(false);
    376          },
    377          [this, self, promise](const RefPtr<MediaMgrError>& aError) {
    378            if (!mWindow || !mWindow->IsCurrentInnerWindow()) {
    379              return;  // Leave Promise pending after navigation by design.
    380            }
    381            promise->MaybeReject(
    382                MakeRefPtr<MediaStreamError>(mWindow, *aError));
    383          });
    384  return promise.forget();
    385 }
    386 
    387 ProcessedMediaTrack* MediaStreamTrack::GetTrack() const {
    388  MOZ_DIAGNOSTIC_ASSERT(!Ended());
    389  return mTrack;
    390 }
    391 
    392 MediaTrackGraph* MediaStreamTrack::Graph() const {
    393  MOZ_DIAGNOSTIC_ASSERT(!Ended());
    394  return mTrack->Graph();
    395 }
    396 
    397 MediaTrackGraphImpl* MediaStreamTrack::GraphImpl() const {
    398  MOZ_DIAGNOSTIC_ASSERT(!Ended());
    399  return mTrack->GraphImpl();
    400 }
    401 
    402 void MediaStreamTrack::SetPrincipal(nsIPrincipal* aPrincipal) {
    403  if (aPrincipal == mPrincipal) {
    404    return;
    405  }
    406  mPrincipal = aPrincipal;
    407 
    408  LOG(LogLevel::Info,
    409      ("MediaStreamTrack %p principal changed to %p. Now: "
    410       "null=%d, codebase=%d, expanded=%d, system=%d",
    411       this, mPrincipal.get(), mPrincipal->GetIsNullPrincipal(),
    412       mPrincipal->GetIsContentPrincipal(),
    413       mPrincipal->GetIsExpandedPrincipal(), mPrincipal->IsSystemPrincipal()));
    414  for (PrincipalChangeObserver<MediaStreamTrack>* observer :
    415       mPrincipalChangeObservers) {
    416    observer->PrincipalChanged(this);
    417  }
    418 }
    419 
    420 void MediaStreamTrack::PrincipalChanged() {
    421  mPendingPrincipal = GetSource().GetPrincipal();
    422  nsCOMPtr<nsIPrincipal> newPrincipal = mPrincipal;
    423  LOG(LogLevel::Info, ("MediaStreamTrack %p Principal changed on main thread "
    424                       "to %p (pending). Combining with existing principal %p.",
    425                       this, mPendingPrincipal.get(), mPrincipal.get()));
    426  if (nsContentUtils::CombineResourcePrincipals(&newPrincipal,
    427                                                mPendingPrincipal)) {
    428    SetPrincipal(newPrincipal);
    429  }
    430 }
    431 
    432 void MediaStreamTrack::NotifyPrincipalHandleChanged(
    433    const PrincipalHandle& aNewPrincipalHandle) {
    434  PrincipalHandle handle(aNewPrincipalHandle);
    435  LOG(LogLevel::Info, ("MediaStreamTrack %p principalHandle changed on "
    436                       "MediaTrackGraph thread to %p. Current principal: %p, "
    437                       "pending: %p",
    438                       this, GetPrincipalFromHandle(handle), mPrincipal.get(),
    439                       mPendingPrincipal.get()));
    440  if (PrincipalHandleMatches(handle, mPendingPrincipal)) {
    441    SetPrincipal(mPendingPrincipal);
    442    mPendingPrincipal = nullptr;
    443  }
    444 }
    445 
    446 void MediaStreamTrack::MutedChanged(bool aNewState) {
    447  MOZ_ASSERT(NS_IsMainThread());
    448 
    449  /**
    450   * 4.3.1 Life-cycle and Media flow - Media flow
    451   * To set a track's muted state to newState, the User Agent MUST run the
    452   * following steps:
    453   *  1. Let track be the MediaStreamTrack in question.
    454   *  2. Set track's muted attribute to newState.
    455   *  3. If newState is true let eventName be mute, otherwise unmute.
    456   *  4. Fire a simple event named eventName on track.
    457   */
    458 
    459  if (mMuted == aNewState) {
    460    return;
    461  }
    462 
    463  LOG(LogLevel::Info,
    464      ("MediaStreamTrack %p became %s", this, aNewState ? "muted" : "unmuted"));
    465 
    466  mMuted = aNewState;
    467 
    468  if (Ended()) {
    469    return;
    470  }
    471 
    472  nsString eventName = aNewState ? u"mute"_ns : u"unmute"_ns;
    473  DispatchTrustedEvent(eventName);
    474 }
    475 
    476 void MediaStreamTrack::ConstraintsChanged(
    477    const MediaTrackConstraints& aConstraints) {
    478  MOZ_ASSERT(NS_IsMainThread());
    479  mConstraints = aConstraints;
    480 }
    481 
    482 void MediaStreamTrack::NotifyEnded() {
    483  MOZ_ASSERT(mReadyState == MediaStreamTrackState::Ended);
    484 
    485  for (const auto& consumer : mConsumers.Clone()) {
    486    if (consumer) {
    487      consumer->NotifyEnded(this);
    488    } else {
    489      MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
    490      mConsumers.RemoveElement(consumer);
    491    }
    492  }
    493 }
    494 
    495 void MediaStreamTrack::NotifyEnabledChanged() {
    496  GetSource().SinkEnabledStateChanged();
    497 
    498  for (const auto& consumer : mConsumers.Clone()) {
    499    if (consumer) {
    500      consumer->NotifyEnabledChanged(this, Enabled());
    501    } else {
    502      MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
    503      mConsumers.RemoveElement(consumer);
    504    }
    505  }
    506 }
    507 
    508 bool MediaStreamTrack::AddPrincipalChangeObserver(
    509    PrincipalChangeObserver<MediaStreamTrack>* aObserver) {
    510  // XXX(Bug 1631371) Check if this should use a fallible operation as it
    511  // pretended earlier.
    512  mPrincipalChangeObservers.AppendElement(aObserver);
    513  return true;
    514 }
    515 
    516 bool MediaStreamTrack::RemovePrincipalChangeObserver(
    517    PrincipalChangeObserver<MediaStreamTrack>* aObserver) {
    518  return mPrincipalChangeObservers.RemoveElement(aObserver);
    519 }
    520 
    521 void MediaStreamTrack::AddConsumer(MediaStreamTrackConsumer* aConsumer) {
    522  MOZ_ASSERT(!mConsumers.Contains(aConsumer));
    523  mConsumers.AppendElement(aConsumer);
    524 
    525  // Remove destroyed consumers for cleanliness
    526  while (mConsumers.RemoveElement(nullptr)) {
    527    MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
    528  }
    529 }
    530 
    531 void MediaStreamTrack::RemoveConsumer(MediaStreamTrackConsumer* aConsumer) {
    532  mConsumers.RemoveElement(aConsumer);
    533 
    534  // Remove destroyed consumers for cleanliness
    535  while (mConsumers.RemoveElement(nullptr)) {
    536    MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
    537  }
    538 }
    539 
    540 void MediaStreamTrack::SetReadyState(MediaStreamTrackState aState) {
    541  MOZ_ASSERT(!(mReadyState == MediaStreamTrackState::Ended &&
    542               aState == MediaStreamTrackState::Live),
    543             "We don't support overriding the ready state from ended to live");
    544 
    545  if (Ended()) {
    546    return;
    547  }
    548 
    549  if (mReadyState == MediaStreamTrackState::Live &&
    550      aState == MediaStreamTrackState::Ended) {
    551    if (mSource) {
    552      mSource->UnregisterSink(mSink.get());
    553    }
    554    if (mMTGListener) {
    555      RemoveListener(mMTGListener);
    556    }
    557    if (mPort) {
    558      mPort->Destroy();
    559    }
    560    if (mTrack) {
    561      mTrack->Destroy();
    562    }
    563    mPort = nullptr;
    564    mTrack = nullptr;
    565    mMTGListener = nullptr;
    566  }
    567 
    568  mReadyState = aState;
    569 }
    570 
    571 void MediaStreamTrack::OverrideEnded() {
    572  MOZ_ASSERT(NS_IsMainThread());
    573 
    574  if (Ended()) {
    575    return;
    576  }
    577 
    578  LOG(LogLevel::Info, ("MediaStreamTrack %p ended", this));
    579 
    580  SetReadyState(MediaStreamTrackState::Ended);
    581 
    582  NotifyEnded();
    583 
    584  DispatchTrustedEvent(u"ended"_ns);
    585 }
    586 
    587 void MediaStreamTrack::AddListener(MediaTrackListener* aListener) {
    588  LOG(LogLevel::Debug,
    589      ("MediaStreamTrack %p adding listener %p", this, aListener));
    590  mTrackListeners.AppendElement(aListener);
    591 
    592  if (Ended()) {
    593    return;
    594  }
    595  mTrack->AddListener(aListener);
    596 }
    597 
    598 void MediaStreamTrack::RemoveListener(MediaTrackListener* aListener) {
    599  LOG(LogLevel::Debug,
    600      ("MediaStreamTrack %p removing listener %p", this, aListener));
    601  mTrackListeners.RemoveElement(aListener);
    602 
    603  if (Ended()) {
    604    return;
    605  }
    606  mTrack->RemoveListener(aListener);
    607 }
    608 
    609 void MediaStreamTrack::AddDirectListener(DirectMediaTrackListener* aListener) {
    610  LOG(LogLevel::Debug, ("MediaStreamTrack %p (%s) adding direct listener %p to "
    611                        "track %p",
    612                        this, AsAudioStreamTrack() ? "audio" : "video",
    613                        aListener, mTrack.get()));
    614  mDirectTrackListeners.AppendElement(aListener);
    615 
    616  if (Ended()) {
    617    return;
    618  }
    619  mTrack->AddDirectListener(aListener);
    620 }
    621 
    622 void MediaStreamTrack::RemoveDirectListener(
    623    DirectMediaTrackListener* aListener) {
    624  LOG(LogLevel::Debug,
    625      ("MediaStreamTrack %p removing direct listener %p from track %p", this,
    626       aListener, mTrack.get()));
    627  mDirectTrackListeners.RemoveElement(aListener);
    628 
    629  if (Ended()) {
    630    return;
    631  }
    632  mTrack->RemoveDirectListener(aListener);
    633 }
    634 
    635 already_AddRefed<MediaInputPort> MediaStreamTrack::ForwardTrackContentsTo(
    636    ProcessedMediaTrack* aTrack) {
    637  MOZ_ASSERT(NS_IsMainThread());
    638  MOZ_RELEASE_ASSERT(aTrack);
    639  return aTrack->AllocateInputPort(mTrack);
    640 }
    641 
    642 }  // namespace mozilla::dom
    643 
    644 #undef LOG