LCOV - code coverage report
Current view: top level - dom/html - HTMLMediaElement.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 59 3420 1.7 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=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
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifdef XP_WIN
       8             : #include "objbase.h"
       9             : // Some Windows header defines this, so undef it as it conflicts with our
      10             : // function of the same name.
      11             : #undef GetCurrentTime
      12             : #endif
      13             : 
      14             : #include "mozilla/dom/HTMLMediaElement.h"
      15             : #include "AudioChannelService.h"
      16             : #include "AudioStreamTrack.h"
      17             : #include "AutoplayPolicy.h"
      18             : #include "ChannelMediaDecoder.h"
      19             : #include "DOMMediaStream.h"
      20             : #include "DecoderDoctorDiagnostics.h"
      21             : #include "DecoderDoctorLogger.h"
      22             : #include "DecoderTraits.h"
      23             : #include "FrameStatistics.h"
      24             : #include "GMPCrashHelper.h"
      25             : #ifdef MOZ_ANDROID_HLS_SUPPORT
      26             : #include "HLSDecoder.h"
      27             : #endif
      28             : #include "HTMLMediaElement.h"
      29             : #include "ImageContainer.h"
      30             : #include "Layers.h"
      31             : #include "MP4Decoder.h"
      32             : #include "MediaContainerType.h"
      33             : #include "MediaError.h"
      34             : #include "MediaMetadataManager.h"
      35             : #include "MediaResource.h"
      36             : #include "MediaSourceDecoder.h"
      37             : #include "MediaStreamError.h"
      38             : #include "MediaStreamGraph.h"
      39             : #include "MediaStreamListener.h"
      40             : #include "MediaTrackList.h"
      41             : #include "SVGObserverUtils.h"
      42             : #include "TimeRanges.h"
      43             : #include "VideoFrameContainer.h"
      44             : #include "VideoStreamTrack.h"
      45             : #include "base/basictypes.h"
      46             : #include "jsapi.h"
      47             : #include "mozilla/ArrayUtils.h"
      48             : #include "mozilla/AsyncEventDispatcher.h"
      49             : #include "mozilla/EMEUtils.h"
      50             : #include "mozilla/EventDispatcher.h"
      51             : #include "mozilla/EventStateManager.h"
      52             : #include "mozilla/FloatingPoint.h"
      53             : #include "mozilla/MathAlgorithms.h"
      54             : #include "mozilla/NotNull.h"
      55             : #include "mozilla/Preferences.h"
      56             : #include "mozilla/Sprintf.h"
      57             : #include "mozilla/StaticPrefs.h"
      58             : #include "mozilla/Telemetry.h"
      59             : #include "mozilla/dom/AudioTrack.h"
      60             : #include "mozilla/dom/AudioTrackList.h"
      61             : #include "mozilla/dom/BlobURLProtocolHandler.h"
      62             : #include "mozilla/dom/ElementInlines.h"
      63             : #include "mozilla/dom/HTMLAudioElement.h"
      64             : #include "mozilla/dom/HTMLInputElement.h"
      65             : #include "mozilla/dom/HTMLMediaElementBinding.h"
      66             : #include "mozilla/dom/HTMLSourceElement.h"
      67             : #include "mozilla/dom/HTMLVideoElement.h"
      68             : #include "mozilla/dom/MediaEncryptedEvent.h"
      69             : #include "mozilla/dom/MediaErrorBinding.h"
      70             : #include "mozilla/dom/MediaSource.h"
      71             : #include "mozilla/dom/PlayPromise.h"
      72             : #include "mozilla/dom/Promise.h"
      73             : #include "mozilla/dom/TextTrack.h"
      74             : #include "mozilla/dom/VideoPlaybackQuality.h"
      75             : #include "mozilla/dom/VideoTrack.h"
      76             : #include "mozilla/dom/VideoTrackList.h"
      77             : #include "mozilla/dom/WakeLock.h"
      78             : #include "mozilla/dom/power/PowerManagerService.h"
      79             : #include "nsAttrValueInlines.h"
      80             : #include "nsContentPolicyUtils.h"
      81             : #include "nsContentUtils.h"
      82             : #include "nsCycleCollectionParticipant.h"
      83             : #include "nsDisplayList.h"
      84             : #include "nsDocShell.h"
      85             : #include "nsError.h"
      86             : #include "nsGenericHTMLElement.h"
      87             : #include "nsGkAtoms.h"
      88             : #include "nsIAsyncVerifyRedirectCallback.h"
      89             : #include "nsICachingChannel.h"
      90             : #include "nsICategoryManager.h"
      91             : #include "nsIClassOfService.h"
      92             : #include "nsIContentPolicy.h"
      93             : #include "nsIContentSecurityPolicy.h"
      94             : #include "nsIDocShell.h"
      95             : #include "nsIDocument.h"
      96             : #include "nsIFrame.h"
      97             : #include "nsIObserverService.h"
      98             : #include "nsIPermissionManager.h"
      99             : #include "nsIPresShell.h"
     100             : #include "nsIRequest.h"
     101             : #include "nsIScriptError.h"
     102             : #include "nsIScriptSecurityManager.h"
     103             : #include "nsISupportsPrimitives.h"
     104             : #include "nsIThreadInternal.h"
     105             : #include "nsITimer.h"
     106             : #include "nsIXPConnect.h"
     107             : #include "nsJSUtils.h"
     108             : #include "nsLayoutUtils.h"
     109             : #include "nsMediaFragmentURIParser.h"
     110             : #include "nsMimeTypes.h"
     111             : #include "nsNetUtil.h"
     112             : #include "nsNodeInfoManager.h"
     113             : #include "nsPresContext.h"
     114             : #include "nsQueryObject.h"
     115             : #include "nsRange.h"
     116             : #include "nsSize.h"
     117             : #include "nsThreadUtils.h"
     118             : #include "nsURIHashKey.h"
     119             : #include "nsVideoFrame.h"
     120             : #include "xpcpublic.h"
     121             : #include <algorithm>
     122             : #include <cmath>
     123             : #include <limits>
     124             : 
     125             : mozilla::LazyLogModule gMediaElementLog("nsMediaElement");
     126             : static mozilla::LazyLogModule gMediaElementEventsLog("nsMediaElementEvents");
     127             : 
     128             : #define LOG(type, msg) MOZ_LOG(gMediaElementLog, type, msg)
     129             : #define LOG_EVENT(type, msg) MOZ_LOG(gMediaElementEventsLog, type, msg)
     130             : 
     131             : using namespace mozilla::layers;
     132             : using mozilla::net::nsMediaFragmentURIParser;
     133             : using namespace mozilla::dom::HTMLMediaElementBinding;
     134             : 
     135             : namespace mozilla {
     136             : namespace dom {
     137             : 
     138             : // Number of milliseconds between progress events as defined by spec
     139             : static const uint32_t PROGRESS_MS = 350;
     140             : 
     141             : // Number of milliseconds of no data before a stall event is fired as defined by
     142             : // spec
     143             : static const uint32_t STALL_MS = 3000;
     144             : 
     145             : // Used by AudioChannel for suppresssing the volume to this ratio.
     146             : #define FADED_VOLUME_RATIO 0.25
     147             : 
     148             : // These constants are arbitrary
     149             : // Minimum playbackRate for a media
     150             : static const double MIN_PLAYBACKRATE = 1.0 / 16;
     151             : // Maximum playbackRate for a media
     152             : static const double MAX_PLAYBACKRATE = 16.0;
     153             : // These are the limits beyonds which SoundTouch does not perform too well and
     154             : // when speech is hard to understand anyway. Threshold above which audio is
     155             : // muted
     156             : static const double THRESHOLD_HIGH_PLAYBACKRATE_AUDIO = 4.0;
     157             : // Threshold under which audio is muted
     158             : static const double THRESHOLD_LOW_PLAYBACKRATE_AUDIO = 0.5;
     159             : 
     160             : // Media error values.  These need to match the ones in MediaError.webidl.
     161             : static const unsigned short MEDIA_ERR_ABORTED = 1;
     162             : static const unsigned short MEDIA_ERR_NETWORK = 2;
     163             : static const unsigned short MEDIA_ERR_DECODE = 3;
     164             : static const unsigned short MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
     165             : 
     166             : static void
     167           0 : ResolvePromisesWithUndefined(const nsTArray<RefPtr<PlayPromise>>& aPromises)
     168             : {
     169           0 :   for (auto& promise : aPromises) {
     170           0 :     promise->MaybeResolveWithUndefined();
     171             :   }
     172           0 : }
     173             : 
     174             : static void
     175           0 : RejectPromises(const nsTArray<RefPtr<PlayPromise>>& aPromises, nsresult aError)
     176             : {
     177           0 :   for (auto& promise : aPromises) {
     178           0 :     promise->MaybeReject(aError);
     179             :   }
     180           0 : }
     181             : 
     182             : // Under certain conditions there may be no-one holding references to
     183             : // a media element from script, DOM parent, etc, but the element may still
     184             : // fire meaningful events in the future so we can't destroy it yet:
     185             : // 1) If the element is delaying the load event (or would be, if it were
     186             : // in a document), then events up to loadeddata or error could be fired,
     187             : // so we need to stay alive.
     188             : // 2) If the element is not paused and playback has not ended, then
     189             : // we will (or might) play, sending timeupdate and ended events and possibly
     190             : // audio output, so we need to stay alive.
     191             : // 3) if the element is seeking then we will fire seeking events and possibly
     192             : // start playing afterward, so we need to stay alive.
     193             : // 4) If autoplay could start playback in this element (if we got enough data),
     194             : // then we need to stay alive.
     195             : // 5) if the element is currently loading, not suspended, and its source is
     196             : // not a MediaSource, then script might be waiting for progress events or a
     197             : // 'stalled' or 'suspend' event, so we need to stay alive.
     198             : // If we're already suspended then (all other conditions being met),
     199             : // it's OK to just disappear without firing any more events,
     200             : // since we have the freedom to remain suspended indefinitely. Note
     201             : // that we could use this 'suspended' loophole to garbage-collect a suspended
     202             : // element in case 4 even if it had 'autoplay' set, but we choose not to.
     203             : // If someone throws away all references to a loading 'autoplay' element
     204             : // sound should still eventually play.
     205             : // 6) If the source is a MediaSource, most loading events will not fire unless
     206             : // appendBuffer() is called on a SourceBuffer, in which case something is
     207             : // already referencing the SourceBuffer, which keeps the associated media
     208             : // element alive. Further, a MediaSource will never time out the resource
     209             : // fetch, and so should not keep the media element alive if it is
     210             : // unreferenced. A pending 'stalled' event keeps the media element alive.
     211             : //
     212             : // Media elements owned by inactive documents (i.e. documents not contained in
     213             : // any document viewer) should never hold a self-reference because none of the
     214             : // above conditions are allowed: the element will stop loading and playing
     215             : // and never resume loading or playing unless its owner document changes to
     216             : // an active document (which can only happen if there is an external reference
     217             : // to the element).
     218             : // Media elements with no owner doc should be able to hold a self-reference.
     219             : // Something native must have created the element and may expect it to
     220             : // stay alive to play.
     221             : 
     222             : // It's very important that any change in state which could change the value of
     223             : // needSelfReference in AddRemoveSelfReference be followed by a call to
     224             : // AddRemoveSelfReference before this element could die!
     225             : // It's especially important if needSelfReference would change to 'true',
     226             : // since if we neglect to add a self-reference, this element might be
     227             : // garbage collected while there are still event listeners that should
     228             : // receive events. If we neglect to remove the self-reference then the element
     229             : // just lives longer than it needs to.
     230             : 
     231             : class nsMediaEvent : public Runnable
     232             : {
     233             : public:
     234           0 :   explicit nsMediaEvent(const char* aName, HTMLMediaElement* aElement)
     235           0 :     : Runnable(aName)
     236             :     , mElement(aElement)
     237           0 :     , mLoadID(mElement->GetCurrentLoadID())
     238             :   {
     239           0 :   }
     240           0 :   ~nsMediaEvent() {}
     241             : 
     242             :   NS_IMETHOD Run() override = 0;
     243             : 
     244             : protected:
     245           0 :   bool IsCancelled() { return mElement->GetCurrentLoadID() != mLoadID; }
     246             : 
     247             :   RefPtr<HTMLMediaElement> mElement;
     248             :   uint32_t mLoadID;
     249             : };
     250             : 
     251           0 : class HTMLMediaElement::nsAsyncEventRunner : public nsMediaEvent
     252             : {
     253             : private:
     254             :   nsString mName;
     255             : 
     256             : public:
     257           0 :   nsAsyncEventRunner(const nsAString& aName, HTMLMediaElement* aElement)
     258           0 :     : nsMediaEvent("HTMLMediaElement::nsAsyncEventRunner", aElement)
     259           0 :     , mName(aName)
     260             :   {
     261           0 :   }
     262             : 
     263           0 :   NS_IMETHOD Run() override
     264             :   {
     265             :     // Silently cancel if our load has been cancelled.
     266           0 :     if (IsCancelled())
     267             :       return NS_OK;
     268             : 
     269           0 :     return mElement->DispatchEvent(mName);
     270             :   }
     271             : };
     272             : 
     273             : /*
     274             :  * If no error is passed while constructing an instance, the instance will
     275             :  * resolve the passed promises with undefined; otherwise, the instance will
     276             :  * reject the passed promises with the passed error.
     277             :  *
     278             :  * The constructor appends the constructed instance into the passed media
     279             :  * element's mPendingPlayPromisesRunners member and once the the runner is run
     280             :  * (whether fulfilled or canceled), it removes itself from
     281             :  * mPendingPlayPromisesRunners.
     282             :  */
     283           0 : class HTMLMediaElement::nsResolveOrRejectPendingPlayPromisesRunner
     284             :   : public nsMediaEvent
     285             : {
     286             :   nsTArray<RefPtr<PlayPromise>> mPromises;
     287             :   nsresult mError;
     288             : 
     289             : public:
     290           0 :   nsResolveOrRejectPendingPlayPromisesRunner(
     291             :     HTMLMediaElement* aElement,
     292             :     nsTArray<RefPtr<PlayPromise>>&& aPromises,
     293             :     nsresult aError = NS_OK)
     294           0 :     : nsMediaEvent(
     295             :         "HTMLMediaElement::nsResolveOrRejectPendingPlayPromisesRunner",
     296             :         aElement)
     297           0 :     , mPromises(std::move(aPromises))
     298           0 :     , mError(aError)
     299             :   {
     300           0 :     mElement->mPendingPlayPromisesRunners.AppendElement(this);
     301           0 :   }
     302             : 
     303           0 :   void ResolveOrReject()
     304             :   {
     305           0 :     if (NS_SUCCEEDED(mError)) {
     306           0 :       ResolvePromisesWithUndefined(mPromises);
     307             :     } else {
     308           0 :       RejectPromises(mPromises, mError);
     309             :     }
     310           0 :   }
     311             : 
     312           0 :   NS_IMETHOD Run() override
     313             :   {
     314           0 :     if (!IsCancelled()) {
     315           0 :       ResolveOrReject();
     316             :     }
     317             : 
     318           0 :     mElement->mPendingPlayPromisesRunners.RemoveElement(this);
     319           0 :     return NS_OK;
     320             :   }
     321             : };
     322             : 
     323           0 : class HTMLMediaElement::nsNotifyAboutPlayingRunner
     324             :   : public nsResolveOrRejectPendingPlayPromisesRunner
     325             : {
     326             : public:
     327           0 :   nsNotifyAboutPlayingRunner(
     328             :     HTMLMediaElement* aElement,
     329             :     nsTArray<RefPtr<PlayPromise>>&& aPendingPlayPromises)
     330           0 :     : nsResolveOrRejectPendingPlayPromisesRunner(aElement,
     331           0 :                                                  std::move(aPendingPlayPromises))
     332             :   {
     333           0 :   }
     334             : 
     335           0 :   NS_IMETHOD Run() override
     336             :   {
     337           0 :     if (IsCancelled()) {
     338           0 :       mElement->mPendingPlayPromisesRunners.RemoveElement(this);
     339           0 :       return NS_OK;
     340             :     }
     341             : 
     342           0 :     mElement->DispatchEvent(NS_LITERAL_STRING("playing"));
     343           0 :     return nsResolveOrRejectPendingPlayPromisesRunner::Run();
     344             :   }
     345             : };
     346             : 
     347           0 : class nsSourceErrorEventRunner : public nsMediaEvent
     348             : {
     349             : private:
     350             :   nsCOMPtr<nsIContent> mSource;
     351             : 
     352             : public:
     353           0 :   nsSourceErrorEventRunner(HTMLMediaElement* aElement, nsIContent* aSource)
     354           0 :     : nsMediaEvent("dom::nsSourceErrorEventRunner", aElement)
     355           0 :     , mSource(aSource)
     356             :   {
     357           0 :   }
     358             : 
     359           0 :   NS_IMETHOD Run() override
     360             :   {
     361             :     // Silently cancel if our load has been cancelled.
     362           0 :     if (IsCancelled())
     363             :       return NS_OK;
     364           0 :     LOG_EVENT(LogLevel::Debug,
     365             :               ("%p Dispatching simple event source error", mElement.get()));
     366           0 :     return nsContentUtils::DispatchTrustedEvent(
     367           0 :       mElement->OwnerDoc(), mSource, NS_LITERAL_STRING("error"), false, false);
     368             :   }
     369             : };
     370             : 
     371             : /**
     372             :  * This listener observes the first video frame to arrive with a non-empty size,
     373             :  * and calls HTMLMediaElement::UpdateInitialMediaSize() with that size.
     374             :  */
     375           0 : class HTMLMediaElement::StreamSizeListener
     376             :   : public DirectMediaStreamTrackListener
     377             : {
     378             : public:
     379           0 :   explicit StreamSizeListener(HTMLMediaElement* aElement)
     380           0 :     : mElement(aElement)
     381             :     , mMainThreadEventTarget(aElement->MainThreadEventTarget())
     382           0 :     , mInitialSizeFound(false)
     383             :   {
     384           0 :     MOZ_ASSERT(mElement);
     385           0 :     MOZ_ASSERT(mMainThreadEventTarget);
     386           0 :   }
     387             : 
     388           0 :   void Forget() { mElement = nullptr; }
     389             : 
     390           0 :   void ReceivedSize(gfx::IntSize aSize)
     391             :   {
     392           0 :     MOZ_ASSERT(NS_IsMainThread());
     393             : 
     394           0 :     if (!mElement) {
     395           0 :       return;
     396             :     }
     397             : 
     398           0 :     RefPtr<HTMLMediaElement> deathGrip = mElement;
     399           0 :     deathGrip->UpdateInitialMediaSize(aSize);
     400             :   }
     401             : 
     402           0 :   void NotifyRealtimeTrackData(MediaStreamGraph* aGraph,
     403             :                                StreamTime aTrackOffset,
     404             :                                const MediaSegment& aMedia) override
     405             :   {
     406           0 :     if (mInitialSizeFound) {
     407             :       return;
     408             :     }
     409             : 
     410           0 :     if (aMedia.GetType() != MediaSegment::VIDEO) {
     411           0 :       MOZ_ASSERT(false, "Should only lock on to a video track");
     412             :       return;
     413             :     }
     414             : 
     415           0 :     const VideoSegment& video = static_cast<const VideoSegment&>(aMedia);
     416           0 :     for (VideoSegment::ConstChunkIterator c(video); !c.IsEnded(); c.Next()) {
     417           0 :       if (c->mFrame.GetIntrinsicSize() != gfx::IntSize(0, 0)) {
     418           0 :         mInitialSizeFound = true;
     419             :         // This is fine to dispatch straight to main thread (instead of via
     420             :         // ...AfterStreamUpdate()) since it reflects state of the element,
     421             :         // not the stream. Events reflecting stream or track state should be
     422             :         // dispatched so their order is preserved.
     423           0 :         mMainThreadEventTarget->Dispatch(NewRunnableMethod<gfx::IntSize>(
     424             :           "dom::HTMLMediaElement::StreamSizeListener::ReceivedSize",
     425             :           this,
     426             :           &StreamSizeListener::ReceivedSize,
     427           0 :           c->mFrame.GetIntrinsicSize()));
     428           0 :         return;
     429             :       }
     430             :     }
     431             :   }
     432             : 
     433             : private:
     434             :   // These fields may only be accessed on the main thread
     435             :   HTMLMediaElement* mElement;
     436             :   // We hold mElement->MainThreadEventTarget() here because the mElement could
     437             :   // be reset in Forget().
     438             :   nsCOMPtr<nsISerialEventTarget> mMainThreadEventTarget;
     439             : 
     440             :   // These fields may only be accessed on the MSG's appending thread.
     441             :   // (this is a direct listener so we get called by whoever is producing
     442             :   // this track's data)
     443             :   bool mInitialSizeFound;
     444             : };
     445             : 
     446             : /**
     447             :  * There is a reference cycle involving this class: MediaLoadListener
     448             :  * holds a reference to the HTMLMediaElement, which holds a reference
     449             :  * to an nsIChannel, which holds a reference to this listener.
     450             :  * We break the reference cycle in OnStartRequest by clearing mElement.
     451             :  */
     452             : class HTMLMediaElement::MediaLoadListener final
     453             :   : public nsIStreamListener
     454             :   , public nsIChannelEventSink
     455             :   , public nsIInterfaceRequestor
     456             :   , public nsIObserver
     457             :   , public nsIThreadRetargetableStreamListener
     458             : {
     459           0 :   ~MediaLoadListener() {}
     460             : 
     461             :   NS_DECL_THREADSAFE_ISUPPORTS
     462             :   NS_DECL_NSIREQUESTOBSERVER
     463             :   NS_DECL_NSISTREAMLISTENER
     464             :   NS_DECL_NSICHANNELEVENTSINK
     465             :   NS_DECL_NSIOBSERVER
     466             :   NS_DECL_NSIINTERFACEREQUESTOR
     467             :   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     468             : 
     469             : public:
     470           0 :   explicit MediaLoadListener(HTMLMediaElement* aElement)
     471           0 :     : mElement(aElement)
     472           0 :     , mLoadID(aElement->GetCurrentLoadID())
     473             :   {
     474           0 :     MOZ_ASSERT(mElement, "Must pass an element to call back");
     475           0 :   }
     476             : 
     477             : private:
     478             :   RefPtr<HTMLMediaElement> mElement;
     479             :   nsCOMPtr<nsIStreamListener> mNextListener;
     480             :   const uint32_t mLoadID;
     481             : };
     482             : 
     483           0 : NS_IMPL_ISUPPORTS(HTMLMediaElement::MediaLoadListener,
     484             :                   nsIRequestObserver,
     485             :                   nsIStreamListener,
     486             :                   nsIChannelEventSink,
     487             :                   nsIInterfaceRequestor,
     488             :                   nsIObserver,
     489             :                   nsIThreadRetargetableStreamListener)
     490             : 
     491             : NS_IMETHODIMP
     492           0 : HTMLMediaElement::MediaLoadListener::Observe(nsISupports* aSubject,
     493             :                                              const char* aTopic,
     494             :                                              const char16_t* aData)
     495             : {
     496           0 :   nsContentUtils::UnregisterShutdownObserver(this);
     497             : 
     498             :   // Clear mElement to break cycle so we don't leak on shutdown
     499           0 :   mElement = nullptr;
     500           0 :   return NS_OK;
     501             : }
     502             : 
     503             : NS_IMETHODIMP
     504           0 : HTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest,
     505             :                                                     nsISupports* aContext)
     506             : {
     507           0 :   nsContentUtils::UnregisterShutdownObserver(this);
     508             : 
     509           0 :   if (!mElement) {
     510             :     // We've been notified by the shutdown observer, and are shutting down.
     511             :     return NS_BINDING_ABORTED;
     512             :   }
     513             : 
     514             :   // The element is only needed until we've had a chance to call
     515             :   // InitializeDecoderForChannel. So make sure mElement is cleared here.
     516           0 :   RefPtr<HTMLMediaElement> element;
     517           0 :   element.swap(mElement);
     518             : 
     519           0 :   AbstractThread::AutoEnter context(element->AbstractMainThread());
     520             : 
     521           0 :   if (mLoadID != element->GetCurrentLoadID()) {
     522             :     // The channel has been cancelled before we had a chance to create
     523             :     // a decoder. Abort, don't dispatch an "error" event, as the new load
     524             :     // may not be in an error state.
     525             :     return NS_BINDING_ABORTED;
     526             :   }
     527             : 
     528             :   // Don't continue to load if the request failed or has been canceled.
     529             :   nsresult status;
     530           0 :   nsresult rv = aRequest->GetStatus(&status);
     531           0 :   NS_ENSURE_SUCCESS(rv, rv);
     532           0 :   if (NS_FAILED(status)) {
     533           0 :     if (element) {
     534             :       // Handle media not loading error because source was a tracking URL.
     535             :       // We make a note of this media node by including it in a dedicated
     536             :       // array of blocked tracking nodes under its parent document.
     537           0 :       if (status == NS_ERROR_TRACKING_URI) {
     538           0 :         nsIDocument* ownerDoc = element->OwnerDoc();
     539           0 :         if (ownerDoc) {
     540           0 :           ownerDoc->AddBlockedTrackingNode(element);
     541             :         }
     542             :       }
     543           0 :       element->NotifyLoadError(
     544           0 :         nsPrintfCString("%u: %s", uint32_t(status), "Request failed"));
     545             :     }
     546           0 :     return status;
     547             :   }
     548             : 
     549           0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
     550             :   bool succeeded;
     551           0 :   if (hc && NS_SUCCEEDED(hc->GetRequestSucceeded(&succeeded)) && !succeeded) {
     552           0 :     uint32_t responseStatus = 0;
     553           0 :     Unused << hc->GetResponseStatus(&responseStatus);
     554           0 :     nsAutoCString statusText;
     555           0 :     Unused << hc->GetResponseStatusText(statusText);
     556           0 :     element->NotifyLoadError(
     557           0 :       nsPrintfCString("%u: %s", responseStatus, statusText.get()));
     558             : 
     559           0 :     nsAutoString code;
     560           0 :     code.AppendInt(responseStatus);
     561           0 :     nsAutoString src;
     562           0 :     element->GetCurrentSrc(src);
     563           0 :     const char16_t* params[] = { code.get(), src.get() };
     564           0 :     element->ReportLoadError("MediaLoadHttpError", params, ArrayLength(params));
     565           0 :     return NS_BINDING_ABORTED;
     566             :   }
     567             : 
     568           0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     569           0 :   if (channel &&
     570           0 :       NS_SUCCEEDED(rv = element->InitializeDecoderForChannel(
     571           0 :                      channel, getter_AddRefs(mNextListener))) &&
     572           0 :       mNextListener) {
     573           0 :     rv = mNextListener->OnStartRequest(aRequest, aContext);
     574             :   } else {
     575             :     // If InitializeDecoderForChannel() returned an error, fire a network error.
     576           0 :     if (NS_FAILED(rv) && !mNextListener) {
     577             :       // Load failed, attempt to load the next candidate resource. If there
     578             :       // are none, this will trigger a MEDIA_ERR_SRC_NOT_SUPPORTED error.
     579           0 :       element->NotifyLoadError(NS_LITERAL_CSTRING("Failed to init decoder"));
     580             :     }
     581             :     // If InitializeDecoderForChannel did not return a listener (but may
     582             :     // have otherwise succeeded), we abort the connection since we aren't
     583             :     // interested in keeping the channel alive ourselves.
     584             :     rv = NS_BINDING_ABORTED;
     585             :   }
     586             : 
     587             :   return rv;
     588             : }
     589             : 
     590             : NS_IMETHODIMP
     591           0 : HTMLMediaElement::MediaLoadListener::OnStopRequest(nsIRequest* aRequest,
     592             :                                                    nsISupports* aContext,
     593             :                                                    nsresult aStatus)
     594             : {
     595           0 :   if (mNextListener) {
     596           0 :     return mNextListener->OnStopRequest(aRequest, aContext, aStatus);
     597             :   }
     598             :   return NS_OK;
     599             : }
     600             : 
     601             : NS_IMETHODIMP
     602           0 : HTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest* aRequest,
     603             :                                                      nsISupports* aContext,
     604             :                                                      nsIInputStream* aStream,
     605             :                                                      uint64_t aOffset,
     606             :                                                      uint32_t aCount)
     607             : {
     608           0 :   if (!mNextListener) {
     609           0 :     NS_ERROR("Must have a chained listener; OnStartRequest should have "
     610             :              "canceled this request");
     611           0 :     return NS_BINDING_ABORTED;
     612             :   }
     613           0 :   return mNextListener->OnDataAvailable(
     614           0 :     aRequest, aContext, aStream, aOffset, aCount);
     615             : }
     616             : 
     617             : NS_IMETHODIMP
     618           0 : HTMLMediaElement::MediaLoadListener::AsyncOnChannelRedirect(
     619             :   nsIChannel* aOldChannel,
     620             :   nsIChannel* aNewChannel,
     621             :   uint32_t aFlags,
     622             :   nsIAsyncVerifyRedirectCallback* cb)
     623             : {
     624             :   // TODO is this really correct?? See bug #579329.
     625           0 :   if (mElement) {
     626           0 :     mElement->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
     627             :   }
     628           0 :   nsCOMPtr<nsIChannelEventSink> sink = do_QueryInterface(mNextListener);
     629           0 :   if (sink) {
     630           0 :     return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
     631             :   }
     632           0 :   cb->OnRedirectVerifyCallback(NS_OK);
     633           0 :   return NS_OK;
     634             : }
     635             : 
     636             : NS_IMETHODIMP
     637           0 : HTMLMediaElement::MediaLoadListener::CheckListenerChain()
     638             : {
     639           0 :   MOZ_ASSERT(mNextListener);
     640             :   nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
     641           0 :     do_QueryInterface(mNextListener);
     642           0 :   if (retargetable) {
     643           0 :     return retargetable->CheckListenerChain();
     644             :   }
     645             :   return NS_ERROR_NO_INTERFACE;
     646             : }
     647             : 
     648             : NS_IMETHODIMP
     649           0 : HTMLMediaElement::MediaLoadListener::GetInterface(const nsIID& aIID,
     650             :                                                   void** aResult)
     651             : {
     652           0 :   return QueryInterface(aIID, aResult);
     653             : }
     654             : 
     655             : void
     656           0 : HTMLMediaElement::ReportLoadError(const char* aMsg,
     657             :                                   const char16_t** aParams,
     658             :                                   uint32_t aParamCount)
     659             : {
     660           0 :   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     661           0 :                                   NS_LITERAL_CSTRING("Media"),
     662           0 :                                   OwnerDoc(),
     663             :                                   nsContentUtils::eDOM_PROPERTIES,
     664             :                                   aMsg,
     665             :                                   aParams,
     666           0 :                                   aParamCount);
     667           0 : }
     668             : 
     669             : class HTMLMediaElement::AudioChannelAgentCallback final
     670             :   : public nsIAudioChannelAgentCallback
     671             : {
     672             : public:
     673             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     674           0 :   NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgentCallback)
     675             : 
     676           0 :   explicit AudioChannelAgentCallback(HTMLMediaElement* aOwner)
     677           0 :     : mOwner(aOwner)
     678             :     , mAudioChannelVolume(1.0)
     679             :     , mPlayingThroughTheAudioChannel(false)
     680             :     , mAudioCapturedByWindow(false)
     681             :     , mSuspended(nsISuspendedTypes::NONE_SUSPENDED)
     682           0 :     , mIsOwnerAudible(IsOwnerAudible())
     683           0 :     , mIsShutDown(false)
     684             :   {
     685           0 :     MOZ_ASSERT(mOwner);
     686           0 :     MaybeCreateAudioChannelAgent();
     687           0 :   }
     688             : 
     689           0 :   void UpdateAudioChannelPlayingState(bool aForcePlaying = false)
     690             :   {
     691           0 :     MOZ_ASSERT(!mIsShutDown);
     692             :     bool playingThroughTheAudioChannel =
     693           0 :       aForcePlaying || IsPlayingThroughTheAudioChannel();
     694             : 
     695           0 :     if (playingThroughTheAudioChannel != mPlayingThroughTheAudioChannel) {
     696           0 :       if (!MaybeCreateAudioChannelAgent()) {
     697             :         return;
     698             :       }
     699             : 
     700           0 :       mPlayingThroughTheAudioChannel = playingThroughTheAudioChannel;
     701           0 :       NotifyAudioChannelAgent(mPlayingThroughTheAudioChannel);
     702             :     }
     703             :   }
     704             : 
     705           0 :   bool ShouldResetSuspend() const
     706             :   {
     707             :     // The disposable-pause should be clear after media starts playing.
     708           0 :     if (!mOwner->Paused() &&
     709           0 :         mSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE) {
     710             :       return true;
     711             :     }
     712             : 
     713             :     // If the blocked media is paused, we don't need to resume it. We reset the
     714             :     // mSuspended in order to unregister the agent.
     715           0 :     if (mOwner->Paused() && mSuspended == nsISuspendedTypes::SUSPENDED_BLOCK) {
     716             :       return true;
     717             :     }
     718             : 
     719           0 :     return false;
     720             :   }
     721             : 
     722           0 :   void NotifyPlayStateChanged()
     723             :   {
     724           0 :     MOZ_ASSERT(!mIsShutDown);
     725           0 :     if (ShouldResetSuspend()) {
     726           0 :       SetSuspended(nsISuspendedTypes::NONE_SUSPENDED);
     727             :       NotifyAudioPlaybackChanged(
     728           0 :         AudioChannelService::AudibleChangedReasons::ePauseStateChanged);
     729             :     }
     730           0 :     UpdateAudioChannelPlayingState();
     731           0 :   }
     732             : 
     733           0 :   NS_IMETHODIMP WindowVolumeChanged(float aVolume, bool aMuted) override
     734             :   {
     735           0 :     MOZ_ASSERT(mAudioChannelAgent);
     736             : 
     737           0 :     MOZ_LOG(
     738             :       AudioChannelService::GetAudioChannelLog(),
     739             :       LogLevel::Debug,
     740             :       ("HTMLMediaElement::AudioChannelAgentCallback, WindowVolumeChanged, "
     741             :        "this = %p, aVolume = %f, aMuted = %s\n",
     742             :        this,
     743             :        aVolume,
     744             :        aMuted ? "true" : "false"));
     745             : 
     746           0 :     if (mAudioChannelVolume != aVolume) {
     747           0 :       mAudioChannelVolume = aVolume;
     748           0 :       mOwner->SetVolumeInternal();
     749             :     }
     750             : 
     751           0 :     const uint32_t muted = mOwner->mMuted;
     752           0 :     if (aMuted && !mOwner->ComputedMuted()) {
     753           0 :       mOwner->SetMutedInternal(muted | MUTED_BY_AUDIO_CHANNEL);
     754           0 :     } else if (!aMuted && mOwner->ComputedMuted()) {
     755           0 :       mOwner->SetMutedInternal(muted & ~MUTED_BY_AUDIO_CHANNEL);
     756             :     }
     757             : 
     758           0 :     return NS_OK;
     759             :   }
     760             : 
     761           0 :   NS_IMETHODIMP WindowSuspendChanged(SuspendTypes aSuspend) override
     762             :   {
     763           0 :     MOZ_ASSERT(mAudioChannelAgent);
     764             : 
     765           0 :     MOZ_LOG(
     766             :       AudioChannelService::GetAudioChannelLog(),
     767             :       LogLevel::Debug,
     768             :       ("HTMLMediaElement::AudioChannelAgentCallback, WindowSuspendChanged, "
     769             :        "this = %p, aSuspend = %s\n",
     770             :        this,
     771             :        SuspendTypeToStr(aSuspend)));
     772             : 
     773           0 :     switch (aSuspend) {
     774             :       case nsISuspendedTypes::NONE_SUSPENDED:
     775           0 :         Resume();
     776           0 :         break;
     777             :       case nsISuspendedTypes::SUSPENDED_PAUSE:
     778             :       case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
     779             :       case nsISuspendedTypes::SUSPENDED_BLOCK:
     780           0 :         Suspend(aSuspend);
     781           0 :         break;
     782             :       case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
     783           0 :         Stop();
     784           0 :         break;
     785             :       default:
     786           0 :         MOZ_LOG(
     787             :           AudioChannelService::GetAudioChannelLog(),
     788             :           LogLevel::Debug,
     789             :           ("HTMLMediaElement::AudioChannelAgentCallback, WindowSuspendChanged, "
     790             :            "this = %p, Error : unknown suspended type!\n",
     791             :            this));
     792             :     }
     793           0 :     return NS_OK;
     794             :   }
     795             : 
     796           0 :   NS_IMETHODIMP WindowAudioCaptureChanged(bool aCapture) override
     797             :   {
     798           0 :     MOZ_ASSERT(mAudioChannelAgent);
     799             : 
     800           0 :     if (mAudioCapturedByWindow != aCapture) {
     801           0 :       mAudioCapturedByWindow = aCapture;
     802           0 :       AudioCaptureStreamChangeIfNeeded();
     803             :     }
     804           0 :     return NS_OK;
     805             :   }
     806             : 
     807           0 :   void AudioCaptureStreamChangeIfNeeded()
     808             :   {
     809           0 :     MOZ_ASSERT(!mIsShutDown);
     810           0 :     if (!IsPlayingStarted()) {
     811             :       return;
     812             :     }
     813             : 
     814           0 :     if (!mOwner->HasAudio()) {
     815             :       return;
     816             :     }
     817             : 
     818           0 :     mOwner->AudioCaptureStreamChange(mAudioCapturedByWindow);
     819             :   }
     820             : 
     821           0 :   void NotifyAudioPlaybackChanged(AudibleChangedReasons aReason)
     822             :   {
     823           0 :     MOZ_ASSERT(!mIsShutDown);
     824           0 :     if (!IsPlayingStarted()) {
     825             :       return;
     826             :     }
     827             : 
     828           0 :     AudibleState newAudibleState = IsOwnerAudible();
     829           0 :     if (mIsOwnerAudible == newAudibleState) {
     830             :       return;
     831             :     }
     832             : 
     833           0 :     mIsOwnerAudible = newAudibleState;
     834           0 :     mAudioChannelAgent->NotifyStartedAudible(mIsOwnerAudible, aReason);
     835             :   }
     836             : 
     837           0 :   bool IsPlaybackBlocked()
     838             :   {
     839           0 :     MOZ_ASSERT(!mIsShutDown);
     840             :     // If the tab hasn't been activated yet, the media element in that tab can't
     841             :     // be playback now until the tab goes to foreground first time or user
     842             :     // clicks the unblocking tab icon.
     843           0 :     if (!IsTabActivated()) {
     844             :       // Even we haven't start playing yet, we still need to notify the audio
     845             :       // channe system because we need to receive the resume notification later.
     846           0 :       UpdateAudioChannelPlayingState(true /* force to start */);
     847           0 :       return true;
     848             :     }
     849             : 
     850             :     return false;
     851             :   }
     852             : 
     853           0 :   void Shutdown()
     854             :   {
     855           0 :     MOZ_ASSERT(!mIsShutDown);
     856           0 :     if (mAudioChannelAgent) {
     857           0 :       mAudioChannelAgent->NotifyStoppedPlaying();
     858           0 :       mAudioChannelAgent = nullptr;
     859             :     }
     860           0 :     mIsShutDown = true;
     861           0 :   }
     862             : 
     863           0 :   float GetEffectiveVolume() const
     864             :   {
     865           0 :     MOZ_ASSERT(!mIsShutDown);
     866           0 :     return mOwner->Volume() * mAudioChannelVolume;
     867             :   }
     868             : 
     869           0 :   SuspendTypes GetSuspendType() const
     870             :   {
     871           0 :     MOZ_ASSERT(!mIsShutDown);
     872           0 :     return mSuspended;
     873             :   }
     874             : 
     875             : private:
     876           0 :   ~AudioChannelAgentCallback() { MOZ_ASSERT(mIsShutDown); };
     877             : 
     878           0 :   bool MaybeCreateAudioChannelAgent()
     879             :   {
     880           0 :     if (mAudioChannelAgent) {
     881             :       return true;
     882             :     }
     883             : 
     884           0 :     mAudioChannelAgent = new AudioChannelAgent();
     885             :     nsresult rv =
     886           0 :       mAudioChannelAgent->Init(mOwner->OwnerDoc()->GetInnerWindow(), this);
     887           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     888           0 :       mAudioChannelAgent = nullptr;
     889           0 :       MOZ_LOG(
     890             :         AudioChannelService::GetAudioChannelLog(),
     891             :         LogLevel::Debug,
     892             :         ("HTMLMediaElement::AudioChannelAgentCallback, Fail to initialize "
     893             :          "the audio channel agent, this = %p\n",
     894             :          this));
     895             :       return false;
     896             :     }
     897             : 
     898             :     return true;
     899             :   }
     900             : 
     901           0 :   void NotifyAudioChannelAgent(bool aPlaying)
     902             :   {
     903           0 :     MOZ_ASSERT(mAudioChannelAgent);
     904             : 
     905           0 :     if (aPlaying) {
     906           0 :       AudioPlaybackConfig config;
     907             :       nsresult rv =
     908           0 :         mAudioChannelAgent->NotifyStartedPlaying(&config, IsOwnerAudible());
     909           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     910           0 :         return;
     911             :       }
     912             : 
     913           0 :       WindowVolumeChanged(config.mVolume, config.mMuted);
     914           0 :       WindowSuspendChanged(config.mSuspend);
     915             :     } else {
     916           0 :       mAudioChannelAgent->NotifyStoppedPlaying();
     917             :     }
     918             :   }
     919             : 
     920           0 :   void SetSuspended(SuspendTypes aSuspend)
     921             :   {
     922           0 :     if (mSuspended == aSuspend) {
     923             :       return;
     924             :     }
     925             : 
     926           0 :     MaybeNotifyMediaResumed(aSuspend);
     927           0 :     mSuspended = aSuspend;
     928           0 :     MOZ_LOG(
     929             :       AudioChannelService::GetAudioChannelLog(),
     930             :       LogLevel::Debug,
     931             :       ("HTMLMediaElement::AudioChannelAgentCallback, SetAudioChannelSuspended, "
     932             :        "this = %p, aSuspend = %s\n",
     933             :        this,
     934             :        SuspendTypeToStr(aSuspend)));
     935             :   }
     936             : 
     937           0 :   void Resume()
     938             :   {
     939           0 :     if (!IsSuspended()) {
     940           0 :       MOZ_LOG(
     941             :         AudioChannelService::GetAudioChannelLog(),
     942             :         LogLevel::Debug,
     943             :         ("HTMLMediaElement::AudioChannelAgentCallback, ResumeFromAudioChannel, "
     944             :          "this = %p, don't need to be resumed!\n",
     945             :          this));
     946           0 :       return;
     947             :     }
     948             : 
     949           0 :     SetSuspended(nsISuspendedTypes::NONE_SUSPENDED);
     950           0 :     IgnoredErrorResult rv;
     951           0 :     RefPtr<Promise> toBeIgnored = mOwner->Play(rv);
     952           0 :     MOZ_ASSERT_IF(toBeIgnored &&
     953             :                     toBeIgnored->State() == Promise::PromiseState::Rejected,
     954             :                   rv.Failed());
     955           0 :     if (rv.Failed()) {
     956           0 :       NS_WARNING("Not able to resume from AudioChannel.");
     957             :     }
     958             : 
     959             :     NotifyAudioPlaybackChanged(
     960           0 :       AudioChannelService::AudibleChangedReasons::ePauseStateChanged);
     961             :   }
     962             : 
     963           0 :   void Suspend(SuspendTypes aSuspend)
     964             :   {
     965           0 :     if (IsSuspended()) {
     966             :       return;
     967             :     }
     968             : 
     969           0 :     SetSuspended(aSuspend);
     970           0 :     if (aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE ||
     971           0 :         aSuspend == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE) {
     972           0 :       IgnoredErrorResult rv;
     973           0 :       mOwner->Pause(rv);
     974           0 :       if (NS_WARN_IF(rv.Failed())) {
     975           0 :         return;
     976             :       }
     977             :     }
     978             :     NotifyAudioPlaybackChanged(
     979           0 :       AudioChannelService::AudibleChangedReasons::ePauseStateChanged);
     980             :   }
     981             : 
     982           0 :   void Stop()
     983             :   {
     984           0 :     SetSuspended(nsISuspendedTypes::NONE_SUSPENDED);
     985           0 :     mOwner->Pause();
     986           0 :   }
     987             : 
     988           0 :   bool IsPlayingStarted()
     989             :   {
     990           0 :     if (MaybeCreateAudioChannelAgent()) {
     991           0 :       return mAudioChannelAgent->IsPlayingStarted();
     992             :     }
     993             :     return false;
     994             :   }
     995             : 
     996           0 :   void MaybeNotifyMediaResumed(SuspendTypes aSuspend)
     997             :   {
     998             :     // In fennec, we should send the notification when media is resumed from the
     999             :     // pause-disposable which was called by media control.
    1000           0 :     if (mSuspended != nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE &&
    1001             :         aSuspend != nsISuspendedTypes::NONE_SUSPENDED) {
    1002             :       return;
    1003             :     }
    1004             : 
    1005           0 :     if (!IsPlayingStarted()) {
    1006             :       return;
    1007             :     }
    1008             : 
    1009           0 :     uint64_t windowID = mAudioChannelAgent->WindowID();
    1010           0 :     mOwner->MainThreadEventTarget()->Dispatch(NS_NewRunnableFunction(
    1011             :       "dom::HTMLMediaElement::AudioChannelAgentCallback::"
    1012             :       "MaybeNotifyMediaResumed",
    1013           0 :       [windowID]() -> void {
    1014             :         nsCOMPtr<nsIObserverService> observerService =
    1015           0 :           services::GetObserverService();
    1016           0 :         if (NS_WARN_IF(!observerService)) {
    1017           0 :           return;
    1018             :         }
    1019             : 
    1020             :         nsCOMPtr<nsISupportsPRUint64> wrapper =
    1021           0 :           do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
    1022           0 :         if (NS_WARN_IF(!wrapper)) {
    1023           0 :           return;
    1024             :         }
    1025             : 
    1026           0 :         wrapper->SetData(windowID);
    1027           0 :         observerService->NotifyObservers(
    1028           0 :           wrapper, "media-playback-resumed", u"active");
    1029           0 :       }));
    1030             :   }
    1031             : 
    1032           0 :   bool IsTabActivated()
    1033             :   {
    1034           0 :     if (MaybeCreateAudioChannelAgent()) {
    1035           0 :       return !mAudioChannelAgent->ShouldBlockMedia();
    1036             :     }
    1037             :     return false;
    1038             :   }
    1039             : 
    1040           0 :   bool IsSuspended() const
    1041             :   {
    1042           0 :     return (mSuspended == nsISuspendedTypes::SUSPENDED_PAUSE ||
    1043           0 :             mSuspended == nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE ||
    1044           0 :             mSuspended == nsISuspendedTypes::SUSPENDED_BLOCK);
    1045             :   }
    1046             : 
    1047           0 :   AudibleState IsOwnerAudible() const
    1048             :   {
    1049             :     // Muted or the volume should not be ~0
    1050           0 :     if (mOwner->mMuted || (std::fabs(mOwner->Volume()) <= 1e-7)) {
    1051           0 :       return mOwner->HasAudio()
    1052           0 :                ? AudioChannelService::AudibleState::eMaybeAudible
    1053             :                : AudioChannelService::AudibleState::eNotAudible;
    1054             :     }
    1055             : 
    1056             :     // No audio track.
    1057           0 :     if (!mOwner->HasAudio()) {
    1058             :       return AudioChannelService::AudibleState::eNotAudible;
    1059             :     }
    1060             : 
    1061             :     // Might be audible but not yet.
    1062           0 :     if (mOwner->HasAudio() && !mOwner->mIsAudioTrackAudible) {
    1063             :       return AudioChannelService::AudibleState::eMaybeAudible;
    1064             :     }
    1065             : 
    1066             :     // Suspended or paused media doesn't produce any sound.
    1067           0 :     if (mSuspended != nsISuspendedTypes::NONE_SUSPENDED || mOwner->mPaused) {
    1068             :       return AudioChannelService::AudibleState::eNotAudible;
    1069             :     }
    1070             : 
    1071           0 :     return AudioChannelService::AudibleState::eAudible;
    1072             :   }
    1073             : 
    1074           0 :   bool IsPlayingThroughTheAudioChannel() const
    1075             :   {
    1076             :     // If we have an error, we are not playing.
    1077           0 :     if (mOwner->GetError()) {
    1078             :       return false;
    1079             :     }
    1080             : 
    1081             :     // We should consider any bfcached page or inactive document as non-playing.
    1082           0 :     if (!mOwner->IsActive()) {
    1083             :       return false;
    1084             :     }
    1085             : 
    1086             :     // It might be resumed from remote, we should keep the audio channel agent.
    1087           0 :     if (IsSuspended()) {
    1088             :       return true;
    1089             :     }
    1090             : 
    1091             :     // Are we paused
    1092           0 :     if (mOwner->mPaused) {
    1093             :       return false;
    1094             :     }
    1095             : 
    1096             :     // No audio track
    1097           0 :     if (!mOwner->HasAudio()) {
    1098             :       return false;
    1099             :     }
    1100             : 
    1101             :     // A loop always is playing
    1102           0 :     if (mOwner->HasAttr(kNameSpaceID_None, nsGkAtoms::loop)) {
    1103             :       return true;
    1104             :     }
    1105             : 
    1106             :     // If we are actually playing...
    1107           0 :     if (mOwner->IsCurrentlyPlaying()) {
    1108             :       return true;
    1109             :     }
    1110             : 
    1111             :     // If we are playing an external stream.
    1112           0 :     if (mOwner->mSrcAttrStream) {
    1113             :       return true;
    1114             :     }
    1115             : 
    1116           0 :     return false;
    1117             :   }
    1118             : 
    1119             :   RefPtr<AudioChannelAgent> mAudioChannelAgent;
    1120             :   HTMLMediaElement* mOwner;
    1121             : 
    1122             :   // The audio channel volume
    1123             :   float mAudioChannelVolume;
    1124             :   // Is this media element playing?
    1125             :   bool mPlayingThroughTheAudioChannel;
    1126             :   // True if the sound is being captured by the window.
    1127             :   bool mAudioCapturedByWindow;
    1128             :   // We have different kinds of suspended cases,
    1129             :   // - SUSPENDED_PAUSE
    1130             :   // It's used when we temporary lost platform audio focus. MediaElement can
    1131             :   // only be resumed when we gain the audio focus again.
    1132             :   // - SUSPENDED_PAUSE_DISPOSABLE
    1133             :   // It's used when user press the pause button on the remote media-control.
    1134             :   // MediaElement can be resumed by remote media-control or via play().
    1135             :   // - SUSPENDED_BLOCK
    1136             :   // It's used to reduce the power consumption, we won't play the auto-play
    1137             :   // audio/video in the page we have never visited before. MediaElement would
    1138             :   // be resumed when the page is active. See bug647429 for more details.
    1139             :   // - SUSPENDED_STOP_DISPOSABLE
    1140             :   // When we permanently lost platform audio focus, we should stop playing
    1141             :   // and stop the audio channel agent. MediaElement can only be restarted by
    1142             :   // play().
    1143             :   SuspendTypes mSuspended;
    1144             :   // Indicate whether media element is audible for users.
    1145             :   AudibleState mIsOwnerAudible;
    1146             :   bool mIsShutDown;
    1147             : };
    1148             : 
    1149             : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement::AudioChannelAgentCallback)
    1150             : 
    1151           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
    1152             :   HTMLMediaElement::AudioChannelAgentCallback)
    1153           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelAgent)
    1154           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1155             : 
    1156           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
    1157             :   HTMLMediaElement::AudioChannelAgentCallback)
    1158           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioChannelAgent)
    1159           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1160             : 
    1161           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    1162             :   HTMLMediaElement::AudioChannelAgentCallback)
    1163           0 :   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
    1164           0 : NS_INTERFACE_MAP_END
    1165             : 
    1166           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLMediaElement::AudioChannelAgentCallback)
    1167           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLMediaElement::AudioChannelAgentCallback)
    1168             : 
    1169           0 : class HTMLMediaElement::ChannelLoader final
    1170             : {
    1171             : public:
    1172           0 :   NS_INLINE_DECL_REFCOUNTING(ChannelLoader);
    1173             : 
    1174           0 :   void LoadInternal(HTMLMediaElement* aElement)
    1175             :   {
    1176           0 :     if (mCancelled) {
    1177           0 :       return;
    1178             :     }
    1179             : 
    1180             :     // determine what security checks need to be performed in AsyncOpen2().
    1181             :     nsSecurityFlags securityFlags =
    1182           0 :       aElement->ShouldCheckAllowOrigin()
    1183           0 :         ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
    1184           0 :         : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
    1185             : 
    1186           0 :     if (aElement->GetCORSMode() == CORS_USE_CREDENTIALS) {
    1187           0 :       securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
    1188             :     }
    1189             : 
    1190           0 :     MOZ_ASSERT(
    1191             :       aElement->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
    1192             :     nsContentPolicyType contentPolicyType =
    1193           0 :       aElement->IsHTMLElement(nsGkAtoms::audio)
    1194           0 :         ? nsIContentPolicy::TYPE_INTERNAL_AUDIO
    1195           0 :         : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
    1196             : 
    1197             :     // If aElement has 'triggeringprincipal' attribute, we will use the value as
    1198             :     // triggeringPrincipal for the channel, otherwise it will default to use
    1199             :     // aElement->NodePrincipal().
    1200             :     // This function returns true when aElement has 'triggeringprincipal', so if
    1201             :     // setAttrs is true we will override the origin attributes on the channel
    1202             :     // later.
    1203           0 :     nsCOMPtr<nsIPrincipal> triggeringPrincipal;
    1204           0 :     bool setAttrs = nsContentUtils::QueryTriggeringPrincipal(
    1205             :       aElement,
    1206             :       aElement->mLoadingSrcTriggeringPrincipal,
    1207           0 :       getter_AddRefs(triggeringPrincipal));
    1208             : 
    1209           0 :     nsCOMPtr<nsILoadGroup> loadGroup = aElement->GetDocumentLoadGroup();
    1210           0 :     nsCOMPtr<nsIChannel> channel;
    1211           0 :     nsresult rv = NS_NewChannelWithTriggeringPrincipal(
    1212           0 :       getter_AddRefs(channel),
    1213             :       aElement->mLoadingSrc,
    1214             :       static_cast<Element*>(aElement),
    1215             :       triggeringPrincipal,
    1216             :       securityFlags,
    1217             :       contentPolicyType,
    1218             :       nullptr, // aPerformanceStorage
    1219             :       loadGroup,
    1220             :       nullptr, // aCallbacks
    1221             :       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
    1222             :         nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
    1223           0 :         nsIChannel::LOAD_CLASSIFY_URI | nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
    1224             : 
    1225           0 :     if (NS_FAILED(rv)) {
    1226             :       // Notify load error so the element will try next resource candidate.
    1227           0 :       aElement->NotifyLoadError(NS_LITERAL_CSTRING("Fail to create channel"));
    1228           0 :       return;
    1229             :     }
    1230             : 
    1231           0 :     if (setAttrs) {
    1232           0 :       nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
    1233           0 :       if (loadInfo) {
    1234             :         // The function simply returns NS_OK, so we ignore the return value.
    1235           0 :         Unused << loadInfo->SetOriginAttributes(
    1236           0 :           triggeringPrincipal->OriginAttributesRef());
    1237             :       }
    1238             :     }
    1239             : 
    1240           0 :     nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
    1241           0 :     if (cos) {
    1242           0 :       if (aElement->mUseUrgentStartForChannel) {
    1243           0 :         cos->AddClassFlags(nsIClassOfService::UrgentStart);
    1244             : 
    1245             :         // Reset the flag to avoid loading again without initiated by user
    1246             :         // interaction.
    1247           0 :         aElement->mUseUrgentStartForChannel = false;
    1248             :       }
    1249             : 
    1250             :       // Unconditionally disable throttling since we want the media to fluently
    1251             :       // play even when we switch the tab to background.
    1252           0 :       cos->AddClassFlags(nsIClassOfService::DontThrottle);
    1253             :     }
    1254             : 
    1255             :     // The listener holds a strong reference to us.  This creates a
    1256             :     // reference cycle, once we've set mChannel, which is manually broken
    1257             :     // in the listener's OnStartRequest method after it is finished with
    1258             :     // the element. The cycle will also be broken if we get a shutdown
    1259             :     // notification before OnStartRequest fires.  Necko guarantees that
    1260             :     // OnStartRequest will eventually fire if we don't shut down first.
    1261           0 :     RefPtr<MediaLoadListener> loadListener = new MediaLoadListener(aElement);
    1262             : 
    1263           0 :     channel->SetNotificationCallbacks(loadListener);
    1264             : 
    1265           0 :     nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
    1266           0 :     if (hc) {
    1267             :       // Use a byte range request from the start of the resource.
    1268             :       // This enables us to detect if the stream supports byte range
    1269             :       // requests, and therefore seeking, early.
    1270           0 :       rv = hc->SetRequestHeader(
    1271           0 :         NS_LITERAL_CSTRING("Range"), NS_LITERAL_CSTRING("bytes=0-"), false);
    1272           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    1273           0 :       aElement->SetRequestHeaders(hc);
    1274             :     }
    1275             : 
    1276           0 :     rv = channel->AsyncOpen2(loadListener);
    1277           0 :     if (NS_FAILED(rv)) {
    1278             :       // Notify load error so the element will try next resource candidate.
    1279           0 :       aElement->NotifyLoadError(NS_LITERAL_CSTRING("Failed to open channel"));
    1280           0 :       return;
    1281             :     }
    1282             : 
    1283             :     // Else the channel must be open and starting to download. If it encounters
    1284             :     // a non-catastrophic failure, it will set a new task to continue loading
    1285             :     // another candidate.  It's safe to set it as mChannel now.
    1286           0 :     mChannel = channel;
    1287             : 
    1288             :     // loadListener will be unregistered either on shutdown or when
    1289             :     // OnStartRequest for the channel we just opened fires.
    1290           0 :     nsContentUtils::RegisterShutdownObserver(loadListener);
    1291             :   }
    1292             : 
    1293           0 :   nsresult Load(HTMLMediaElement* aElement)
    1294             :   {
    1295           0 :     MOZ_ASSERT(aElement);
    1296             :     // Per bug 1235183 comment 8, we can't spin the event loop from stable
    1297             :     // state. Defer NS_NewChannel() to a new regular runnable.
    1298           0 :     return aElement->MainThreadEventTarget()->Dispatch(
    1299           0 :       NewRunnableMethod<HTMLMediaElement*>("ChannelLoader::LoadInternal",
    1300             :                                            this,
    1301             :                                            &ChannelLoader::LoadInternal,
    1302           0 :                                            aElement));
    1303             :   }
    1304             : 
    1305           0 :   void Cancel()
    1306             :   {
    1307           0 :     mCancelled = true;
    1308           0 :     if (mChannel) {
    1309           0 :       mChannel->Cancel(NS_BINDING_ABORTED);
    1310           0 :       mChannel = nullptr;
    1311             :     }
    1312           0 :   }
    1313             : 
    1314           0 :   void Done()
    1315             :   {
    1316           0 :     MOZ_ASSERT(mChannel);
    1317             :     // Decoder successfully created, the decoder now owns the MediaResource
    1318             :     // which owns the channel.
    1319           0 :     mChannel = nullptr;
    1320           0 :   }
    1321             : 
    1322           0 :   nsresult Redirect(nsIChannel* aChannel,
    1323             :                     nsIChannel* aNewChannel,
    1324             :                     uint32_t aFlags)
    1325             :   {
    1326           0 :     NS_ASSERTION(aChannel == mChannel, "Channels should match!");
    1327           0 :     mChannel = aNewChannel;
    1328             : 
    1329             :     // Handle forwarding of Range header so that the intial detection
    1330             :     // of seeking support (via result code 206) works across redirects.
    1331           0 :     nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
    1332           0 :     NS_ENSURE_STATE(http);
    1333             : 
    1334           0 :     NS_NAMED_LITERAL_CSTRING(rangeHdr, "Range");
    1335             : 
    1336           0 :     nsAutoCString rangeVal;
    1337           0 :     if (NS_SUCCEEDED(http->GetRequestHeader(rangeHdr, rangeVal))) {
    1338           0 :       NS_ENSURE_STATE(!rangeVal.IsEmpty());
    1339             : 
    1340           0 :       http = do_QueryInterface(aNewChannel);
    1341           0 :       NS_ENSURE_STATE(http);
    1342             : 
    1343           0 :       nsresult rv = http->SetRequestHeader(rangeHdr, rangeVal, false);
    1344           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1345             :     }
    1346             : 
    1347             :     return NS_OK;
    1348             :   }
    1349             : 
    1350             : private:
    1351           0 :   ~ChannelLoader() { MOZ_ASSERT(!mChannel); }
    1352             :   // Holds a reference to the first channel we open to the media resource.
    1353             :   // Once the decoder is created, control over the channel passes to the
    1354             :   // decoder, and we null out this reference. We must store this in case
    1355             :   // we need to cancel the channel before control of it passes to the decoder.
    1356             :   nsCOMPtr<nsIChannel> mChannel;
    1357             : 
    1358             :   bool mCancelled = false;
    1359             : };
    1360             : 
    1361           0 : class HTMLMediaElement::ErrorSink
    1362             : {
    1363             : public:
    1364           0 :   explicit ErrorSink(HTMLMediaElement* aOwner)
    1365           0 :     : mOwner(aOwner)
    1366           0 :     , mSrcIsUnsupportedTypeMedia(false)
    1367             :   {
    1368           0 :     MOZ_ASSERT(mOwner);
    1369           0 :   }
    1370             : 
    1371           0 :   void SetError(uint16_t aErrorCode, const nsACString& aErrorDetails)
    1372             :   {
    1373             :     // Since we have multiple paths calling into DecodeError, e.g.
    1374             :     // MediaKeys::Terminated and EMEH264Decoder::Error. We should take the 1st
    1375             :     // one only in order not to fire multiple 'error' events.
    1376           0 :     if (mError) {
    1377             :       return;
    1378             :     }
    1379             : 
    1380           0 :     if (!IsValidErrorCode(aErrorCode)) {
    1381           0 :       NS_ASSERTION(false, "Undefined MediaError codes!");
    1382             :       return;
    1383             :     }
    1384             : 
    1385             :     // TODO : remove unsupported type related codes after finishing native
    1386             :     // support for HLS, see bug 1350842.
    1387           0 :     if (CanOwnerPlayUnsupportedTypeMedia() &&
    1388             :         aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED) {
    1389             :       // On Fennec, we do some hack for unsupported type media, we don't set
    1390             :       // its error state in order to open it with external app.
    1391             :       mSrcIsUnsupportedTypeMedia = true;
    1392             :       mOwner->ChangeNetworkState(NETWORK_NO_SOURCE);
    1393             :       MaybeOpenUnsupportedMediaForOwner();
    1394             :     } else {
    1395           0 :       mError = new MediaError(mOwner, aErrorCode, aErrorDetails);
    1396           0 :       mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("error"));
    1397           0 :       if (mOwner->ReadyState() == HAVE_NOTHING &&
    1398             :           aErrorCode == MEDIA_ERR_ABORTED) {
    1399             :         // https://html.spec.whatwg.org/multipage/embedded-content.html#media-data-processing-steps-list
    1400             :         // "If the media data fetching process is aborted by the user"
    1401           0 :         mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
    1402           0 :         mOwner->ChangeNetworkState(NETWORK_EMPTY);
    1403           0 :         mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
    1404           0 :         if (mOwner->mDecoder) {
    1405           0 :           mOwner->ShutdownDecoder();
    1406             :         }
    1407           0 :       } else if (aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED) {
    1408           0 :         mOwner->ChangeNetworkState(NETWORK_NO_SOURCE);
    1409             :       } else {
    1410           0 :         mOwner->ChangeNetworkState(NETWORK_IDLE);
    1411             :       }
    1412             :     }
    1413             :   }
    1414             : 
    1415           0 :   void ResetError()
    1416             :   {
    1417           0 :     mError = nullptr;
    1418           0 :     mSrcIsUnsupportedTypeMedia = false;
    1419           0 :   }
    1420             : 
    1421             :   void MaybeOpenUnsupportedMediaForOwner() const
    1422             :   {
    1423             :     // Src is supported type or we don't open the pref for external app.
    1424             :     if (!mSrcIsUnsupportedTypeMedia || !CanOwnerPlayUnsupportedTypeMedia()) {
    1425             :       return;
    1426             :     }
    1427             : 
    1428             :     // If media doesn't start playing, we don't need to open it.
    1429             :     if (mOwner->Paused()) {
    1430             :       return;
    1431             :     }
    1432             : 
    1433             :     nsContentUtils::DispatchTrustedEvent(
    1434             :       mOwner->OwnerDoc(),
    1435             :       static_cast<nsIContent*>(mOwner),
    1436             :       NS_LITERAL_STRING("OpenMediaWithExternalApp"),
    1437             :       true,
    1438             :       true);
    1439             :   }
    1440             : 
    1441             :   RefPtr<MediaError> mError;
    1442             : 
    1443             : private:
    1444             :   bool IsValidErrorCode(const uint16_t& aErrorCode) const
    1445             :   {
    1446             :     return (aErrorCode == MEDIA_ERR_DECODE || aErrorCode == MEDIA_ERR_NETWORK ||
    1447           0 :             aErrorCode == MEDIA_ERR_ABORTED ||
    1448             :             aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED);
    1449             :   }
    1450             : 
    1451             :   bool CanOwnerPlayUnsupportedTypeMedia() const
    1452             :   {
    1453             : #if defined(MOZ_WIDGET_ANDROID)
    1454             :     // On Fennec, we will use an external app to open unsupported media types.
    1455             :     return Preferences::GetBool("media.openUnsupportedTypeWithExternalApp");
    1456             : #endif
    1457             :     return false;
    1458             :   }
    1459             : 
    1460             :   // Media elememt's life cycle would be longer than error sink, so we use the
    1461             :   // raw pointer and this class would only be referenced by media element.
    1462             :   HTMLMediaElement* mOwner;
    1463             :   bool mSrcIsUnsupportedTypeMedia;
    1464             : };
    1465             : 
    1466             : NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement)
    1467             : 
    1468           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement,
    1469             :                                                   nsGenericHTMLElement)
    1470           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
    1471           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcMediaSource)
    1472           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcStream)
    1473           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcAttrStream)
    1474           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourcePointer)
    1475           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDoc)
    1476           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceLoadCandidate)
    1477           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelWrapper)
    1478           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mErrorSink->mError)
    1479           0 :   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
    1480           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream)
    1481           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mPreCreatedTracks)
    1482             :   }
    1483           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed);
    1484           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager)
    1485           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioTrackList)
    1486           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoTrackList)
    1487           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
    1488           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncomingMediaKeys)
    1489           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
    1490           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayPromises)
    1491           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSeekDOMPromise)
    1492           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSetMediaKeysDOMPromise)
    1493           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    1494             : 
    1495           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement,
    1496             :                                                 nsGenericHTMLElement)
    1497           0 :   tmp->RemoveMutationObserver(tmp);
    1498           0 :   if (tmp->mSrcStream) {
    1499             :     // Need to EndMediaStreamPlayback to clear mSrcStream and make sure
    1500             :     // everything gets unhooked correctly.
    1501           0 :     tmp->EndSrcMediaStreamPlayback();
    1502             :   }
    1503           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcAttrStream)
    1504           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
    1505           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcMediaSource)
    1506           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourcePointer)
    1507           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoadBlockedDoc)
    1508           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceLoadCandidate)
    1509           0 :   if (tmp->mAudioChannelWrapper) {
    1510           0 :     tmp->mAudioChannelWrapper->Shutdown();
    1511             :   }
    1512           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioChannelWrapper)
    1513           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mErrorSink->mError)
    1514           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams)
    1515           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed)
    1516           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
    1517           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
    1518           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
    1519           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
    1520           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncomingMediaKeys)
    1521           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
    1522           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayPromises)
    1523           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSeekDOMPromise)
    1524           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSetMediaKeysDOMPromise)
    1525           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1526             : 
    1527           8 : NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(HTMLMediaElement,
    1528             :                                                nsGenericHTMLElement)
    1529             : 
    1530             : void
    1531           0 : HTMLMediaElement::ContentRemoved(nsIContent* aChild,
    1532             :                                  nsIContent* aPreviousSibling)
    1533             : {
    1534           0 :   if (aChild == mSourcePointer) {
    1535           0 :     mSourcePointer = aPreviousSibling;
    1536             :   }
    1537           0 : }
    1538             : 
    1539             : already_AddRefed<MediaSource>
    1540           0 : HTMLMediaElement::GetMozMediaSourceObject() const
    1541             : {
    1542           0 :   RefPtr<MediaSource> source = mMediaSource;
    1543           0 :   return source.forget();
    1544             : }
    1545             : 
    1546             : void
    1547           0 : HTMLMediaElement::GetMozDebugReaderData(nsAString& aString)
    1548             : {
    1549           0 :   if (mDecoder && !mSrcStream) {
    1550           0 :     nsAutoCString result;
    1551           0 :     mDecoder->GetMozDebugReaderData(result);
    1552           0 :     CopyUTF8toUTF16(result, aString);
    1553             :   }
    1554           0 : }
    1555             : 
    1556             : already_AddRefed<Promise>
    1557           0 : HTMLMediaElement::MozRequestDebugInfo(ErrorResult& aRv)
    1558             : {
    1559           0 :   RefPtr<Promise> promise = CreateDOMPromise(aRv);
    1560           0 :   if (NS_WARN_IF(aRv.Failed())) {
    1561             :     return nullptr;
    1562             :   }
    1563             : 
    1564           0 :   nsAutoString result;
    1565           0 :   GetMozDebugReaderData(result);
    1566             : 
    1567           0 :   if (mVideoFrameContainer) {
    1568           0 :     result.AppendPrintf(
    1569             :       "Compositor dropped frame(including when element's invisible): %u\n",
    1570           0 :       mVideoFrameContainer->GetDroppedImageCount());
    1571             :   }
    1572             : 
    1573           0 :   if (mMediaKeys) {
    1574           0 :     nsString EMEInfo;
    1575           0 :     GetEMEInfo(EMEInfo);
    1576           0 :     result.AppendLiteral("EME Info: ");
    1577           0 :     result.Append(EMEInfo);
    1578           0 :     result.AppendLiteral("\n");
    1579             :   }
    1580             : 
    1581           0 :   if (mDecoder) {
    1582           0 :     mDecoder->RequestDebugInfo()->Then(
    1583             :       mAbstractMainThread,
    1584             :       __func__,
    1585           0 :       [promise, result](const nsACString& aString) {
    1586           0 :         promise->MaybeResolve(result + NS_ConvertUTF8toUTF16(aString));
    1587           0 :       },
    1588           0 :       [promise, result]() { promise->MaybeResolve(result); });
    1589             :   } else {
    1590           0 :     promise->MaybeResolve(result);
    1591             :   }
    1592             : 
    1593           0 :   return promise.forget();
    1594             : }
    1595             : 
    1596             : /* static */ void
    1597           0 : HTMLMediaElement::MozEnableDebugLog(const GlobalObject&)
    1598             : {
    1599           0 :   DecoderDoctorLogger::EnableLogging();
    1600           0 : }
    1601             : 
    1602             : already_AddRefed<Promise>
    1603           0 : HTMLMediaElement::MozRequestDebugLog(ErrorResult& aRv)
    1604             : {
    1605           0 :   RefPtr<Promise> promise = CreateDOMPromise(aRv);
    1606           0 :   if (NS_WARN_IF(aRv.Failed())) {
    1607             :     return nullptr;
    1608             :   }
    1609             : 
    1610           0 :   DecoderDoctorLogger::RetrieveMessages(this)->Then(
    1611             :     mAbstractMainThread,
    1612             :     __func__,
    1613           0 :     [promise](const nsACString& aString) {
    1614           0 :       promise->MaybeResolve(NS_ConvertUTF8toUTF16(aString));
    1615           0 :     },
    1616           0 :     [promise](nsresult rv) { promise->MaybeReject(rv); });
    1617             : 
    1618             :   return promise.forget();
    1619             : }
    1620             : 
    1621             : already_AddRefed<Promise>
    1622           0 : HTMLMediaElement::MozDumpDebugInfo()
    1623             : {
    1624           0 :   ErrorResult rv;
    1625           0 :   RefPtr<Promise> promise = CreateDOMPromise(rv);
    1626           0 :   if (NS_WARN_IF(rv.Failed())) {
    1627             :     return nullptr;
    1628             :   }
    1629           0 :   if (mDecoder) {
    1630           0 :     mDecoder->DumpDebugInfo()->Then(mAbstractMainThread,
    1631             :                                     __func__,
    1632             :                                     promise.get(),
    1633           0 :                                     &Promise::MaybeResolveWithUndefined);
    1634             :   } else {
    1635           0 :     promise->MaybeResolveWithUndefined();
    1636             :   }
    1637             :   return promise.forget();
    1638             : }
    1639             : 
    1640             : void
    1641           0 : HTMLMediaElement::SetVisible(bool aVisible)
    1642             : {
    1643           0 :   mForcedHidden = !aVisible;
    1644           0 :   if (mDecoder) {
    1645           0 :     mDecoder->SetForcedHidden(!aVisible);
    1646             :   }
    1647           0 : }
    1648             : 
    1649             : already_AddRefed<layers::Image>
    1650           0 : HTMLMediaElement::GetCurrentImage()
    1651             : {
    1652           0 :   MarkAsTainted();
    1653             : 
    1654             :   // TODO: In bug 1345404, handle case when video decoder is already suspended.
    1655           0 :   ImageContainer* container = GetImageContainer();
    1656           0 :   if (!container) {
    1657             :     return nullptr;
    1658             :   }
    1659             : 
    1660           0 :   AutoLockImage lockImage(container);
    1661           0 :   RefPtr<layers::Image> image = lockImage.GetImage(TimeStamp::Now());
    1662           0 :   return image.forget();
    1663             : }
    1664             : 
    1665             : bool
    1666           0 : HTMLMediaElement::HasSuspendTaint() const
    1667             : {
    1668           0 :   MOZ_ASSERT(!mDecoder || (mDecoder->HasSuspendTaint() == mHasSuspendTaint));
    1669           0 :   return mHasSuspendTaint;
    1670             : }
    1671             : 
    1672             : already_AddRefed<DOMMediaStream>
    1673           0 : HTMLMediaElement::GetSrcObject() const
    1674             : {
    1675           0 :   NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetPlaybackStream(),
    1676             :                "MediaStream should have been set up properly");
    1677           0 :   RefPtr<DOMMediaStream> stream = mSrcAttrStream;
    1678           0 :   return stream.forget();
    1679             : }
    1680             : 
    1681             : void
    1682           0 : HTMLMediaElement::SetSrcObject(DOMMediaStream& aValue)
    1683             : {
    1684           0 :   SetSrcObject(&aValue);
    1685           0 : }
    1686             : 
    1687             : void
    1688           0 : HTMLMediaElement::SetSrcObject(DOMMediaStream* aValue)
    1689             : {
    1690           0 :   mSrcAttrStream = aValue;
    1691           0 :   UpdateAudioChannelPlayingState();
    1692           0 :   DoLoad();
    1693           0 : }
    1694             : 
    1695             : bool
    1696           0 : HTMLMediaElement::Ended()
    1697             : {
    1698           0 :   return (mDecoder && mDecoder->IsEnded()) ||
    1699           0 :          (mSrcStream && !mSrcStream->Active());
    1700             : }
    1701             : 
    1702             : void
    1703           0 : HTMLMediaElement::GetCurrentSrc(nsAString& aCurrentSrc)
    1704             : {
    1705           0 :   nsAutoCString src;
    1706           0 :   GetCurrentSpec(src);
    1707           0 :   CopyUTF8toUTF16(src, aCurrentSrc);
    1708           0 : }
    1709             : 
    1710             : nsresult
    1711           0 : HTMLMediaElement::OnChannelRedirect(nsIChannel* aChannel,
    1712             :                                     nsIChannel* aNewChannel,
    1713             :                                     uint32_t aFlags)
    1714             : {
    1715           0 :   MOZ_ASSERT(mChannelLoader);
    1716           0 :   return mChannelLoader->Redirect(aChannel, aNewChannel, aFlags);
    1717             : }
    1718             : 
    1719             : void
    1720           0 : HTMLMediaElement::ShutdownDecoder()
    1721             : {
    1722           0 :   RemoveMediaElementFromURITable();
    1723           0 :   NS_ASSERTION(mDecoder, "Must have decoder to shut down");
    1724             : 
    1725           0 :   mWaitingForKeyListener.DisconnectIfExists();
    1726           0 :   if (mMediaSource) {
    1727           0 :     mMediaSource->CompletePendingTransactions();
    1728             :   }
    1729           0 :   for (OutputMediaStream& out : mOutputStreams) {
    1730           0 :     if (!out.mCapturingDecoder) {
    1731             :       continue;
    1732             :     }
    1733           0 :     out.mNextAvailableTrackID = std::max<TrackID>(
    1734           0 :       mDecoder->NextAvailableTrackIDFor(out.mStream->GetInputStream()),
    1735           0 :       out.mNextAvailableTrackID);
    1736             :   }
    1737           0 :   mDecoder->Shutdown();
    1738           0 :   DDUNLINKCHILD(mDecoder.get());
    1739           0 :   mDecoder = nullptr;
    1740           0 : }
    1741             : 
    1742             : void
    1743           0 : HTMLMediaElement::AbortExistingLoads()
    1744             : {
    1745             :   // Abort any already-running instance of the resource selection algorithm.
    1746           0 :   mLoadWaitStatus = NOT_WAITING;
    1747             : 
    1748             :   // Set a new load ID. This will cause events which were enqueued
    1749             :   // with a different load ID to silently be cancelled.
    1750           0 :   mCurrentLoadID++;
    1751             : 
    1752             :   // Immediately reject or resolve the already-dispatched
    1753             :   // nsResolveOrRejectPendingPlayPromisesRunners. These runners won't be
    1754             :   // executed again later since the mCurrentLoadID had been changed.
    1755           0 :   for (auto& runner : mPendingPlayPromisesRunners) {
    1756           0 :     runner->ResolveOrReject();
    1757             :   }
    1758           0 :   mPendingPlayPromisesRunners.Clear();
    1759             : 
    1760           0 :   if (mChannelLoader) {
    1761           0 :     mChannelLoader->Cancel();
    1762           0 :     mChannelLoader = nullptr;
    1763             :   }
    1764             : 
    1765           0 :   bool fireTimeUpdate = false;
    1766             : 
    1767             :   // We need to remove StreamSizeListener before VideoTracks get emptied.
    1768           0 :   if (mMediaStreamSizeListener) {
    1769           0 :     mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
    1770           0 :     mMediaStreamSizeListener->Forget();
    1771           0 :     mMediaStreamSizeListener = nullptr;
    1772             :   }
    1773             : 
    1774             :   // When aborting the existing loads, empty the objects in audio track list and
    1775             :   // video track list, no events (in particular, no removetrack events) are
    1776             :   // fired as part of this. Ending MediaStream sends track ended notifications,
    1777             :   // so we empty the track lists prior.
    1778           0 :   AudioTracks()->EmptyTracks();
    1779           0 :   VideoTracks()->EmptyTracks();
    1780             : 
    1781           0 :   if (mDecoder) {
    1782           0 :     fireTimeUpdate = mDecoder->GetCurrentTime() != 0.0;
    1783           0 :     ShutdownDecoder();
    1784             :   }
    1785           0 :   if (mSrcStream) {
    1786           0 :     EndSrcMediaStreamPlayback();
    1787             :   }
    1788             : 
    1789           0 :   RemoveMediaElementFromURITable();
    1790           0 :   mLoadingSrc = nullptr;
    1791           0 :   mLoadingSrcTriggeringPrincipal = nullptr;
    1792           0 :   DDLOG(DDLogCategory::Property, "loading_src", "");
    1793           0 :   DDUNLINKCHILD(mMediaSource.get());
    1794           0 :   mMediaSource = nullptr;
    1795             : 
    1796           0 :   if (mNetworkState == NETWORK_LOADING || mNetworkState == NETWORK_IDLE) {
    1797           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
    1798             :   }
    1799             : 
    1800           0 :   mErrorSink->ResetError();
    1801           0 :   mCurrentPlayRangeStart = -1.0;
    1802           0 :   mLoadedDataFired = false;
    1803           0 :   mAutoplaying = true;
    1804           0 :   mIsLoadingFromSourceChildren = false;
    1805           0 :   mSuspendedAfterFirstFrame = false;
    1806           0 :   mAllowSuspendAfterFirstFrame = true;
    1807           0 :   mHaveQueuedSelectResource = false;
    1808           0 :   mSuspendedForPreloadNone = false;
    1809           0 :   mDownloadSuspendedByCache = false;
    1810           0 :   mMediaInfo = MediaInfo();
    1811           0 :   mIsEncrypted = false;
    1812           0 :   mPendingEncryptedInitData.Reset();
    1813           0 :   mWaitingForKey = NOT_WAITING_FOR_KEY;
    1814           0 :   mSourcePointer = nullptr;
    1815             : 
    1816           0 :   mTags = nullptr;
    1817             : 
    1818           0 :   if (mNetworkState != NETWORK_EMPTY) {
    1819           0 :     NS_ASSERTION(!mDecoder && !mSrcStream,
    1820             :                  "How did someone setup a new stream/decoder already?");
    1821             :     // ChangeNetworkState() will call UpdateAudioChannelPlayingState()
    1822             :     // indirectly which depends on mPaused. So we need to update mPaused first.
    1823           0 :     if (!mPaused) {
    1824           0 :       mPaused = true;
    1825           0 :       DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
    1826           0 :       RejectPromises(TakePendingPlayPromises(), NS_ERROR_DOM_MEDIA_ABORT_ERR);
    1827             :     }
    1828           0 :     ChangeNetworkState(NETWORK_EMPTY);
    1829           0 :     ChangeReadyState(HAVE_NOTHING);
    1830             : 
    1831             :     // TODO: Apply the rules for text track cue rendering Bug 865407
    1832           0 :     if (mTextTrackManager) {
    1833           0 :       mTextTrackManager->GetTextTracks()->SetCuesInactive();
    1834             :     }
    1835             : 
    1836           0 :     if (fireTimeUpdate) {
    1837             :       // Since we destroyed the decoder above, the current playback position
    1838             :       // will now be reported as 0. The playback position was non-zero when
    1839             :       // we destroyed the decoder, so fire a timeupdate event so that the
    1840             :       // change will be reflected in the controls.
    1841           0 :       FireTimeUpdate(false);
    1842             :     }
    1843           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
    1844           0 :     UpdateAudioChannelPlayingState();
    1845             :   }
    1846             : 
    1847             :   // We may have changed mPaused, mAutoplaying, and other
    1848             :   // things which can affect AddRemoveSelfReference
    1849           0 :   AddRemoveSelfReference();
    1850             : 
    1851           0 :   mIsRunningSelectResource = false;
    1852             : 
    1853           0 :   if (mTextTrackManager) {
    1854           0 :     mTextTrackManager->NotifyReset();
    1855             :   }
    1856             : 
    1857           0 :   mEventDeliveryPaused = false;
    1858           0 :   mPendingEvents.Clear();
    1859             : 
    1860           0 :   AssertReadyStateIsNothing();
    1861           0 : }
    1862             : 
    1863             : void
    1864           0 : HTMLMediaElement::NoSupportedMediaSourceError(const nsACString& aErrorDetails)
    1865             : {
    1866           0 :   if (mDecoder) {
    1867           0 :     ShutdownDecoder();
    1868             :   }
    1869           0 :   mErrorSink->SetError(MEDIA_ERR_SRC_NOT_SUPPORTED, aErrorDetails);
    1870           0 :   ChangeDelayLoadStatus(false);
    1871           0 :   UpdateAudioChannelPlayingState();
    1872           0 :   RejectPromises(TakePendingPlayPromises(),
    1873           0 :                  NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
    1874           0 : }
    1875             : 
    1876             : typedef void (HTMLMediaElement::*SyncSectionFn)();
    1877             : 
    1878             : // Runs a "synchronous section", a function that must run once the event loop
    1879             : // has reached a "stable state". See:
    1880             : // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
    1881           0 : class nsSyncSection : public nsMediaEvent
    1882             : {
    1883             : private:
    1884             :   nsCOMPtr<nsIRunnable> mRunnable;
    1885             : 
    1886             : public:
    1887           0 :   nsSyncSection(HTMLMediaElement* aElement, nsIRunnable* aRunnable)
    1888           0 :     : nsMediaEvent("dom::nsSyncSection", aElement)
    1889           0 :     , mRunnable(aRunnable)
    1890             :   {
    1891           0 :   }
    1892             : 
    1893           0 :   NS_IMETHOD Run() override
    1894             :   {
    1895             :     // Silently cancel if our load has been cancelled.
    1896           0 :     if (IsCancelled())
    1897             :       return NS_OK;
    1898           0 :     mRunnable->Run();
    1899           0 :     return NS_OK;
    1900             :   }
    1901             : };
    1902             : 
    1903             : void
    1904           0 : HTMLMediaElement::RunInStableState(nsIRunnable* aRunnable)
    1905             : {
    1906           0 :   if (mShuttingDown) {
    1907           0 :     return;
    1908             :   }
    1909             : 
    1910           0 :   nsCOMPtr<nsIRunnable> event = new nsSyncSection(this, aRunnable);
    1911           0 :   nsContentUtils::RunInStableState(event.forget());
    1912             : }
    1913             : 
    1914             : void
    1915           0 : HTMLMediaElement::QueueLoadFromSourceTask()
    1916             : {
    1917           0 :   if (!mIsLoadingFromSourceChildren || mShuttingDown) {
    1918           0 :     return;
    1919             :   }
    1920             : 
    1921           0 :   if (mDecoder) {
    1922             :     // Reset readyState to HAVE_NOTHING since we're going to load a new decoder.
    1923           0 :     ShutdownDecoder();
    1924           0 :     ChangeReadyState(HAVE_NOTHING);
    1925             :   }
    1926             : 
    1927           0 :   AssertReadyStateIsNothing();
    1928             : 
    1929           0 :   ChangeDelayLoadStatus(true);
    1930           0 :   ChangeNetworkState(NETWORK_LOADING);
    1931             :   RefPtr<Runnable> r =
    1932           0 :     NewRunnableMethod("HTMLMediaElement::LoadFromSourceChildren",
    1933             :                       this,
    1934           0 :                       &HTMLMediaElement::LoadFromSourceChildren);
    1935           0 :   RunInStableState(r);
    1936             : }
    1937             : 
    1938             : void
    1939           0 : HTMLMediaElement::QueueSelectResourceTask()
    1940             : {
    1941             :   // Don't allow multiple async select resource calls to be queued.
    1942           0 :   if (mHaveQueuedSelectResource)
    1943           0 :     return;
    1944           0 :   mHaveQueuedSelectResource = true;
    1945           0 :   ChangeNetworkState(NETWORK_NO_SOURCE);
    1946             :   RefPtr<Runnable> r =
    1947           0 :     NewRunnableMethod("HTMLMediaElement::SelectResourceWrapper",
    1948             :                       this,
    1949           0 :                       &HTMLMediaElement::SelectResourceWrapper);
    1950           0 :   RunInStableState(r);
    1951             : }
    1952             : 
    1953             : static bool
    1954           0 : HasSourceChildren(nsIContent* aElement)
    1955             : {
    1956           0 :   for (nsIContent* child = aElement->GetFirstChild(); child;
    1957           0 :        child = child->GetNextSibling()) {
    1958           0 :     if (child->IsHTMLElement(nsGkAtoms::source)) {
    1959             :       return true;
    1960             :     }
    1961             :   }
    1962             :   return false;
    1963             : }
    1964             : 
    1965             : static nsCString
    1966           0 : DocumentOrigin(nsIDocument* aDoc)
    1967             : {
    1968           0 :   if (!aDoc) {
    1969           0 :     return NS_LITERAL_CSTRING("null");
    1970             :   }
    1971           0 :   nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
    1972           0 :   if (!principal) {
    1973           0 :     return NS_LITERAL_CSTRING("null");
    1974             :   }
    1975           0 :   nsCString origin;
    1976           0 :   if (NS_FAILED(principal->GetOrigin(origin))) {
    1977           0 :     return NS_LITERAL_CSTRING("null");
    1978             :   }
    1979           0 :   return origin;
    1980             : }
    1981             : 
    1982             : void
    1983           0 : HTMLMediaElement::Load()
    1984             : {
    1985           0 :   LOG(LogLevel::Debug,
    1986             :       ("%p Load() hasSrcAttrStream=%d hasSrcAttr=%d hasSourceChildren=%d "
    1987             :        "handlingInput=%d hasAutoplayAttr=%d IsAllowedToPlay=%d "
    1988             :        "ownerDoc=%p (%s) ownerDocUserActivated=%d "
    1989             :        "muted=%d volume=%f",
    1990             :        this,
    1991             :        !!mSrcAttrStream,
    1992             :        HasAttr(kNameSpaceID_None, nsGkAtoms::src),
    1993             :        HasSourceChildren(this),
    1994             :        EventStateManager::IsHandlingUserInput(),
    1995             :        HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay),
    1996             :        IsAllowedToPlay(),
    1997             :        OwnerDoc(),
    1998             :        DocumentOrigin(OwnerDoc()).get(),
    1999             :        OwnerDoc() ? OwnerDoc()->HasBeenUserActivated() : 0,
    2000             :        mMuted,
    2001             :        mVolume));
    2002             : 
    2003           0 :   if (mIsRunningLoadMethod) {
    2004             :     return;
    2005             :   }
    2006             : 
    2007           0 :   mIsDoingExplicitLoad = true;
    2008           0 :   DoLoad();
    2009             : }
    2010             : 
    2011             : void
    2012           0 : HTMLMediaElement::DoLoad()
    2013             : {
    2014             :   // Check if media is allowed for the docshell.
    2015           0 :   nsCOMPtr<nsIDocShell> docShell = OwnerDoc()->GetDocShell();
    2016           0 :   if (docShell && !docShell->GetAllowMedia()) {
    2017           0 :     LOG(LogLevel::Debug, ("%p Media not allowed", this));
    2018           0 :     return;
    2019             :   }
    2020             : 
    2021           0 :   if (mIsRunningLoadMethod) {
    2022             :     return;
    2023             :   }
    2024             : 
    2025           0 :   if (EventStateManager::IsHandlingUserInput()) {
    2026             :     // Detect if user has interacted with element so that play will not be
    2027             :     // blocked when initiated by a script. This enables sites to capture user
    2028             :     // intent to play by calling load() in the click handler of a "catalog
    2029             :     // view" of a gallery of videos.
    2030           0 :     mIsBlessed = true;
    2031             :     // Mark the channel as urgent-start when autopaly so that it will play the
    2032             :     // media from src after loading enough resource.
    2033           0 :     if (HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) {
    2034           0 :       mUseUrgentStartForChannel = true;
    2035             :     }
    2036             :   }
    2037             : 
    2038           0 :   SetPlayedOrSeeked(false);
    2039           0 :   mIsRunningLoadMethod = true;
    2040           0 :   AbortExistingLoads();
    2041           0 :   SetPlaybackRate(mDefaultPlaybackRate, IgnoreErrors());
    2042           0 :   QueueSelectResourceTask();
    2043           0 :   ResetState();
    2044           0 :   mIsRunningLoadMethod = false;
    2045             : }
    2046             : 
    2047             : void
    2048           0 : HTMLMediaElement::ResetState()
    2049             : {
    2050             :   // There might be a pending MediaDecoder::PlaybackPositionChanged() which
    2051             :   // will overwrite |mMediaInfo.mVideo.mDisplay| in UpdateMediaSize() to give
    2052             :   // staled videoWidth and videoHeight. We have to call ForgetElement() here
    2053             :   // such that the staled callbacks won't reach us.
    2054           0 :   if (mVideoFrameContainer) {
    2055           0 :     mVideoFrameContainer->ForgetElement();
    2056           0 :     mVideoFrameContainer = nullptr;
    2057             :   }
    2058           0 : }
    2059             : 
    2060             : void
    2061           0 : HTMLMediaElement::SelectResourceWrapper()
    2062             : {
    2063           0 :   SelectResource();
    2064           0 :   mIsRunningSelectResource = false;
    2065           0 :   mHaveQueuedSelectResource = false;
    2066           0 :   mIsDoingExplicitLoad = false;
    2067           0 : }
    2068             : 
    2069             : void
    2070           0 : HTMLMediaElement::SelectResource()
    2071             : {
    2072           0 :   if (!mSrcAttrStream && !HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
    2073           0 :       !HasSourceChildren(this)) {
    2074             :     // The media element has neither a src attribute nor any source
    2075             :     // element children, abort the load.
    2076           0 :     ChangeNetworkState(NETWORK_EMPTY);
    2077           0 :     ChangeDelayLoadStatus(false);
    2078           0 :     return;
    2079             :   }
    2080             : 
    2081           0 :   ChangeDelayLoadStatus(true);
    2082             : 
    2083           0 :   ChangeNetworkState(NETWORK_LOADING);
    2084           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
    2085             : 
    2086             :   // Delay setting mIsRunningSeletResource until after UpdatePreloadAction
    2087             :   // so that we don't lose our state change by bailing out of the preload
    2088             :   // state update
    2089           0 :   UpdatePreloadAction();
    2090           0 :   mIsRunningSelectResource = true;
    2091             : 
    2092             :   // If we have a 'src' attribute, use that exclusively.
    2093           0 :   nsAutoString src;
    2094           0 :   if (mSrcAttrStream) {
    2095           0 :     SetupSrcMediaStreamPlayback(mSrcAttrStream);
    2096           0 :   } else if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
    2097           0 :     nsCOMPtr<nsIURI> uri;
    2098           0 :     MediaResult rv = NewURIFromString(src, getter_AddRefs(uri));
    2099           0 :     if (NS_SUCCEEDED(rv)) {
    2100           0 :       LOG(
    2101             :         LogLevel::Debug,
    2102             :         ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
    2103           0 :       NS_ASSERTION(
    2104             :         !mIsLoadingFromSourceChildren,
    2105             :         "Should think we're not loading from source children by default");
    2106             : 
    2107           0 :       RemoveMediaElementFromURITable();
    2108           0 :       mLoadingSrc = uri;
    2109           0 :       mLoadingSrcTriggeringPrincipal = mSrcAttrTriggeringPrincipal;
    2110           0 :       DDLOG(DDLogCategory::Property,
    2111             :             "loading_src",
    2112             :             nsCString(NS_ConvertUTF16toUTF8(src)));
    2113           0 :       mMediaSource = mSrcMediaSource;
    2114           0 :       DDLINKCHILD("mediasource", mMediaSource.get());
    2115           0 :       UpdatePreloadAction();
    2116           0 :       if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE && !mMediaSource) {
    2117             :         // preload:none media, suspend the load here before we make any
    2118             :         // network requests.
    2119           0 :         SuspendLoad();
    2120           0 :         return;
    2121             :       }
    2122             : 
    2123           0 :       rv = LoadResource();
    2124           0 :       if (NS_SUCCEEDED(rv)) {
    2125             :         return;
    2126             :       }
    2127             :     } else {
    2128           0 :       const char16_t* params[] = { src.get() };
    2129           0 :       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
    2130           0 :       rv = MediaResult(rv.Code(), "MediaLoadInvalidURI");
    2131             :     }
    2132             :     // The media element has neither a src attribute nor a source element child:
    2133             :     // set the networkState to NETWORK_EMPTY, and abort these steps; the
    2134             :     // synchronous section ends.
    2135           0 :     mMainThreadEventTarget->Dispatch(NewRunnableMethod<nsCString>(
    2136             :       "HTMLMediaElement::NoSupportedMediaSourceError",
    2137             :       this,
    2138             :       &HTMLMediaElement::NoSupportedMediaSourceError,
    2139           0 :       rv.Description()));
    2140             :   } else {
    2141             :     // Otherwise, the source elements will be used.
    2142           0 :     mIsLoadingFromSourceChildren = true;
    2143           0 :     LoadFromSourceChildren();
    2144             :   }
    2145             : }
    2146             : 
    2147             : void
    2148           0 : HTMLMediaElement::NotifyLoadError(const nsACString& aErrorDetails)
    2149             : {
    2150           0 :   if (!mIsLoadingFromSourceChildren) {
    2151           0 :     LOG(LogLevel::Debug, ("NotifyLoadError(), no supported media error"));
    2152           0 :     NoSupportedMediaSourceError(aErrorDetails);
    2153           0 :   } else if (mSourceLoadCandidate) {
    2154           0 :     DispatchAsyncSourceError(mSourceLoadCandidate);
    2155           0 :     QueueLoadFromSourceTask();
    2156             :   } else {
    2157           0 :     NS_WARNING("Should know the source we were loading from!");
    2158             :   }
    2159           0 : }
    2160             : 
    2161             : void
    2162           0 : HTMLMediaElement::NotifyMediaTrackEnabled(MediaTrack* aTrack)
    2163             : {
    2164           0 :   MOZ_ASSERT(aTrack);
    2165           0 :   if (!aTrack) {
    2166           0 :     return;
    2167             :   }
    2168             : #ifdef DEBUG
    2169           0 :   nsString id;
    2170           0 :   aTrack->GetId(id);
    2171             : 
    2172           0 :   LOG(LogLevel::Debug,
    2173             :       ("MediaElement %p %sTrack with id %s enabled",
    2174             :        this,
    2175             :        aTrack->AsAudioTrack() ? "Audio" : "Video",
    2176             :        NS_ConvertUTF16toUTF8(id).get()));
    2177             : #endif
    2178             : 
    2179           0 :   MOZ_ASSERT((aTrack->AsAudioTrack() && aTrack->AsAudioTrack()->Enabled()) ||
    2180             :              (aTrack->AsVideoTrack() && aTrack->AsVideoTrack()->Selected()));
    2181             : 
    2182           0 :   if (aTrack->AsAudioTrack()) {
    2183           0 :     SetMutedInternal(mMuted & ~MUTED_BY_AUDIO_TRACK);
    2184           0 :   } else if (aTrack->AsVideoTrack()) {
    2185           0 :     if (!IsVideo()) {
    2186           0 :       MOZ_ASSERT(false);
    2187             :       return;
    2188             :     }
    2189           0 :     mDisableVideo = false;
    2190             :   } else {
    2191           0 :     MOZ_ASSERT(false, "Unknown track type");
    2192             :   }
    2193             : 
    2194           0 :   if (mSrcStream) {
    2195           0 :     if (aTrack->AsVideoTrack()) {
    2196           0 :       MOZ_ASSERT(!mSelectedVideoStreamTrack);
    2197           0 :       MOZ_ASSERT(!mMediaStreamSizeListener);
    2198             : 
    2199           0 :       mSelectedVideoStreamTrack = aTrack->AsVideoTrack()->GetVideoStreamTrack();
    2200           0 :       VideoFrameContainer* container = GetVideoFrameContainer();
    2201           0 :       if (mSrcStreamIsPlaying && container) {
    2202           0 :         mSelectedVideoStreamTrack->AddVideoOutput(container);
    2203             :       }
    2204           0 :       HTMLVideoElement* self = static_cast<HTMLVideoElement*>(this);
    2205           0 :       if (self->VideoWidth() <= 1 && self->VideoHeight() <= 1) {
    2206             :         // MediaInfo uses dummy values of 1 for width and height to
    2207             :         // mark video as valid. We need a new stream size listener
    2208             :         // if size is 0x0 or 1x1.
    2209           0 :         mMediaStreamSizeListener = new StreamSizeListener(this);
    2210           0 :         mSelectedVideoStreamTrack->AddDirectListener(mMediaStreamSizeListener);
    2211             :       }
    2212             :     }
    2213             : 
    2214           0 :     if (mReadyState == HAVE_NOTHING) {
    2215             :       // No MediaStreamTracks are captured until we have metadata.
    2216             :       return;
    2217             :     }
    2218           0 :     for (OutputMediaStream& ms : mOutputStreams) {
    2219           0 :       if (aTrack->AsVideoTrack() && ms.mCapturingAudioOnly) {
    2220             :         // If the output stream is for audio only we ignore video tracks.
    2221             :         continue;
    2222             :       }
    2223           0 :       AddCaptureMediaTrackToOutputStream(aTrack, ms);
    2224             :     }
    2225             :   }
    2226             : }
    2227             : 
    2228             : void
    2229           0 : HTMLMediaElement::NotifyMediaTrackDisabled(MediaTrack* aTrack)
    2230             : {
    2231           0 :   MOZ_ASSERT(aTrack);
    2232           0 :   if (!aTrack) {
    2233           0 :     return;
    2234             :   }
    2235             : #ifdef DEBUG
    2236           0 :   nsString id;
    2237           0 :   aTrack->GetId(id);
    2238             : 
    2239           0 :   LOG(LogLevel::Debug,
    2240             :       ("MediaElement %p %sTrack with id %s disabled",
    2241             :        this,
    2242             :        aTrack->AsAudioTrack() ? "Audio" : "Video",
    2243             :        NS_ConvertUTF16toUTF8(id).get()));
    2244             : #endif
    2245             : 
    2246           0 :   MOZ_ASSERT((!aTrack->AsAudioTrack() || !aTrack->AsAudioTrack()->Enabled()) &&
    2247             :              (!aTrack->AsVideoTrack() || !aTrack->AsVideoTrack()->Selected()));
    2248             : 
    2249           0 :   if (aTrack->AsAudioTrack()) {
    2250             :     // If we don't have any alive track , we don't need to mute MediaElement.
    2251           0 :     if (AudioTracks()->Length() > 0) {
    2252             :       bool shouldMute = true;
    2253           0 :       for (uint32_t i = 0; i < AudioTracks()->Length(); ++i) {
    2254           0 :         if ((*AudioTracks())[i]->Enabled()) {
    2255             :           shouldMute = false;
    2256             :           break;
    2257             :         }
    2258             :       }
    2259             : 
    2260           0 :       if (shouldMute) {
    2261           0 :         SetMutedInternal(mMuted | MUTED_BY_AUDIO_TRACK);
    2262             :       }
    2263             :     }
    2264           0 :   } else if (aTrack->AsVideoTrack()) {
    2265           0 :     if (mSrcStream) {
    2266           0 :       MOZ_ASSERT(mSelectedVideoStreamTrack);
    2267           0 :       if (mSelectedVideoStreamTrack && mMediaStreamSizeListener) {
    2268           0 :         mSelectedVideoStreamTrack->RemoveDirectListener(
    2269           0 :           mMediaStreamSizeListener);
    2270           0 :         mMediaStreamSizeListener->Forget();
    2271           0 :         mMediaStreamSizeListener = nullptr;
    2272             :       }
    2273           0 :       VideoFrameContainer* container = GetVideoFrameContainer();
    2274           0 :       if (mSrcStreamIsPlaying && container) {
    2275           0 :         mSelectedVideoStreamTrack->RemoveVideoOutput(container);
    2276             :       }
    2277           0 :       mSelectedVideoStreamTrack = nullptr;
    2278             :     }
    2279             :   }
    2280             : 
    2281           0 :   if (mReadyState == HAVE_NOTHING) {
    2282             :     // No MediaStreamTracks are captured until we have metadata, and code
    2283             :     // below doesn't do anything for captured decoders.
    2284             :     return;
    2285             :   }
    2286             : 
    2287           0 :   for (OutputMediaStream& ms : mOutputStreams) {
    2288           0 :     if (ms.mCapturingDecoder) {
    2289           0 :       MOZ_ASSERT(!ms.mCapturingMediaStream);
    2290             :       continue;
    2291             :     }
    2292           0 :     MOZ_ASSERT(ms.mCapturingMediaStream);
    2293           0 :     for (int32_t i = ms.mTrackPorts.Length() - 1; i >= 0; --i) {
    2294           0 :       if (ms.mTrackPorts[i].first() == aTrack->GetId()) {
    2295             :         // The source of this track just ended. Force-notify that it ended.
    2296             :         // If we bounce it to the MediaStreamGraph it might not be picked up,
    2297             :         // for instance if the MediaInputPort was destroyed in the same
    2298             :         // iteration as it was added.
    2299           0 :         MediaStreamTrack* outputTrack = ms.mStream->FindOwnedDOMTrack(
    2300           0 :           ms.mTrackPorts[i].second()->GetDestination(),
    2301           0 :           ms.mTrackPorts[i].second()->GetDestinationTrackId());
    2302           0 :         MOZ_ASSERT(outputTrack);
    2303           0 :         if (outputTrack) {
    2304           0 :           mMainThreadEventTarget->Dispatch(
    2305           0 :             NewRunnableMethod("MediaStreamTrack::OverrideEnded",
    2306             :                               outputTrack,
    2307           0 :                               &MediaStreamTrack::OverrideEnded));
    2308             :         }
    2309             : 
    2310           0 :         ms.mTrackPorts[i].second()->Destroy();
    2311           0 :         ms.mTrackPorts.RemoveElementAt(i);
    2312             :         break;
    2313             :       }
    2314             :     }
    2315             : #ifdef DEBUG
    2316           0 :     for (auto pair : ms.mTrackPorts) {
    2317           0 :       MOZ_ASSERT(pair.first() != aTrack->GetId(),
    2318             :                  "The same MediaTrack was forwarded to the output stream more "
    2319             :                  "than once. This shouldn't happen.");
    2320             :     }
    2321             : #endif
    2322             :   }
    2323             : }
    2324             : 
    2325             : void
    2326           0 : HTMLMediaElement::NotifyMediaStreamTracksAvailable(DOMMediaStream* aStream)
    2327             : {
    2328           0 :   if (!mSrcStream || mSrcStream != aStream) {
    2329             :     return;
    2330             :   }
    2331             : 
    2332           0 :   LOG(LogLevel::Debug, ("MediaElement %p MediaStream tracks available", this));
    2333             : 
    2334           0 :   mSrcStreamTracksAvailable = true;
    2335             : 
    2336           0 :   bool videoHasChanged = IsVideo() && HasVideo() != !VideoTracks()->IsEmpty();
    2337             : 
    2338           0 :   if (videoHasChanged) {
    2339             :     // We are a video element and HasVideo() changed so update the screen
    2340             :     // wakelock
    2341           0 :     NotifyOwnerDocumentActivityChanged();
    2342             :   }
    2343             : 
    2344           0 :   UpdateReadyStateInternal();
    2345             : }
    2346             : 
    2347             : void
    2348           0 : HTMLMediaElement::DealWithFailedElement(nsIContent* aSourceElement)
    2349             : {
    2350           0 :   if (mShuttingDown) {
    2351             :     return;
    2352             :   }
    2353             : 
    2354           0 :   DispatchAsyncSourceError(aSourceElement);
    2355           0 :   mMainThreadEventTarget->Dispatch(
    2356           0 :     NewRunnableMethod("HTMLMediaElement::QueueLoadFromSourceTask",
    2357             :                       this,
    2358           0 :                       &HTMLMediaElement::QueueLoadFromSourceTask));
    2359             : }
    2360             : 
    2361             : void
    2362           0 : HTMLMediaElement::NotifyOutputTrackStopped(DOMMediaStream* aOwningStream,
    2363             :                                            TrackID aDestinationTrackID)
    2364             : {
    2365           0 :   for (OutputMediaStream& ms : mOutputStreams) {
    2366           0 :     if (!ms.mCapturingMediaStream) {
    2367             :       continue;
    2368             :     }
    2369             : 
    2370           0 :     if (ms.mStream != aOwningStream) {
    2371             :       continue;
    2372             :     }
    2373             : 
    2374           0 :     for (int32_t i = ms.mTrackPorts.Length() - 1; i >= 0; --i) {
    2375           0 :       MediaInputPort* port = ms.mTrackPorts[i].second();
    2376           0 :       if (port->GetDestinationTrackId() != aDestinationTrackID) {
    2377             :         continue;
    2378             :       }
    2379             : 
    2380           0 :       port->Destroy();
    2381           0 :       ms.mTrackPorts.RemoveElementAt(i);
    2382           0 :       return;
    2383             :     }
    2384             :   }
    2385             : 
    2386             :   // An output track ended but its port is already gone.
    2387             :   // It was probably cleared by the removal of the source MediaTrack.
    2388             : }
    2389             : 
    2390             : void
    2391           0 : HTMLMediaElement::LoadFromSourceChildren()
    2392             : {
    2393           0 :   NS_ASSERTION(mDelayingLoadEvent,
    2394             :                "Should delay load event (if in document) during load");
    2395           0 :   NS_ASSERTION(mIsLoadingFromSourceChildren,
    2396             :                "Must remember we're loading from source children");
    2397             : 
    2398           0 :   AddMutationObserverUnlessExists(this);
    2399             : 
    2400             :   while (true) {
    2401           0 :     Element* child = GetNextSource();
    2402           0 :     if (!child) {
    2403             :       // Exhausted candidates, wait for more candidates to be appended to
    2404             :       // the media element.
    2405           0 :       mLoadWaitStatus = WAITING_FOR_SOURCE;
    2406           0 :       ChangeNetworkState(NETWORK_NO_SOURCE);
    2407           0 :       ChangeDelayLoadStatus(false);
    2408           0 :       ReportLoadError("MediaLoadExhaustedCandidates");
    2409           0 :       return;
    2410             :     }
    2411             : 
    2412             :     // Must have src attribute.
    2413           0 :     nsAutoString src;
    2414           0 :     if (!child->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
    2415           0 :       ReportLoadError("MediaLoadSourceMissingSrc");
    2416           0 :       DealWithFailedElement(child);
    2417           0 :       return;
    2418             :     }
    2419             : 
    2420             :     // If we have a type attribute, it must be a supported type.
    2421           0 :     nsAutoString type;
    2422           0 :     if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
    2423           0 :       DecoderDoctorDiagnostics diagnostics;
    2424           0 :       CanPlayStatus canPlay = GetCanPlay(type, &diagnostics);
    2425           0 :       diagnostics.StoreFormatDiagnostics(
    2426           0 :         OwnerDoc(), type, canPlay != CANPLAY_NO, __func__);
    2427           0 :       if (canPlay == CANPLAY_NO) {
    2428           0 :         const char16_t* params[] = { type.get(), src.get() };
    2429             :         ReportLoadError(
    2430           0 :           "MediaLoadUnsupportedTypeAttribute", params, ArrayLength(params));
    2431           0 :         DealWithFailedElement(child);
    2432             :         return;
    2433             :       }
    2434             :     }
    2435           0 :     HTMLSourceElement* childSrc = HTMLSourceElement::FromNode(child);
    2436           0 :     LOG(LogLevel::Debug,
    2437             :         ("%p Trying load from <source>=%s type=%s",
    2438             :          this,
    2439             :          NS_ConvertUTF16toUTF8(src).get(),
    2440             :          NS_ConvertUTF16toUTF8(type).get()));
    2441             : 
    2442           0 :     nsCOMPtr<nsIURI> uri;
    2443           0 :     NewURIFromString(src, getter_AddRefs(uri));
    2444           0 :     if (!uri) {
    2445           0 :       const char16_t* params[] = { src.get() };
    2446           0 :       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
    2447           0 :       DealWithFailedElement(child);
    2448             :       return;
    2449             :     }
    2450             : 
    2451           0 :     RemoveMediaElementFromURITable();
    2452           0 :     mLoadingSrc = uri;
    2453           0 :     mLoadingSrcTriggeringPrincipal = childSrc->GetSrcTriggeringPrincipal();
    2454           0 :     DDLOG(DDLogCategory::Property,
    2455             :           "loading_src",
    2456             :           nsCString(NS_ConvertUTF16toUTF8(src)));
    2457           0 :     mMediaSource = childSrc->GetSrcMediaSource();
    2458           0 :     DDLINKCHILD("mediasource", mMediaSource.get());
    2459           0 :     NS_ASSERTION(mNetworkState == NETWORK_LOADING,
    2460             :                  "Network state should be loading");
    2461             : 
    2462           0 :     if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE && !mMediaSource) {
    2463             :       // preload:none media, suspend the load here before we make any
    2464             :       // network requests.
    2465           0 :       SuspendLoad();
    2466           0 :       return;
    2467             :     }
    2468             : 
    2469           0 :     if (NS_SUCCEEDED(LoadResource())) {
    2470             :       return;
    2471             :     }
    2472             : 
    2473             :     // If we fail to load, loop back and try loading the next resource.
    2474           0 :     DispatchAsyncSourceError(child);
    2475             :   }
    2476             :   NS_NOTREACHED("Execution should not reach here!");
    2477             : }
    2478             : 
    2479             : void
    2480           0 : HTMLMediaElement::SuspendLoad()
    2481             : {
    2482           0 :   mSuspendedForPreloadNone = true;
    2483           0 :   ChangeNetworkState(NETWORK_IDLE);
    2484           0 :   ChangeDelayLoadStatus(false);
    2485           0 : }
    2486             : 
    2487             : void
    2488           0 : HTMLMediaElement::ResumeLoad(PreloadAction aAction)
    2489             : {
    2490           0 :   NS_ASSERTION(mSuspendedForPreloadNone,
    2491             :                "Must be halted for preload:none to resume from preload:none "
    2492             :                "suspended load.");
    2493           0 :   mSuspendedForPreloadNone = false;
    2494           0 :   mPreloadAction = aAction;
    2495           0 :   ChangeDelayLoadStatus(true);
    2496           0 :   ChangeNetworkState(NETWORK_LOADING);
    2497           0 :   if (!mIsLoadingFromSourceChildren) {
    2498             :     // We were loading from the element's src attribute.
    2499           0 :     MediaResult rv = LoadResource();
    2500           0 :     if (NS_FAILED(rv)) {
    2501           0 :       NoSupportedMediaSourceError(rv.Description());
    2502             :     }
    2503             :   } else {
    2504             :     // We were loading from a child <source> element. Try to resume the
    2505             :     // load of that child, and if that fails, try the next child.
    2506           0 :     if (NS_FAILED(LoadResource())) {
    2507           0 :       LoadFromSourceChildren();
    2508             :     }
    2509             :   }
    2510           0 : }
    2511             : 
    2512             : void
    2513           1 : HTMLMediaElement::UpdatePreloadAction()
    2514             : {
    2515           1 :   PreloadAction nextAction = PRELOAD_UNDEFINED;
    2516             :   // If autoplay is set, or we're playing, we should always preload data,
    2517             :   // as we'll need it to play.
    2518           3 :   if ((AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this)) &&
    2519           2 :        HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
    2520           2 :       !mPaused) {
    2521             :     nextAction = HTMLMediaElement::PRELOAD_ENOUGH;
    2522             :   } else {
    2523             :     // Find the appropriate preload action by looking at the attribute.
    2524             :     const nsAttrValue* val =
    2525           0 :       mAttrsAndChildren.GetAttr(nsGkAtoms::preload, kNameSpaceID_None);
    2526             :     // MSE doesn't work if preload is none, so it ignores the pref when src is
    2527             :     // from MSE.
    2528             :     uint32_t preloadDefault =
    2529             :       mMediaSource
    2530           2 :         ? HTMLMediaElement::PRELOAD_ATTR_METADATA
    2531           0 :         : Preferences::GetInt("media.preload.default",
    2532           3 :                               HTMLMediaElement::PRELOAD_ATTR_METADATA);
    2533           0 :     uint32_t preloadAuto = Preferences::GetInt(
    2534           0 :       "media.preload.auto", HTMLMediaElement::PRELOAD_ENOUGH);
    2535           0 :     if (!val) {
    2536             :       // Attribute is not set. Use the preload action specified by the
    2537             :       // media.preload.default pref, or just preload metadata if not present.
    2538             :       nextAction = static_cast<PreloadAction>(preloadDefault);
    2539           0 :     } else if (val->Type() == nsAttrValue::eEnum) {
    2540             :       PreloadAttrValue attr =
    2541           0 :         static_cast<PreloadAttrValue>(val->GetEnumValue());
    2542           0 :       if (attr == HTMLMediaElement::PRELOAD_ATTR_EMPTY ||
    2543           0 :           attr == HTMLMediaElement::PRELOAD_ATTR_AUTO) {
    2544             :         nextAction = static_cast<PreloadAction>(preloadAuto);
    2545           0 :       } else if (attr == HTMLMediaElement::PRELOAD_ATTR_METADATA) {
    2546             :         nextAction = HTMLMediaElement::PRELOAD_METADATA;
    2547           0 :       } else if (attr == HTMLMediaElement::PRELOAD_ATTR_NONE) {
    2548           0 :         nextAction = HTMLMediaElement::PRELOAD_NONE;
    2549             :       }
    2550             :     } else {
    2551             :       // Use the suggested "missing value default" of "metadata", or the value
    2552             :       // specified by the media.preload.default, if present.
    2553             :       nextAction = static_cast<PreloadAction>(preloadDefault);
    2554             :     }
    2555             :   }
    2556             : 
    2557           1 :   if (nextAction == HTMLMediaElement::PRELOAD_NONE && mIsDoingExplicitLoad) {
    2558           0 :     nextAction = HTMLMediaElement::PRELOAD_METADATA;
    2559             :   }
    2560             : 
    2561           1 :   mPreloadAction = nextAction;
    2562             : 
    2563           1 :   if (nextAction == HTMLMediaElement::PRELOAD_ENOUGH) {
    2564           0 :     if (mSuspendedForPreloadNone) {
    2565             :       // Our load was previouly suspended due to the media having preload
    2566             :       // value "none". The preload value has changed to preload:auto, so
    2567             :       // resume the load.
    2568           0 :       ResumeLoad(PRELOAD_ENOUGH);
    2569             :     } else {
    2570             :       // Preload as much of the video as we can, i.e. don't suspend after
    2571             :       // the first frame.
    2572           0 :       StopSuspendingAfterFirstFrame();
    2573             :     }
    2574             : 
    2575           0 :   } else if (nextAction == HTMLMediaElement::PRELOAD_METADATA) {
    2576             :     // Ensure that the video can be suspended after first frame.
    2577           1 :     mAllowSuspendAfterFirstFrame = true;
    2578           0 :     if (mSuspendedForPreloadNone) {
    2579             :       // Our load was previouly suspended due to the media having preload
    2580             :       // value "none". The preload value has changed to preload:metadata, so
    2581             :       // resume the load. We'll pause the load again after we've read the
    2582             :       // metadata.
    2583           0 :       ResumeLoad(PRELOAD_METADATA);
    2584             :     }
    2585             :   }
    2586           1 : }
    2587             : 
    2588             : MediaResult
    2589           0 : HTMLMediaElement::LoadResource()
    2590             : {
    2591           0 :   AbstractThread::AutoEnter context(AbstractMainThread());
    2592             : 
    2593           0 :   NS_ASSERTION(mDelayingLoadEvent,
    2594             :                "Should delay load event (if in document) during load");
    2595             : 
    2596           0 :   if (mChannelLoader) {
    2597           0 :     mChannelLoader->Cancel();
    2598           0 :     mChannelLoader = nullptr;
    2599             :   }
    2600             : 
    2601             :   // Set the media element's CORS mode only when loading a resource
    2602           0 :   mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
    2603             : 
    2604           0 :   HTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
    2605           0 :   if (other && other->mDecoder) {
    2606             :     // Clone it.
    2607             :     // TODO: remove the cast by storing ChannelMediaDecoder in the URI table.
    2608             :     nsresult rv = InitializeDecoderAsClone(
    2609           0 :       static_cast<ChannelMediaDecoder*>(other->mDecoder.get()));
    2610           0 :     if (NS_SUCCEEDED(rv))
    2611             :       return rv;
    2612             :   }
    2613             : 
    2614           0 :   if (mMediaSource) {
    2615             :     MediaDecoderInit decoderInit(
    2616             :       this,
    2617           0 :       mMuted ? 0.0 : mVolume,
    2618           0 :       mPreservesPitch,
    2619             :       mPlaybackRate,
    2620           0 :       mPreloadAction == HTMLMediaElement::PRELOAD_METADATA,
    2621           0 :       mHasSuspendTaint,
    2622           0 :       HasAttr(kNameSpaceID_None, nsGkAtoms::loop),
    2623           0 :       MediaContainerType(MEDIAMIMETYPE("application/x.mediasource")));
    2624             : 
    2625           0 :     RefPtr<MediaSourceDecoder> decoder = new MediaSourceDecoder(decoderInit);
    2626           0 :     if (!mMediaSource->Attach(decoder)) {
    2627             :       // TODO: Handle failure: run "If the media data cannot be fetched at
    2628             :       // all, due to network errors, causing the user agent to give up
    2629             :       // trying to fetch the resource" section of resource fetch algorithm.
    2630           0 :       decoder->Shutdown();
    2631           0 :       return MediaResult(NS_ERROR_FAILURE, "Failed to attach MediaSource");
    2632             :     }
    2633           0 :     ChangeDelayLoadStatus(false);
    2634           0 :     nsresult rv = decoder->Load(mMediaSource->GetPrincipal());
    2635           0 :     if (NS_FAILED(rv)) {
    2636           0 :       decoder->Shutdown();
    2637           0 :       LOG(LogLevel::Debug,
    2638             :           ("%p Failed to load for decoder %p", this, decoder.get()));
    2639           0 :       return MediaResult(rv, "Fail to load decoder");
    2640             :     }
    2641           0 :     rv = FinishDecoderSetup(decoder);
    2642           0 :     return MediaResult(rv, "Failed to set up decoder");
    2643             :   }
    2644             : 
    2645           0 :   AssertReadyStateIsNothing();
    2646             : 
    2647           0 :   RefPtr<ChannelLoader> loader = new ChannelLoader;
    2648           0 :   nsresult rv = loader->Load(this);
    2649           0 :   if (NS_SUCCEEDED(rv)) {
    2650           0 :     mChannelLoader = loader.forget();
    2651             :   }
    2652           0 :   return MediaResult(rv, "Failed to load channel");
    2653             : }
    2654             : 
    2655             : nsresult
    2656           0 : HTMLMediaElement::LoadWithChannel(nsIChannel* aChannel,
    2657             :                                   nsIStreamListener** aListener)
    2658             : {
    2659           0 :   NS_ENSURE_ARG_POINTER(aChannel);
    2660           0 :   NS_ENSURE_ARG_POINTER(aListener);
    2661             : 
    2662           0 :   *aListener = nullptr;
    2663             : 
    2664             :   // Make sure we don't reenter during synchronous abort events.
    2665           0 :   if (mIsRunningLoadMethod)
    2666             :     return NS_OK;
    2667           0 :   mIsRunningLoadMethod = true;
    2668           0 :   AbortExistingLoads();
    2669           0 :   mIsRunningLoadMethod = false;
    2670             : 
    2671           0 :   mLoadingSrcTriggeringPrincipal = nullptr;
    2672           0 :   nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(mLoadingSrc));
    2673           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2674             : 
    2675           0 :   ChangeDelayLoadStatus(true);
    2676           0 :   rv = InitializeDecoderForChannel(aChannel, aListener);
    2677           0 :   if (NS_FAILED(rv)) {
    2678           0 :     ChangeDelayLoadStatus(false);
    2679           0 :     return rv;
    2680             :   }
    2681             : 
    2682           0 :   SetPlaybackRate(mDefaultPlaybackRate, IgnoreErrors());
    2683           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
    2684             : 
    2685           0 :   return NS_OK;
    2686             : }
    2687             : 
    2688             : bool
    2689           0 : HTMLMediaElement::Seeking() const
    2690             : {
    2691           0 :   return mDecoder && mDecoder->IsSeeking();
    2692             : }
    2693             : 
    2694             : double
    2695           0 : HTMLMediaElement::CurrentTime() const
    2696             : {
    2697           0 :   if (MediaStream* stream = GetSrcMediaStream()) {
    2698           0 :     if (mSrcStreamPausedCurrentTime >= 0) {
    2699             :       return mSrcStreamPausedCurrentTime;
    2700             :     }
    2701           0 :     return stream->StreamTimeToSeconds(stream->GetCurrentTime());
    2702             :   }
    2703             : 
    2704           0 :   if (mDefaultPlaybackStartPosition == 0.0 && mDecoder) {
    2705           0 :     return mDecoder->GetCurrentTime();
    2706             :   }
    2707             : 
    2708             :   return mDefaultPlaybackStartPosition;
    2709             : }
    2710             : 
    2711             : void
    2712           0 : HTMLMediaElement::FastSeek(double aTime, ErrorResult& aRv)
    2713             : {
    2714           0 :   LOG(LogLevel::Debug, ("%p FastSeek(%f) called by JS", this, aTime));
    2715           0 :   LOG(LogLevel::Debug, ("Reporting telemetry VIDEO_FASTSEEK_USED"));
    2716           0 :   Telemetry::Accumulate(Telemetry::VIDEO_FASTSEEK_USED, 1);
    2717           0 :   RefPtr<Promise> tobeDropped = Seek(aTime, SeekTarget::PrevSyncPoint, aRv);
    2718           0 : }
    2719             : 
    2720             : already_AddRefed<Promise>
    2721           0 : HTMLMediaElement::SeekToNextFrame(ErrorResult& aRv)
    2722             : {
    2723           0 :   if (mSeekDOMPromise) {
    2724             :     // We can't perform NextFrameSeek while seek is already in action.
    2725             :     // Just return the pending seek promise.
    2726           0 :     return do_AddRef(mSeekDOMPromise);
    2727             :   }
    2728             : 
    2729             :   /* This will cause JIT code to be kept around longer, to help performance
    2730             :    * when using SeekToNextFrame to iterate through every frame of a video.
    2731             :    */
    2732           0 :   nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
    2733             : 
    2734           0 :   if (win) {
    2735           0 :     if (JSObject* obj = win->AsGlobal()->GetGlobalJSObject()) {
    2736           0 :       js::NotifyAnimationActivity(obj);
    2737             :     }
    2738             :   }
    2739             : 
    2740           0 :   return Seek(CurrentTime(), SeekTarget::NextFrame, aRv);
    2741             : }
    2742             : 
    2743             : void
    2744           0 : HTMLMediaElement::SetCurrentTime(double aCurrentTime, ErrorResult& aRv)
    2745             : {
    2746           0 :   LOG(LogLevel::Debug,
    2747             :       ("%p SetCurrentTime(%f) called by JS", this, aCurrentTime));
    2748           0 :   RefPtr<Promise> tobeDropped = Seek(aCurrentTime, SeekTarget::Accurate, aRv);
    2749           0 : }
    2750             : 
    2751             : /**
    2752             :  * Check if aValue is inside a range of aRanges, and if so returns true
    2753             :  * and puts the range index in aIntervalIndex. If aValue is not
    2754             :  * inside a range, returns false, and aIntervalIndex
    2755             :  * is set to the index of the range which starts immediately after aValue
    2756             :  * (and can be aRanges.Length() if aValue is after the last range).
    2757             :  */
    2758             : static bool
    2759           0 : IsInRanges(TimeRanges& aRanges, double aValue, uint32_t& aIntervalIndex)
    2760             : {
    2761           0 :   uint32_t length = aRanges.Length();
    2762             : 
    2763           0 :   for (uint32_t i = 0; i < length; i++) {
    2764           0 :     double start = aRanges.Start(i);
    2765           0 :     if (start > aValue) {
    2766           0 :       aIntervalIndex = i;
    2767           0 :       return false;
    2768             :     }
    2769           0 :     double end = aRanges.End(i);
    2770           0 :     if (aValue <= end) {
    2771           0 :       aIntervalIndex = i;
    2772           0 :       return true;
    2773             :     }
    2774             :   }
    2775           0 :   aIntervalIndex = length;
    2776           0 :   return false;
    2777             : }
    2778             : 
    2779             : already_AddRefed<Promise>
    2780           0 : HTMLMediaElement::Seek(double aTime,
    2781             :                        SeekTarget::Type aSeekType,
    2782             :                        ErrorResult& aRv)
    2783             : {
    2784             :   // aTime should be non-NaN.
    2785           0 :   MOZ_ASSERT(!mozilla::IsNaN(aTime));
    2786             : 
    2787           0 :   RefPtr<Promise> promise = CreateDOMPromise(aRv);
    2788             : 
    2789           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2790             :     return nullptr;
    2791             :   }
    2792             : 
    2793             :   // Detect if user has interacted with element by seeking so that
    2794             :   // play will not be blocked when initiated by a script.
    2795           0 :   if (EventStateManager::IsHandlingUserInput()) {
    2796           0 :     mIsBlessed = true;
    2797             :   }
    2798             : 
    2799           0 :   StopSuspendingAfterFirstFrame();
    2800             : 
    2801           0 :   if (mSrcStream) {
    2802             :     // do nothing since media streams have an empty Seekable range.
    2803           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    2804             :     return promise.forget();
    2805             :   }
    2806             : 
    2807           0 :   if (mPlayed && mCurrentPlayRangeStart != -1.0) {
    2808           0 :     double rangeEndTime = CurrentTime();
    2809           0 :     LOG(LogLevel::Debug,
    2810             :         ("%p Adding \'played\' a range : [%f, %f]",
    2811             :          this,
    2812             :          mCurrentPlayRangeStart,
    2813             :          rangeEndTime));
    2814             :     // Multiple seek without playing, or seek while playing.
    2815           0 :     if (mCurrentPlayRangeStart != rangeEndTime) {
    2816           0 :       mPlayed->Add(mCurrentPlayRangeStart, rangeEndTime);
    2817             :     }
    2818             :     // Reset the current played range start time. We'll re-set it once
    2819             :     // the seek completes.
    2820           0 :     mCurrentPlayRangeStart = -1.0;
    2821             :   }
    2822             : 
    2823           0 :   if (mReadyState == HAVE_NOTHING) {
    2824           0 :     mDefaultPlaybackStartPosition = aTime;
    2825           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    2826             :     return promise.forget();
    2827             :   }
    2828             : 
    2829           0 :   if (!mDecoder) {
    2830             :     // mDecoder must always be set in order to reach this point.
    2831           0 :     NS_ASSERTION(mDecoder, "SetCurrentTime failed: no decoder");
    2832           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    2833             :     return promise.forget();
    2834             :   }
    2835             : 
    2836             :   // Clamp the seek target to inside the seekable ranges.
    2837           0 :   media::TimeIntervals seekableIntervals = mDecoder->GetSeekable();
    2838           0 :   if (seekableIntervals.IsInvalid()) {
    2839           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); // This will reject the promise.
    2840             :     return promise.forget();
    2841             :   }
    2842             :   RefPtr<TimeRanges> seekable =
    2843           0 :     new TimeRanges(ToSupports(OwnerDoc()), seekableIntervals);
    2844           0 :   uint32_t length = seekable->Length();
    2845           0 :   if (length == 0) {
    2846           0 :     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    2847             :     return promise.forget();
    2848             :   }
    2849             : 
    2850             :   // If the position we want to seek to is not in a seekable range, we seek
    2851             :   // to the closest position in the seekable ranges instead. If two positions
    2852             :   // are equally close, we seek to the closest position from the currentTime.
    2853             :   // See seeking spec, point 7 :
    2854             :   // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#seeking
    2855           0 :   uint32_t range = 0;
    2856           0 :   bool isInRange = IsInRanges(*seekable, aTime, range);
    2857           0 :   if (!isInRange) {
    2858           0 :     if (range == 0) {
    2859             :       // aTime is before the first range in |seekable|, the closest point we can
    2860             :       // seek to is the start of the first range.
    2861           0 :       aTime = seekable->Start(0);
    2862           0 :     } else if (range == length) {
    2863             :       // Seek target is after the end last range in seekable data.
    2864             :       // Clamp the seek target to the end of the last seekable range.
    2865           0 :       aTime = seekable->End(length - 1);
    2866             :     } else {
    2867           0 :       double leftBound = seekable->End(range - 1);
    2868           0 :       double rightBound = seekable->Start(range);
    2869           0 :       double distanceLeft = Abs(leftBound - aTime);
    2870           0 :       double distanceRight = Abs(rightBound - aTime);
    2871           0 :       if (distanceLeft == distanceRight) {
    2872           0 :         double currentTime = CurrentTime();
    2873           0 :         distanceLeft = Abs(leftBound - currentTime);
    2874           0 :         distanceRight = Abs(rightBound - currentTime);
    2875             :       }
    2876           0 :       aTime = (distanceLeft < distanceRight) ? leftBound : rightBound;
    2877             :     }
    2878             :   }
    2879             : 
    2880             :   // TODO: The spec requires us to update the current time to reflect the
    2881             :   //       actual seek target before beginning the synchronous section, but
    2882             :   //       that requires changing all MediaDecoderReaders to support telling
    2883             :   //       us the fastSeek target, and it's currently not possible to get
    2884             :   //       this information as we don't yet control the demuxer for all
    2885             :   //       MediaDecoderReaders.
    2886             : 
    2887           0 :   mPlayingBeforeSeek = IsPotentiallyPlaying();
    2888             : 
    2889             :   // The media backend is responsible for dispatching the timeupdate
    2890             :   // event if it changes the playback position as a result of the seek.
    2891           0 :   LOG(LogLevel::Debug, ("%p SetCurrentTime(%f) starting seek", this, aTime));
    2892           0 :   mDecoder->Seek(aTime, aSeekType);
    2893             : 
    2894             :   // We changed whether we're seeking so we need to AddRemoveSelfReference.
    2895           0 :   AddRemoveSelfReference();
    2896             : 
    2897             :   // Keep the DOM promise.
    2898           0 :   mSeekDOMPromise = promise;
    2899             : 
    2900             :   return promise.forget();
    2901             : }
    2902             : 
    2903             : double
    2904           0 : HTMLMediaElement::Duration() const
    2905             : {
    2906           0 :   if (mSrcStream) {
    2907             :     return std::numeric_limits<double>::infinity();
    2908             :   }
    2909             : 
    2910           0 :   if (mDecoder) {
    2911           0 :     return mDecoder->GetDuration();
    2912             :   }
    2913             : 
    2914             :   return std::numeric_limits<double>::quiet_NaN();
    2915             : }
    2916             : 
    2917             : already_AddRefed<TimeRanges>
    2918           0 : HTMLMediaElement::Seekable() const
    2919             : {
    2920             :   media::TimeIntervals seekable =
    2921           0 :     mDecoder ? mDecoder->GetSeekable() : media::TimeIntervals();
    2922           0 :   RefPtr<TimeRanges> ranges = new TimeRanges(ToSupports(OwnerDoc()), seekable);
    2923           0 :   return ranges.forget();
    2924             : }
    2925             : 
    2926             : already_AddRefed<TimeRanges>
    2927           0 : HTMLMediaElement::Played()
    2928             : {
    2929           0 :   RefPtr<TimeRanges> ranges = new TimeRanges(ToSupports(OwnerDoc()));
    2930             : 
    2931           0 :   uint32_t timeRangeCount = 0;
    2932           0 :   if (mPlayed) {
    2933           0 :     timeRangeCount = mPlayed->Length();
    2934             :   }
    2935           0 :   for (uint32_t i = 0; i < timeRangeCount; i++) {
    2936           0 :     double begin = mPlayed->Start(i);
    2937           0 :     double end = mPlayed->End(i);
    2938           0 :     ranges->Add(begin, end);
    2939             :   }
    2940             : 
    2941           0 :   if (mCurrentPlayRangeStart != -1.0) {
    2942           0 :     double now = CurrentTime();
    2943           0 :     if (mCurrentPlayRangeStart != now) {
    2944           0 :       ranges->Add(mCurrentPlayRangeStart, now);
    2945             :     }
    2946             :   }
    2947             : 
    2948           0 :   ranges->Normalize();
    2949           0 :   return ranges.forget();
    2950             : }
    2951             : 
    2952             : void
    2953           0 : HTMLMediaElement::Pause(ErrorResult& aRv)
    2954             : {
    2955           0 :   LOG(LogLevel::Debug, ("%p Pause() called by JS", this));
    2956           0 :   if (mNetworkState == NETWORK_EMPTY) {
    2957           0 :     LOG(LogLevel::Debug, ("Loading due to Pause()"));
    2958           0 :     DoLoad();
    2959           0 :   } else if (mDecoder) {
    2960           0 :     mDecoder->Pause();
    2961             :   }
    2962             : 
    2963           0 :   bool oldPaused = mPaused;
    2964           0 :   mPaused = true;
    2965           0 :   mAutoplaying = false;
    2966             :   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
    2967           0 :   AddRemoveSelfReference();
    2968           0 :   UpdateSrcMediaStreamPlaying();
    2969           0 :   if (mAudioChannelWrapper) {
    2970           0 :     mAudioChannelWrapper->NotifyPlayStateChanged();
    2971             :   }
    2972             : 
    2973           0 :   if (!oldPaused) {
    2974           0 :     FireTimeUpdate(false);
    2975           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
    2976           0 :     AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_ABORT_ERR);
    2977             :   }
    2978           0 : }
    2979             : 
    2980             : void
    2981           1 : HTMLMediaElement::SetVolume(double aVolume, ErrorResult& aRv)
    2982             : {
    2983           1 :   LOG(LogLevel::Debug, ("%p SetVolume(%f) called by JS", this, aVolume));
    2984             : 
    2985           0 :   if (aVolume < 0.0 || aVolume > 1.0) {
    2986           0 :     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
    2987           0 :     return;
    2988             :   }
    2989             : 
    2990           0 :   if (aVolume == mVolume)
    2991             :     return;
    2992             : 
    2993           0 :   mVolume = aVolume;
    2994             : 
    2995             :   // Here we want just to update the volume.
    2996           0 :   SetVolumeInternal();
    2997             : 
    2998           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
    2999             : 
    3000             :   // We allow inaudible autoplay. But changing our volume may make this
    3001             :   // media audible. So pause if we are no longer supposed to be autoplaying.
    3002           0 :   PauseIfShouldNotBePlaying();
    3003             : }
    3004             : 
    3005             : void
    3006           0 : HTMLMediaElement::MozGetMetadata(JSContext* cx,
    3007             :                                  JS::MutableHandle<JSObject*> aRetval,
    3008             :                                  ErrorResult& aRv)
    3009             : {
    3010           0 :   if (mReadyState < HAVE_METADATA) {
    3011           0 :     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    3012           0 :     return;
    3013             :   }
    3014             : 
    3015           0 :   JS::Rooted<JSObject*> tags(cx, JS_NewPlainObject(cx));
    3016           0 :   if (!tags) {
    3017           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3018           0 :     return;
    3019             :   }
    3020           0 :   if (mTags) {
    3021           0 :     for (auto iter = mTags->ConstIter(); !iter.Done(); iter.Next()) {
    3022           0 :       nsString wideValue;
    3023           0 :       CopyUTF8toUTF16(iter.UserData(), wideValue);
    3024             :       JS::Rooted<JSString*> string(cx,
    3025           0 :                                    JS_NewUCStringCopyZ(cx, wideValue.Data()));
    3026           0 :       if (!string || !JS_DefineProperty(
    3027           0 :                        cx, tags, iter.Key().Data(), string, JSPROP_ENUMERATE)) {
    3028           0 :         NS_WARNING("couldn't create metadata object!");
    3029           0 :         aRv.Throw(NS_ERROR_FAILURE);
    3030           0 :         return;
    3031             :       }
    3032             :     }
    3033             :   }
    3034             : 
    3035           0 :   aRetval.set(tags);
    3036             : }
    3037             : 
    3038             : void
    3039           0 : HTMLMediaElement::SetMutedInternal(uint32_t aMuted)
    3040             : {
    3041           0 :   uint32_t oldMuted = mMuted;
    3042           0 :   mMuted = aMuted;
    3043             : 
    3044           0 :   if (!!aMuted == !!oldMuted) {
    3045             :     return;
    3046             :   }
    3047             : 
    3048           0 :   SetVolumeInternal();
    3049             : }
    3050             : 
    3051             : void
    3052           0 : HTMLMediaElement::PauseIfShouldNotBePlaying()
    3053             : {
    3054           0 :   if (GetPaused()) {
    3055             :     return;
    3056             :   }
    3057           0 :   if (!AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this))) {
    3058           0 :     ErrorResult rv;
    3059           0 :     Pause(rv);
    3060             :   }
    3061             : }
    3062             : 
    3063             : void
    3064           0 : HTMLMediaElement::SetVolumeInternal()
    3065             : {
    3066           0 :   float effectiveVolume = ComputedVolume();
    3067             : 
    3068           0 :   if (mDecoder) {
    3069           0 :     mDecoder->SetVolume(effectiveVolume);
    3070           0 :   } else if (MediaStream* stream = GetSrcMediaStream()) {
    3071           0 :     if (mSrcStreamIsPlaying) {
    3072           0 :       stream->SetAudioOutputVolume(this, effectiveVolume);
    3073             :     }
    3074             :   }
    3075             : 
    3076             :   NotifyAudioPlaybackChanged(
    3077           0 :     AudioChannelService::AudibleChangedReasons::eVolumeChanged);
    3078           0 : }
    3079             : 
    3080             : void
    3081           0 : HTMLMediaElement::SetMuted(bool aMuted)
    3082             : {
    3083           0 :   LOG(LogLevel::Debug, ("%p SetMuted(%d) called by JS", this, aMuted));
    3084           0 :   if (aMuted == Muted()) {
    3085             :     return;
    3086             :   }
    3087             : 
    3088           0 :   if (aMuted) {
    3089           0 :     SetMutedInternal(mMuted | MUTED_BY_CONTENT);
    3090             :   } else {
    3091           0 :     SetMutedInternal(mMuted & ~MUTED_BY_CONTENT);
    3092             :   }
    3093             : 
    3094           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
    3095             : 
    3096             :   // We allow inaudible autoplay. But changing our mute status may make this
    3097             :   // media audible. So pause if we are no longer supposed to be autoplaying.
    3098           0 :   PauseIfShouldNotBePlaying();
    3099             : }
    3100             : 
    3101             : class HTMLMediaElement::StreamCaptureTrackSource
    3102             :   : public MediaStreamTrackSource
    3103             :   , public MediaStreamTrackSource::Sink
    3104             : {
    3105             : public:
    3106             :   NS_DECL_ISUPPORTS_INHERITED
    3107           0 :   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(StreamCaptureTrackSource,
    3108             :                                            MediaStreamTrackSource)
    3109             : 
    3110           0 :   StreamCaptureTrackSource(HTMLMediaElement* aElement,
    3111             :                            MediaStreamTrackSource* aCapturedTrackSource,
    3112             :                            DOMMediaStream* aOwningStream,
    3113             :                            TrackID aDestinationTrackID)
    3114           0 :     : MediaStreamTrackSource(aCapturedTrackSource->GetPrincipal(), nsString())
    3115             :     , mElement(aElement)
    3116             :     , mCapturedTrackSource(aCapturedTrackSource)
    3117             :     , mOwningStream(aOwningStream)
    3118           0 :     , mDestinationTrackID(aDestinationTrackID)
    3119             :   {
    3120           0 :     MOZ_ASSERT(mElement);
    3121           0 :     MOZ_ASSERT(mCapturedTrackSource);
    3122           0 :     MOZ_ASSERT(mOwningStream);
    3123           0 :     MOZ_ASSERT(IsTrackIDExplicit(mDestinationTrackID));
    3124             : 
    3125           0 :     mCapturedTrackSource->RegisterSink(this);
    3126           0 :   }
    3127             : 
    3128           0 :   void Destroy() override
    3129             :   {
    3130           0 :     if (mCapturedTrackSource) {
    3131           0 :       mCapturedTrackSource->UnregisterSink(this);
    3132           0 :       mCapturedTrackSource = nullptr;
    3133             :     }
    3134           0 :   }
    3135             : 
    3136           0 :   MediaSourceEnum GetMediaSource() const override
    3137             :   {
    3138           0 :     return MediaSourceEnum::Other;
    3139             :   }
    3140             : 
    3141           0 :   CORSMode GetCORSMode() const override
    3142             :   {
    3143           0 :     if (!mCapturedTrackSource) {
    3144             :       // This could happen during shutdown.
    3145             :       return CORS_NONE;
    3146             :     }
    3147             : 
    3148           0 :     return mCapturedTrackSource->GetCORSMode();
    3149             :   }
    3150             : 
    3151           0 :   void Stop() override
    3152             :   {
    3153           0 :     if (mElement && mElement->mSrcStream) {
    3154             :       // Only notify if we're still playing the source stream. GC might have
    3155             :       // cleared it before the track sources.
    3156           0 :       mElement->NotifyOutputTrackStopped(mOwningStream, mDestinationTrackID);
    3157             :     }
    3158           0 :     mElement = nullptr;
    3159           0 :     mOwningStream = nullptr;
    3160             : 
    3161           0 :     Destroy();
    3162           0 :   }
    3163             : 
    3164             :   /**
    3165             :    * Do not keep the track source alive. The source lifetime is controlled by
    3166             :    * its associated tracks.
    3167             :    */
    3168           0 :   bool KeepsSourceAlive() const override { return false; }
    3169             : 
    3170             :   /**
    3171             :    * Do not keep the track source on. It is controlled by its associated tracks.
    3172             :    */
    3173           0 :   bool Enabled() const override { return false; }
    3174             : 
    3175           0 :   void Disable() override {}
    3176             : 
    3177           0 :   void Enable() override {}
    3178             : 
    3179           0 :   void PrincipalChanged() override
    3180             :   {
    3181           0 :     if (!mCapturedTrackSource) {
    3182             :       // This could happen during shutdown.
    3183             :       return;
    3184             :     }
    3185             : 
    3186           0 :     mPrincipal = mCapturedTrackSource->GetPrincipal();
    3187           0 :     MediaStreamTrackSource::PrincipalChanged();
    3188             :   }
    3189             : 
    3190           0 :   void MutedChanged(bool aNewState) override
    3191             :   {
    3192           0 :     if (!mCapturedTrackSource) {
    3193             :       // This could happen during shutdown.
    3194             :       return;
    3195             :     }
    3196             : 
    3197           0 :     MediaStreamTrackSource::MutedChanged(aNewState);
    3198             :   }
    3199             : 
    3200             : private:
    3201           0 :   virtual ~StreamCaptureTrackSource() {}
    3202             : 
    3203             :   RefPtr<HTMLMediaElement> mElement;
    3204             :   RefPtr<MediaStreamTrackSource> mCapturedTrackSource;
    3205             :   RefPtr<DOMMediaStream> mOwningStream;
    3206             :   TrackID mDestinationTrackID;
    3207             : };
    3208             : 
    3209           0 : NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
    3210             :                          MediaStreamTrackSource)
    3211           0 : NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
    3212             :                           MediaStreamTrackSource)
    3213           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    3214             :   HTMLMediaElement::StreamCaptureTrackSource)
    3215           0 : NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
    3216           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::StreamCaptureTrackSource,
    3217             :                                    MediaStreamTrackSource,
    3218             :                                    mElement,
    3219             :                                    mCapturedTrackSource,
    3220             :                                    mOwningStream)
    3221             : 
    3222             : class HTMLMediaElement::DecoderCaptureTrackSource
    3223             :   : public MediaStreamTrackSource
    3224             :   , public DecoderPrincipalChangeObserver
    3225             : {
    3226             : public:
    3227             :   NS_DECL_ISUPPORTS_INHERITED
    3228           0 :   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DecoderCaptureTrackSource,
    3229             :                                            MediaStreamTrackSource)
    3230             : 
    3231           0 :   explicit DecoderCaptureTrackSource(HTMLMediaElement* aElement)
    3232           0 :     : MediaStreamTrackSource(
    3233           0 :         nsCOMPtr<nsIPrincipal>(aElement->GetCurrentPrincipal()).get(),
    3234           0 :         nsString())
    3235           0 :     , mElement(aElement)
    3236             :   {
    3237           0 :     MOZ_ASSERT(mElement);
    3238           0 :     mElement->AddDecoderPrincipalChangeObserver(this);
    3239           0 :   }
    3240             : 
    3241           0 :   void Destroy() override
    3242             :   {
    3243           0 :     if (mElement) {
    3244             :       DebugOnly<bool> res =
    3245           0 :         mElement->RemoveDecoderPrincipalChangeObserver(this);
    3246           0 :       NS_ASSERTION(res,
    3247             :                    "Removing decoder principal changed observer failed. "
    3248             :                    "Had it already been removed?");
    3249           0 :       mElement = nullptr;
    3250             :     }
    3251           0 :   }
    3252             : 
    3253           0 :   MediaSourceEnum GetMediaSource() const override
    3254             :   {
    3255           0 :     return MediaSourceEnum::Other;
    3256             :   }
    3257             : 
    3258           0 :   CORSMode GetCORSMode() const override
    3259             :   {
    3260           0 :     if (!mElement) {
    3261           0 :       MOZ_ASSERT(false, "Should always have an element if in use");
    3262             :       return CORS_NONE;
    3263             :     }
    3264             : 
    3265           0 :     return mElement->GetCORSMode();
    3266             :   }
    3267             : 
    3268           0 :   void Stop() override
    3269             :   {
    3270             :     // We don't notify the source that a track was stopped since it will keep
    3271             :     // producing tracks until the element ends. The decoder also needs the
    3272             :     // tracks it created to be live at the source since the decoder's clock is
    3273             :     // based on MediaStreams during capture.
    3274           0 :   }
    3275             : 
    3276           0 :   void Disable() override {}
    3277             : 
    3278           0 :   void Enable() override {}
    3279             : 
    3280           0 :   void NotifyDecoderPrincipalChanged() override
    3281             :   {
    3282           0 :     nsCOMPtr<nsIPrincipal> newPrincipal = mElement->GetCurrentPrincipal();
    3283           0 :     if (nsContentUtils::CombineResourcePrincipals(&mPrincipal, newPrincipal)) {
    3284           0 :       PrincipalChanged();
    3285             :     }
    3286           0 :   }
    3287             : 
    3288             : protected:
    3289           0 :   virtual ~DecoderCaptureTrackSource() {}
    3290             : 
    3291             :   RefPtr<HTMLMediaElement> mElement;
    3292             : };
    3293             : 
    3294           0 : NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::DecoderCaptureTrackSource,
    3295             :                          MediaStreamTrackSource)
    3296           0 : NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::DecoderCaptureTrackSource,
    3297             :                           MediaStreamTrackSource)
    3298           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    3299             :   HTMLMediaElement::DecoderCaptureTrackSource)
    3300           0 : NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
    3301           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::DecoderCaptureTrackSource,
    3302             :                                    MediaStreamTrackSource,
    3303             :                                    mElement)
    3304             : 
    3305             : class HTMLMediaElement::CaptureStreamTrackSourceGetter
    3306             :   : public MediaStreamTrackSourceGetter
    3307             : {
    3308             : public:
    3309             :   NS_DECL_ISUPPORTS_INHERITED
    3310           0 :   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CaptureStreamTrackSourceGetter,
    3311             :                                            MediaStreamTrackSourceGetter)
    3312             : 
    3313           0 :   explicit CaptureStreamTrackSourceGetter(HTMLMediaElement* aElement)
    3314           0 :     : mElement(aElement)
    3315             :   {
    3316           0 :   }
    3317             : 
    3318           0 :   already_AddRefed<dom::MediaStreamTrackSource> GetMediaStreamTrackSource(
    3319             :     TrackID aInputTrackID) override
    3320             :   {
    3321           0 :     if (mElement && mElement->mSrcStream) {
    3322           0 :       NS_ERROR("Captured media element playing a stream adds tracks explicitly "
    3323             :                "on main thread.");
    3324             :       return nullptr;
    3325             :     }
    3326             : 
    3327             :     // We can return a new source each time here, even for different streams,
    3328             :     // since the sources don't keep any internal state and all of them call
    3329             :     // through to the same HTMLMediaElement.
    3330             :     // If this changes (after implementing Stop()?) we'll have to ensure we
    3331             :     // return the same source for all requests to the same TrackID, and only
    3332             :     // have one getter.
    3333           0 :     return do_AddRef(new DecoderCaptureTrackSource(mElement));
    3334             :   }
    3335             : 
    3336             : protected:
    3337           0 :   virtual ~CaptureStreamTrackSourceGetter() {}
    3338             : 
    3339             :   RefPtr<HTMLMediaElement> mElement;
    3340             : };
    3341             : 
    3342           0 : NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::CaptureStreamTrackSourceGetter,
    3343             :                          MediaStreamTrackSourceGetter)
    3344           0 : NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::CaptureStreamTrackSourceGetter,
    3345             :                           MediaStreamTrackSourceGetter)
    3346           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    3347             :   HTMLMediaElement::CaptureStreamTrackSourceGetter)
    3348           0 : NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSourceGetter)
    3349           0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(
    3350             :   HTMLMediaElement::CaptureStreamTrackSourceGetter,
    3351             :   MediaStreamTrackSourceGetter,
    3352             :   mElement)
    3353             : 
    3354             : void
    3355           0 : HTMLMediaElement::SetCapturedOutputStreamsEnabled(bool aEnabled)
    3356             : {
    3357           0 :   for (OutputMediaStream& ms : mOutputStreams) {
    3358           0 :     if (ms.mCapturingDecoder) {
    3359           0 :       MOZ_ASSERT(!ms.mCapturingMediaStream);
    3360             :       continue;
    3361             :     }
    3362           0 :     for (auto pair : ms.mTrackPorts) {
    3363           0 :       MediaStream* outputSource = ms.mStream->GetInputStream();
    3364           0 :       if (!outputSource) {
    3365           0 :         NS_ERROR("No output source stream");
    3366           0 :         return;
    3367             :       }
    3368             : 
    3369           0 :       TrackID id = pair.second()->GetDestinationTrackId();
    3370           0 :       outputSource->SetTrackEnabled(id,
    3371             :                                     aEnabled
    3372             :                                       ? DisabledTrackMode::ENABLED
    3373           0 :                                       : DisabledTrackMode::SILENCE_FREEZE);
    3374             : 
    3375           0 :       LOG(LogLevel::Debug,
    3376             :           ("%s track %d for captured MediaStream %p",
    3377             :            aEnabled ? "Enabled" : "Disabled",
    3378             :            id,
    3379             :            ms.mStream.get()));
    3380             :     }
    3381             :   }
    3382             : }
    3383             : 
    3384             : void
    3385           0 : HTMLMediaElement::AddCaptureMediaTrackToOutputStream(
    3386             :   MediaTrack* aTrack,
    3387             :   OutputMediaStream& aOutputStream,
    3388             :   bool aAsyncAddtrack)
    3389             : {
    3390           0 :   if (aOutputStream.mCapturingDecoder) {
    3391           0 :     MOZ_ASSERT(!aOutputStream.mCapturingMediaStream);
    3392           0 :     return;
    3393             :   }
    3394           0 :   aOutputStream.mCapturingMediaStream = true;
    3395             : 
    3396           0 :   if (aOutputStream.mStream == mSrcStream) {
    3397             :     // Cycle detected. This can happen since tracks are added async.
    3398             :     // We avoid forwarding it to the output here or we'd get into an infloop.
    3399             :     return;
    3400             :   }
    3401             : 
    3402           0 :   MediaStream* outputSource = aOutputStream.mStream->GetInputStream();
    3403           0 :   if (!outputSource) {
    3404           0 :     NS_ERROR("No output source stream");
    3405             :     return;
    3406             :   }
    3407             : 
    3408             :   ProcessedMediaStream* processedOutputSource =
    3409           0 :     outputSource->AsProcessedStream();
    3410           0 :   if (!processedOutputSource) {
    3411           0 :     NS_ERROR("Input stream not a ProcessedMediaStream");
    3412             :     return;
    3413             :   }
    3414             : 
    3415           0 :   if (!aTrack) {
    3416           0 :     MOZ_ASSERT(false, "Bad MediaTrack");
    3417             :     return;
    3418             :   }
    3419             : 
    3420           0 :   MediaStreamTrack* inputTrack = mSrcStream->GetTrackById(aTrack->GetId());
    3421           0 :   MOZ_ASSERT(inputTrack);
    3422           0 :   if (!inputTrack) {
    3423           0 :     NS_ERROR("Input track not found in source stream");
    3424             :     return;
    3425             :   }
    3426             : 
    3427             : #if DEBUG
    3428           0 :   for (auto pair : aOutputStream.mTrackPorts) {
    3429           0 :     MOZ_ASSERT(pair.first() != aTrack->GetId(),
    3430             :                "Captured track already captured to output stream");
    3431             :   }
    3432             : #endif
    3433             : 
    3434           0 :   TrackID destinationTrackID = aOutputStream.mNextAvailableTrackID++;
    3435             :   RefPtr<MediaStreamTrackSource> source = new StreamCaptureTrackSource(
    3436           0 :     this, &inputTrack->GetSource(), aOutputStream.mStream, destinationTrackID);
    3437             : 
    3438           0 :   MediaSegment::Type type = inputTrack->AsAudioStreamTrack()
    3439           0 :                               ? MediaSegment::AUDIO
    3440           0 :                               : MediaSegment::VIDEO;
    3441             : 
    3442             :   RefPtr<MediaStreamTrack> track =
    3443           0 :     aOutputStream.mStream->CreateDOMTrack(destinationTrackID, type, source);
    3444             : 
    3445           0 :   if (aAsyncAddtrack) {
    3446           0 :     mMainThreadEventTarget->Dispatch(
    3447           0 :       NewRunnableMethod<StoreRefPtrPassByPtr<MediaStreamTrack>>(
    3448             :         "DOMMediaStream::AddTrackInternal",
    3449             :         aOutputStream.mStream,
    3450             :         &DOMMediaStream::AddTrackInternal,
    3451           0 :         track));
    3452             :   } else {
    3453           0 :     aOutputStream.mStream->AddTrackInternal(track);
    3454             :   }
    3455             : 
    3456             :   // Track is muted initially, so we don't leak data if it's added while paused
    3457             :   // and an MSG iteration passes before the mute comes into effect.
    3458           0 :   processedOutputSource->SetTrackEnabled(destinationTrackID,
    3459           0 :                                          DisabledTrackMode::SILENCE_FREEZE);
    3460           0 :   RefPtr<MediaInputPort> port = inputTrack->ForwardTrackContentsTo(
    3461           0 :     processedOutputSource, destinationTrackID);
    3462             : 
    3463           0 :   Pair<nsString, RefPtr<MediaInputPort>> p(aTrack->GetId(), port);
    3464           0 :   aOutputStream.mTrackPorts.AppendElement(std::move(p));
    3465             : 
    3466           0 :   if (mSrcStreamIsPlaying) {
    3467             :     processedOutputSource->SetTrackEnabled(destinationTrackID,
    3468           0 :                                            DisabledTrackMode::ENABLED);
    3469             :   }
    3470             : 
    3471           0 :   LOG(LogLevel::Debug,
    3472             :       ("Created %s track %p with id %d from track %p through MediaInputPort %p",
    3473             :        inputTrack->AsAudioStreamTrack() ? "audio" : "video",
    3474             :        track.get(),
    3475             :        destinationTrackID,
    3476             :        inputTrack,
    3477             :        port.get()));
    3478             : }
    3479             : 
    3480             : bool
    3481           0 : HTMLMediaElement::CanBeCaptured(StreamCaptureType aCaptureType)
    3482             : {
    3483             :   // Don't bother capturing when the document has gone away
    3484           0 :   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
    3485           0 :   if (!window) {
    3486             :     return false;
    3487             :   }
    3488             : 
    3489             :   // Prevent capturing restricted video
    3490           0 :   if (aCaptureType == StreamCaptureType::CAPTURE_ALL_TRACKS &&
    3491           0 :       ContainsRestrictedContent()) {
    3492             :     return false;
    3493             :   }
    3494           0 :   return true;
    3495             : }
    3496             : 
    3497             : already_AddRefed<DOMMediaStream>
    3498           0 : HTMLMediaElement::CaptureStreamInternal(StreamCaptureBehavior aFinishBehavior,
    3499             :                                         StreamCaptureType aStreamCaptureType,
    3500             :                                         MediaStreamGraph* aGraph)
    3501             : {
    3502           0 :   MOZ_RELEASE_ASSERT(aGraph);
    3503           0 :   MOZ_ASSERT(CanBeCaptured(aStreamCaptureType));
    3504             : 
    3505           0 :   MarkAsContentSource(CallerAPI::CAPTURE_STREAM);
    3506           0 :   MarkAsTainted();
    3507             : 
    3508             :   // We don't support routing to a different graph.
    3509           0 :   if (!mOutputStreams.IsEmpty() &&
    3510           0 :       aGraph != mOutputStreams[0].mStream->GetInputStream()->Graph()) {
    3511             :     return nullptr;
    3512             :   }
    3513             : 
    3514           0 :   OutputMediaStream* out = mOutputStreams.AppendElement();
    3515             :   MediaStreamTrackSourceGetter* getter =
    3516           0 :     new CaptureStreamTrackSourceGetter(this);
    3517           0 :   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
    3518             :   out->mStream =
    3519           0 :     DOMMediaStream::CreateTrackUnionStreamAsInput(window, aGraph, getter);
    3520           0 :   out->mStream->SetInactiveOnFinish();
    3521           0 :   out->mFinishWhenEnded =
    3522           0 :     aFinishBehavior == StreamCaptureBehavior::FINISH_WHEN_ENDED;
    3523           0 :   out->mCapturingAudioOnly =
    3524           0 :     aStreamCaptureType == StreamCaptureType::CAPTURE_AUDIO;
    3525             : 
    3526           0 :   if (aStreamCaptureType == StreamCaptureType::CAPTURE_AUDIO) {
    3527           0 :     if (mSrcStream) {
    3528             :       // We don't support applying volume and mute to the captured stream, when
    3529             :       // capturing a MediaStream.
    3530           0 :       nsContentUtils::ReportToConsole(
    3531             :         nsIScriptError::errorFlag,
    3532           0 :         NS_LITERAL_CSTRING("Media"),
    3533           0 :         OwnerDoc(),
    3534             :         nsContentUtils::eDOM_PROPERTIES,
    3535           0 :         "MediaElementAudioCaptureOfMediaStreamError");
    3536             :       return nullptr;
    3537             :     }
    3538             : 
    3539             :     // mAudioCaptured tells the user that the audio played by this media element
    3540             :     // is being routed to the captureStreams *instead* of being played to
    3541             :     // speakers.
    3542           0 :     mAudioCaptured = true;
    3543             :   }
    3544             : 
    3545           0 :   if (mDecoder) {
    3546           0 :     out->mCapturingDecoder = true;
    3547           0 :     mDecoder->AddOutputStream(
    3548           0 :       out->mStream->GetInputStream()->AsProcessedStream(),
    3549             :       out->mNextAvailableTrackID,
    3550           0 :       aFinishBehavior == StreamCaptureBehavior::FINISH_WHEN_ENDED);
    3551           0 :   } else if (mSrcStream) {
    3552           0 :     out->mCapturingMediaStream = true;
    3553             :   }
    3554             : 
    3555           0 :   if (mReadyState == HAVE_NOTHING) {
    3556             :     // Do not expose the tracks until we have metadata.
    3557           0 :     RefPtr<DOMMediaStream> result = out->mStream;
    3558           0 :     return result.forget();
    3559             :   }
    3560             : 
    3561           0 :   if (mDecoder) {
    3562           0 :     if (HasAudio()) {
    3563           0 :       TrackID audioTrackId = out->mNextAvailableTrackID++;
    3564             :       RefPtr<MediaStreamTrackSource> trackSource =
    3565           0 :         getter->GetMediaStreamTrackSource(audioTrackId);
    3566           0 :       RefPtr<MediaStreamTrack> track = out->mStream->CreateDOMTrack(
    3567           0 :         audioTrackId, MediaSegment::AUDIO, trackSource);
    3568           0 :       out->mPreCreatedTracks.AppendElement(track);
    3569           0 :       out->mStream->AddTrackInternal(track);
    3570           0 :       LOG(LogLevel::Debug,
    3571             :           ("Created audio track %d for captured decoder", audioTrackId));
    3572             :     }
    3573           0 :     if (IsVideo() && HasVideo() && !out->mCapturingAudioOnly) {
    3574           0 :       TrackID videoTrackId = out->mNextAvailableTrackID++;
    3575             :       RefPtr<MediaStreamTrackSource> trackSource =
    3576           0 :         getter->GetMediaStreamTrackSource(videoTrackId);
    3577           0 :       RefPtr<MediaStreamTrack> track = out->mStream->CreateDOMTrack(
    3578           0 :         videoTrackId, MediaSegment::VIDEO, trackSource);
    3579           0 :       out->mPreCreatedTracks.AppendElement(track);
    3580           0 :       out->mStream->AddTrackInternal(track);
    3581           0 :       LOG(LogLevel::Debug,
    3582             :           ("Created video track %d for captured decoder", videoTrackId));
    3583             :     }
    3584             :   }
    3585             : 
    3586           0 :   if (mSrcStream) {
    3587           0 :     for (size_t i = 0; i < AudioTracks()->Length(); ++i) {
    3588           0 :       AudioTrack* t = (*AudioTracks())[i];
    3589           0 :       if (t->Enabled()) {
    3590           0 :         AddCaptureMediaTrackToOutputStream(t, *out, false);
    3591             :       }
    3592             :     }
    3593           0 :     if (IsVideo() && !out->mCapturingAudioOnly) {
    3594             :       // Only add video tracks if we're a video element and the output stream
    3595             :       // wants video.
    3596           0 :       for (size_t i = 0; i < VideoTracks()->Length(); ++i) {
    3597           0 :         VideoTrack* t = (*VideoTracks())[i];
    3598           0 :         if (t->Selected()) {
    3599           0 :           AddCaptureMediaTrackToOutputStream(t, *out, false);
    3600             :         }
    3601             :       }
    3602             :     }
    3603             :   }
    3604           0 :   RefPtr<DOMMediaStream> result = out->mStream;
    3605           0 :   return result.forget();
    3606             : }
    3607             : 
    3608             : already_AddRefed<DOMMediaStream>
    3609           0 : HTMLMediaElement::CaptureAudio(ErrorResult& aRv, MediaStreamGraph* aGraph)
    3610             : {
    3611           0 :   MOZ_RELEASE_ASSERT(aGraph);
    3612             : 
    3613           0 :   if (!CanBeCaptured(StreamCaptureType::CAPTURE_AUDIO)) {
    3614           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3615             :     return nullptr;
    3616             :   }
    3617             : 
    3618             :   RefPtr<DOMMediaStream> stream =
    3619           0 :     CaptureStreamInternal(StreamCaptureBehavior::CONTINUE_WHEN_ENDED,
    3620             :                           StreamCaptureType::CAPTURE_AUDIO,
    3621           0 :                           aGraph);
    3622           0 :   if (!stream) {
    3623           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3624             :     return nullptr;
    3625             :   }
    3626             : 
    3627             :   return stream.forget();
    3628             : }
    3629             : 
    3630             : already_AddRefed<DOMMediaStream>
    3631           0 : HTMLMediaElement::MozCaptureStream(ErrorResult& aRv)
    3632             : {
    3633             :   MediaStreamGraph::GraphDriverType graphDriverType =
    3634           0 :     HasAudio() ? MediaStreamGraph::AUDIO_THREAD_DRIVER
    3635           0 :                : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
    3636             : 
    3637           0 :   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
    3638           0 :   if (!window) {
    3639           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3640             :     return nullptr;
    3641             :   }
    3642             : 
    3643           0 :   if (!CanBeCaptured(StreamCaptureType::CAPTURE_ALL_TRACKS)) {
    3644           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3645             :     return nullptr;
    3646             :   }
    3647             : 
    3648             :   MediaStreamGraph* graph = MediaStreamGraph::GetInstance(
    3649           0 :     graphDriverType, window, MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE);
    3650             : 
    3651             :   RefPtr<DOMMediaStream> stream =
    3652           0 :     CaptureStreamInternal(StreamCaptureBehavior::CONTINUE_WHEN_ENDED,
    3653             :                           StreamCaptureType::CAPTURE_ALL_TRACKS,
    3654           0 :                           graph);
    3655           0 :   if (!stream) {
    3656           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3657             :     return nullptr;
    3658             :   }
    3659             : 
    3660             :   return stream.forget();
    3661             : }
    3662             : 
    3663             : already_AddRefed<DOMMediaStream>
    3664           0 : HTMLMediaElement::MozCaptureStreamUntilEnded(ErrorResult& aRv)
    3665             : {
    3666             :   MediaStreamGraph::GraphDriverType graphDriverType =
    3667           0 :     HasAudio() ? MediaStreamGraph::AUDIO_THREAD_DRIVER
    3668           0 :                : MediaStreamGraph::SYSTEM_THREAD_DRIVER;
    3669             : 
    3670           0 :   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
    3671           0 :   if (!window) {
    3672           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3673             :     return nullptr;
    3674             :   }
    3675             : 
    3676           0 :   if (!CanBeCaptured(StreamCaptureType::CAPTURE_ALL_TRACKS)) {
    3677           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3678             :     return nullptr;
    3679             :   }
    3680             : 
    3681             :   MediaStreamGraph* graph = MediaStreamGraph::GetInstance(
    3682           0 :     graphDriverType, window, MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE);
    3683             : 
    3684             :   RefPtr<DOMMediaStream> stream =
    3685           0 :     CaptureStreamInternal(StreamCaptureBehavior::FINISH_WHEN_ENDED,
    3686             :                           StreamCaptureType::CAPTURE_ALL_TRACKS,
    3687           0 :                           graph);
    3688           0 :   if (!stream) {
    3689           0 :     aRv.Throw(NS_ERROR_FAILURE);
    3690             :     return nullptr;
    3691             :   }
    3692             : 
    3693             :   return stream.forget();
    3694             : }
    3695             : 
    3696           0 : class MediaElementSetForURI : public nsURIHashKey
    3697             : {
    3698             : public:
    3699           0 :   explicit MediaElementSetForURI(const nsIURI* aKey)
    3700           0 :     : nsURIHashKey(aKey)
    3701             :   {
    3702           0 :   }
    3703             :   MediaElementSetForURI(const MediaElementSetForURI& toCopy)
    3704             :     : nsURIHashKey(toCopy)
    3705             :     , mElements(toCopy.mElements)
    3706             :   {
    3707             :   }
    3708             :   nsTArray<HTMLMediaElement*> mElements;
    3709             : };
    3710             : 
    3711             : typedef nsTHashtable<MediaElementSetForURI> MediaElementURITable;
    3712             : // Elements in this table must have non-null mDecoder and mLoadingSrc, and those
    3713             : // can't change while the element is in the table. The table is keyed by
    3714             : // the element's mLoadingSrc. Each entry has a list of all elements with the
    3715             : // same mLoadingSrc.
    3716             : static MediaElementURITable* gElementTable;
    3717             : 
    3718             : #ifdef DEBUG
    3719             : static bool
    3720           0 : URISafeEquals(nsIURI* a1, nsIURI* a2)
    3721             : {
    3722           0 :   if (!a1 || !a2) {
    3723             :     // Consider two empty URIs *not* equal!
    3724             :     return false;
    3725             :   }
    3726           0 :   bool equal = false;
    3727           0 :   nsresult rv = a1->Equals(a2, &equal);
    3728           0 :   return NS_SUCCEEDED(rv) && equal;
    3729             : }
    3730             : // Returns the number of times aElement appears in the media element table
    3731             : // for aURI. If this returns other than 0 or 1, there's a bug somewhere!
    3732             : static unsigned
    3733           0 : MediaElementTableCount(HTMLMediaElement* aElement, nsIURI* aURI)
    3734             : {
    3735           0 :   if (!gElementTable || !aElement) {
    3736             :     return 0;
    3737             :   }
    3738           0 :   uint32_t uriCount = 0;
    3739           0 :   uint32_t otherCount = 0;
    3740           0 :   for (auto it = gElementTable->ConstIter(); !it.Done(); it.Next()) {
    3741           0 :     MediaElementSetForURI* entry = it.Get();
    3742           0 :     uint32_t count = 0;
    3743           0 :     for (const auto& elem : entry->mElements) {
    3744           0 :       if (elem == aElement) {
    3745           0 :         count++;
    3746             :       }
    3747             :     }
    3748           0 :     if (URISafeEquals(aURI, entry->GetKey())) {
    3749             :       uriCount = count;
    3750             :     } else {
    3751           0 :       otherCount += count;
    3752             :     }
    3753             :   }
    3754           0 :   NS_ASSERTION(otherCount == 0, "Should not have entries for unknown URIs");
    3755             :   return uriCount;
    3756             : }
    3757             : #endif
    3758             : 
    3759             : void
    3760           0 : HTMLMediaElement::AddMediaElementToURITable()
    3761             : {
    3762           0 :   NS_ASSERTION(mDecoder, "Call this only with decoder Load called");
    3763           0 :   NS_ASSERTION(
    3764             :     MediaElementTableCount(this, mLoadingSrc) == 0,
    3765             :     "Should not have entry for element in element table before addition");
    3766           0 :   if (!gElementTable) {
    3767           0 :     gElementTable = new MediaElementURITable();
    3768             :   }
    3769           0 :   MediaElementSetForURI* entry = gElementTable->PutEntry(mLoadingSrc);
    3770           0 :   entry->mElements.AppendElement(this);
    3771           0 :   NS_ASSERTION(
    3772             :     MediaElementTableCount(this, mLoadingSrc) == 1,
    3773             :     "Should have a single entry for element in element table after addition");
    3774           0 : }
    3775             : 
    3776             : void
    3777           0 : HTMLMediaElement::RemoveMediaElementFromURITable()
    3778             : {
    3779           0 :   if (!mDecoder || !mLoadingSrc || !gElementTable) {
    3780             :     return;
    3781             :   }
    3782           0 :   MediaElementSetForURI* entry = gElementTable->GetEntry(mLoadingSrc);
    3783           0 :   if (!entry) {
    3784             :     return;
    3785             :   }
    3786           0 :   entry->mElements.RemoveElement(this);
    3787           0 :   if (entry->mElements.IsEmpty()) {
    3788           0 :     gElementTable->RemoveEntry(entry);
    3789           0 :     if (gElementTable->Count() == 0) {
    3790           0 :       delete gElementTable;
    3791           0 :       gElementTable = nullptr;
    3792             :     }
    3793             :   }
    3794           0 :   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
    3795             :                "After remove, should no longer have an entry in element table");
    3796             : }
    3797             : 
    3798             : HTMLMediaElement*
    3799           0 : HTMLMediaElement::LookupMediaElementURITable(nsIURI* aURI)
    3800             : {
    3801           0 :   if (!gElementTable) {
    3802             :     return nullptr;
    3803             :   }
    3804           0 :   MediaElementSetForURI* entry = gElementTable->GetEntry(aURI);
    3805           0 :   if (!entry) {
    3806             :     return nullptr;
    3807             :   }
    3808           0 :   for (uint32_t i = 0; i < entry->mElements.Length(); ++i) {
    3809           0 :     HTMLMediaElement* elem = entry->mElements[i];
    3810             :     bool equal;
    3811             :     // Look for elements that have the same principal and CORS mode.
    3812             :     // Ditto for anything else that could cause us to send different headers.
    3813           0 :     if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) &&
    3814           0 :         equal && elem->mCORSMode == mCORSMode) {
    3815             :       // See SetupDecoder() below. We only add a element to the table when
    3816             :       // mDecoder is a ChannelMediaDecoder.
    3817           0 :       auto decoder = static_cast<ChannelMediaDecoder*>(elem->mDecoder.get());
    3818           0 :       NS_ASSERTION(decoder, "Decoder gone");
    3819           0 :       if (decoder->CanClone()) {
    3820           0 :         return elem;
    3821             :       }
    3822             :     }
    3823             :   }
    3824             :   return nullptr;
    3825             : }
    3826             : 
    3827           3 : class HTMLMediaElement::ShutdownObserver : public nsIObserver
    3828             : {
    3829             :   enum class Phase : int8_t
    3830             :   {
    3831             :     Init,
    3832             :     Subscribed,
    3833             :     Unsubscribed
    3834             :   };
    3835             : 
    3836             : public:
    3837             :   NS_DECL_ISUPPORTS
    3838             : 
    3839           0 :   NS_IMETHOD Observe(nsISupports*, const char* aTopic, const char16_t*) override
    3840             :   {
    3841           0 :     if (mPhase != Phase::Subscribed) {
    3842             :       // Bail out if we are not subscribed for this might be called even after
    3843             :       // |nsContentUtils::UnregisterShutdownObserver(this)|.
    3844             :       return NS_OK;
    3845             :     }
    3846           0 :     MOZ_DIAGNOSTIC_ASSERT(mWeak);
    3847           0 :     if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
    3848           0 :       mWeak->NotifyShutdownEvent();
    3849             :     }
    3850             :     return NS_OK;
    3851             :   }
    3852           0 :   void Subscribe(HTMLMediaElement* aPtr)
    3853             :   {
    3854           0 :     MOZ_DIAGNOSTIC_ASSERT(mPhase == Phase::Init);
    3855           0 :     MOZ_DIAGNOSTIC_ASSERT(!mWeak);
    3856           0 :     mWeak = aPtr;
    3857           0 :     nsContentUtils::RegisterShutdownObserver(this);
    3858           0 :     mPhase = Phase::Subscribed;
    3859           1 :   }
    3860           0 :   void Unsubscribe()
    3861             :   {
    3862           0 :     MOZ_DIAGNOSTIC_ASSERT(mPhase == Phase::Subscribed);
    3863           0 :     MOZ_DIAGNOSTIC_ASSERT(mWeak);
    3864           0 :     mWeak = nullptr;
    3865           0 :     nsContentUtils::UnregisterShutdownObserver(this);
    3866           0 :     mPhase = Phase::Unsubscribed;
    3867           0 :   }
    3868           0 :   void AddRefMediaElement() { mWeak->AddRef(); }
    3869           0 :   void ReleaseMediaElement() { mWeak->Release(); }
    3870             : 
    3871             : private:
    3872           0 :   virtual ~ShutdownObserver()
    3873           0 :   {
    3874           0 :     MOZ_DIAGNOSTIC_ASSERT(mPhase == Phase::Unsubscribed);
    3875           0 :     MOZ_DIAGNOSTIC_ASSERT(!mWeak);
    3876           0 :   }
    3877             :   // Guaranteed to be valid by HTMLMediaElement.
    3878             :   HTMLMediaElement* mWeak = nullptr;
    3879             :   Phase mPhase = Phase::Init;
    3880             : };
    3881             : 
    3882           0 : NS_IMPL_ISUPPORTS(HTMLMediaElement::ShutdownObserver, nsIObserver)
    3883             : 
    3884           0 : HTMLMediaElement::HTMLMediaElement(
    3885           0 :   already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
    3886             :   : nsGenericHTMLElement(aNodeInfo)
    3887           0 :   , mWatchManager(this, OwnerDoc()->AbstractMainThreadFor(TaskCategory::Other))
    3888           1 :   , mMainThreadEventTarget(OwnerDoc()->EventTargetFor(TaskCategory::Other))
    3889           0 :   , mAbstractMainThread(OwnerDoc()->AbstractMainThreadFor(TaskCategory::Other))
    3890           1 :   , mShutdownObserver(new ShutdownObserver)
    3891           0 :   , mPlayed(new TimeRanges(ToSupports(OwnerDoc())))
    3892             :   , mPaused(true, "HTMLMediaElement::mPaused")
    3893           1 :   , mErrorSink(new ErrorSink(this))
    3894           0 :   , mAudioChannelWrapper(new AudioChannelAgentCallback(this))
    3895             : {
    3896           2 :   MOZ_ASSERT(mMainThreadEventTarget);
    3897           0 :   MOZ_ASSERT(mAbstractMainThread);
    3898             : 
    3899           1 :   DecoderDoctorLogger::LogConstruction(this);
    3900             : 
    3901           1 :   mWatchManager.Watch(mPaused, &HTMLMediaElement::UpdateWakeLock);
    3902             : 
    3903           2 :   ErrorResult rv;
    3904             : 
    3905           1 :   double defaultVolume = Preferences::GetFloat("media.default_volume", 1.0);
    3906           0 :   SetVolume(defaultVolume, rv);
    3907             : 
    3908           0 :   RegisterActivityObserver();
    3909           0 :   NotifyOwnerDocumentActivityChanged();
    3910             : 
    3911           0 :   mShutdownObserver->Subscribe(this);
    3912           1 : }
    3913             : 
    3914           0 : HTMLMediaElement::~HTMLMediaElement()
    3915             : {
    3916           0 :   NS_ASSERTION(
    3917             :     !mHasSelfReference,
    3918             :     "How can we be destroyed if we're still holding a self reference?");
    3919             : 
    3920           0 :   mShutdownObserver->Unsubscribe();
    3921             : 
    3922           0 :   if (mVideoFrameContainer) {
    3923           0 :     mVideoFrameContainer->ForgetElement();
    3924             :   }
    3925           0 :   UnregisterActivityObserver();
    3926             : 
    3927           0 :   mSetCDMRequest.DisconnectIfExists();
    3928           0 :   if (mDecoder) {
    3929           0 :     ShutdownDecoder();
    3930             :   }
    3931           0 :   if (mProgressTimer) {
    3932           0 :     StopProgress();
    3933             :   }
    3934           0 :   if (mVideoDecodeSuspendTimer) {
    3935           0 :     mVideoDecodeSuspendTimer->Cancel();
    3936           0 :     mVideoDecodeSuspendTimer = nullptr;
    3937             :   }
    3938           0 :   if (mSrcStream) {
    3939           0 :     EndSrcMediaStreamPlayback();
    3940             :   }
    3941             : 
    3942           0 :   if (mCaptureStreamPort) {
    3943           0 :     mCaptureStreamPort->Destroy();
    3944           0 :     mCaptureStreamPort = nullptr;
    3945             :   }
    3946             : 
    3947           0 :   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
    3948             :                "Destroyed media element should no longer be in element table");
    3949             : 
    3950           0 :   if (mChannelLoader) {
    3951           0 :     mChannelLoader->Cancel();
    3952             :   }
    3953             : 
    3954           0 :   if (mAudioChannelWrapper) {
    3955           0 :     mAudioChannelWrapper->Shutdown();
    3956           0 :     mAudioChannelWrapper = nullptr;
    3957             :   }
    3958             : 
    3959           0 :   WakeLockRelease();
    3960             : 
    3961           0 :   DecoderDoctorLogger::LogDestruction(this);
    3962           0 : }
    3963             : 
    3964             : void
    3965           0 : HTMLMediaElement::StopSuspendingAfterFirstFrame()
    3966             : {
    3967           0 :   mAllowSuspendAfterFirstFrame = false;
    3968           0 :   if (!mSuspendedAfterFirstFrame)
    3969             :     return;
    3970           0 :   mSuspendedAfterFirstFrame = false;
    3971           0 :   if (mDecoder) {
    3972           0 :     mDecoder->Resume();
    3973             :   }
    3974             : }
    3975             : 
    3976             : void
    3977           0 : HTMLMediaElement::SetPlayedOrSeeked(bool aValue)
    3978             : {
    3979           0 :   if (aValue == mHasPlayedOrSeeked) {
    3980             :     return;
    3981             :   }
    3982             : 
    3983           0 :   mHasPlayedOrSeeked = aValue;
    3984             : 
    3985             :   // Force a reflow so that the poster frame hides or shows immediately.
    3986           0 :   nsIFrame* frame = GetPrimaryFrame();
    3987           0 :   if (!frame) {
    3988             :     return;
    3989             :   }
    3990           0 :   frame->PresShell()->FrameNeedsReflow(
    3991           0 :     frame, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
    3992             : }
    3993             : 
    3994             : void
    3995           0 : HTMLMediaElement::NotifyXPCOMShutdown()
    3996             : {
    3997           0 :   ShutdownDecoder();
    3998           0 : }
    3999             : 
    4000             : already_AddRefed<Promise>
    4001           0 : HTMLMediaElement::Play(ErrorResult& aRv)
    4002             : {
    4003           0 :   LOG(LogLevel::Debug,
    4004             :       ("%p Play() called by JS readyState=%d", this, mReadyState));
    4005             : 
    4006           0 :   if (mAudioChannelWrapper && mAudioChannelWrapper->IsPlaybackBlocked()) {
    4007           0 :     MaybeDoLoad();
    4008             : 
    4009             :     // A blocked media element will be resumed later, so we return a pending
    4010             :     // promise which might be resolved/rejected depends on the result of
    4011             :     // resuming the blocked media element.
    4012           0 :     RefPtr<PlayPromise> promise = CreatePlayPromise(aRv);
    4013             : 
    4014           0 :     if (NS_WARN_IF(aRv.Failed())) {
    4015             :       return nullptr;
    4016             :     }
    4017             : 
    4018           0 :     LOG(LogLevel::Debug, ("%p Play() call delayed by AudioChannelAgent", this));
    4019             : 
    4020           0 :     mPendingPlayPromises.AppendElement(promise);
    4021           0 :     return promise.forget();
    4022             :   }
    4023             : 
    4024           0 :   RefPtr<Promise> promise = PlayInternal(aRv);
    4025             : 
    4026           0 :   UpdateCustomPolicyAfterPlayed();
    4027             : 
    4028           0 :   return promise.forget();
    4029             : }
    4030             : 
    4031             : already_AddRefed<Promise>
    4032           0 : HTMLMediaElement::PlayInternal(ErrorResult& aRv)
    4033             : {
    4034           0 :   MOZ_ASSERT(!aRv.Failed());
    4035             : 
    4036           0 :   RefPtr<PlayPromise> promise = CreatePlayPromise(aRv);
    4037             : 
    4038           0 :   if (NS_WARN_IF(aRv.Failed())) {
    4039             :     return nullptr;
    4040             :   }
    4041             : 
    4042             :   // 4.8.12.8
    4043             :   // When the play() method on a media element is invoked, the user agent must
    4044             :   // run the following steps.
    4045             : 
    4046             :   // 4.8.12.8 - Step 1:
    4047             :   // If the media element is not allowed to play, return a promise rejected
    4048             :   // with a "NotAllowedError" DOMException and abort these steps.
    4049           0 :   if (!IsAllowedToPlay()) {
    4050             :     // NOTE: for promise-based-play, will return a rejected promise here.
    4051           0 :     LOG(LogLevel::Debug,
    4052             :         ("%p Play() promise rejected because not allowed to play.", this));
    4053           0 :     promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
    4054           0 :     return promise.forget();
    4055             :   }
    4056             : 
    4057             :   // 4.8.12.8 - Step 2:
    4058             :   // If the media element's error attribute is not null and its code
    4059             :   // attribute has the value MEDIA_ERR_SRC_NOT_SUPPORTED, return a promise
    4060             :   // rejected with a "NotSupportedError" DOMException and abort these steps.
    4061           0 :   if (GetError() && GetError()->Code() == MEDIA_ERR_SRC_NOT_SUPPORTED) {
    4062           0 :     LOG(LogLevel::Debug,
    4063             :         ("%p Play() promise rejected because source not supported.", this));
    4064           0 :     promise->MaybeReject(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
    4065           0 :     return promise.forget();
    4066             :   }
    4067             : 
    4068             :   // 4.8.12.8 - Step 3:
    4069             :   // Let promise be a new promise and append promise to the list of pending
    4070             :   // play promises.
    4071           0 :   mPendingPlayPromises.AppendElement(promise);
    4072             : 
    4073           0 :   if (mPreloadAction == HTMLMediaElement::PRELOAD_NONE) {
    4074             :     // The media load algorithm will be initiated by a user interaction.
    4075             :     // We want to boost the channel priority for better responsiveness.
    4076             :     // Note this must be done before UpdatePreloadAction() which will
    4077             :     // update |mPreloadAction|.
    4078           0 :     mUseUrgentStartForChannel = true;
    4079             :   }
    4080             : 
    4081           0 :   StopSuspendingAfterFirstFrame();
    4082           0 :   SetPlayedOrSeeked(true);
    4083             : 
    4084             :   // 4.8.12.8 - Step 4:
    4085             :   // If the media element's networkState attribute has the value NETWORK_EMPTY,
    4086             :   // invoke the media element's resource selection algorithm.
    4087           0 :   MaybeDoLoad();
    4088           0 :   if (mSuspendedForPreloadNone) {
    4089           0 :     ResumeLoad(PRELOAD_ENOUGH);
    4090             :   }
    4091             : 
    4092             :   // 4.8.12.8 - Step 5:
    4093             :   // If the playback has ended and the direction of playback is forwards,
    4094             :   // seek to the earliest possible position of the media resource.
    4095             : 
    4096             :   // Even if we just did Load() or ResumeLoad(), we could already have a decoder
    4097             :   // here if we managed to clone an existing decoder.
    4098           0 :   if (mDecoder) {
    4099           0 :     if (mDecoder->IsEnded()) {
    4100           0 :       SetCurrentTime(0);
    4101             :     }
    4102           0 :     if (!mPausedForInactiveDocumentOrChannel) {
    4103           0 :       mDecoder->Play();
    4104             :     }
    4105             :   }
    4106             : 
    4107           0 :   if (mCurrentPlayRangeStart == -1.0) {
    4108           0 :     mCurrentPlayRangeStart = CurrentTime();
    4109             :   }
    4110             : 
    4111           0 :   const bool oldPaused = mPaused;
    4112           0 :   mPaused = false;
    4113           0 :   mAutoplaying = false;
    4114             : 
    4115             :   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
    4116             :   // and our preload status.
    4117           0 :   AddRemoveSelfReference();
    4118           0 :   UpdatePreloadAction();
    4119           0 :   UpdateSrcMediaStreamPlaying();
    4120             : 
    4121             :   // Once play() has been called in a user generated event handler,
    4122             :   // it is allowed to autoplay. Note: we can reach here when not in
    4123             :   // a user generated event handler if our readyState has not yet
    4124             :   // reached HAVE_METADATA.
    4125           0 :   mIsBlessed |= EventStateManager::IsHandlingUserInput();
    4126             : 
    4127             :   // TODO: If the playback has ended, then the user agent must set
    4128             :   // seek to the effective start.
    4129             : 
    4130             :   // 4.8.12.8 - Step 6:
    4131             :   // If the media element's paused attribute is true, run the following steps:
    4132           0 :   if (oldPaused) {
    4133             :     // 6.1. Change the value of paused to false. (Already done.)
    4134             :     // This step is uplifted because the "block-media-playback" feature needs
    4135             :     // the mPaused to be false before UpdateAudioChannelPlayingState() being
    4136             :     // called.
    4137             : 
    4138             :     // 6.2. If the show poster flag is true, set the element's show poster flag
    4139             :     //      to false and run the time marches on steps.
    4140             : 
    4141             :     // 6.3. Queue a task to fire a simple event named play at the element.
    4142           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
    4143             : 
    4144             :     // 6.4. If the media element's readyState attribute has the value
    4145             :     //      HAVE_NOTHING, HAVE_METADATA, or HAVE_CURRENT_DATA, queue a task to
    4146             :     //      fire a simple event named waiting at the element.
    4147             :     //      Otherwise, the media element's readyState attribute has the value
    4148             :     //      HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA: notify about playing for the
    4149             :     //      element.
    4150           0 :     switch (mReadyState) {
    4151             :       case HAVE_NOTHING:
    4152           0 :         DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    4153           0 :         break;
    4154             :       case HAVE_METADATA:
    4155             :       case HAVE_CURRENT_DATA:
    4156           0 :         FireTimeUpdate(false);
    4157           0 :         DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    4158           0 :         break;
    4159             :       case HAVE_FUTURE_DATA:
    4160             :       case HAVE_ENOUGH_DATA:
    4161           0 :         FireTimeUpdate(false);
    4162           0 :         NotifyAboutPlaying();
    4163           0 :         break;
    4164             :     }
    4165           0 :   } else if (mReadyState >= HAVE_FUTURE_DATA) {
    4166             :     // 7. Otherwise, if the media element's readyState attribute has the value
    4167             :     //    HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, take pending play promises and
    4168             :     //    queue a task to resolve pending play promises with the result.
    4169           0 :     AsyncResolvePendingPlayPromises();
    4170             :   }
    4171             : 
    4172             :   // 8. Set the media element's autoplaying flag to false. (Already done.)
    4173             : 
    4174             :   // 9. Return promise.
    4175           0 :   return promise.forget();
    4176             : }
    4177             : 
    4178             : void
    4179           0 : HTMLMediaElement::MaybeDoLoad()
    4180             : {
    4181           0 :   if (mNetworkState == NETWORK_EMPTY) {
    4182           0 :     DoLoad();
    4183             :   }
    4184           0 : }
    4185             : 
    4186             : void
    4187           0 : HTMLMediaElement::UpdateWakeLock()
    4188             : {
    4189           0 :   MOZ_ASSERT(NS_IsMainThread());
    4190             :   // Ensure we have a wake lock if we're playing audibly. This ensures the
    4191             :   // device doesn't sleep while playing.
    4192           0 :   bool playing = !mPaused;
    4193             :   bool isAudible =
    4194           0 :     Volume() > 0.0 && !mMuted && mIsAudioTrackAudible;
    4195             :   // WakeLock when playing audible media.
    4196           0 :   if (playing && isAudible) {
    4197           0 :     WakeLockCreate();
    4198             :   } else {
    4199           0 :     WakeLockRelease();
    4200             :   }
    4201           0 : }
    4202             : 
    4203             : void
    4204           0 : HTMLMediaElement::WakeLockCreate()
    4205             : {
    4206           0 :   if (!mWakeLock) {
    4207             :     RefPtr<power::PowerManagerService> pmService =
    4208           0 :       power::PowerManagerService::GetInstance();
    4209           0 :     NS_ENSURE_TRUE_VOID(pmService);
    4210             : 
    4211           0 :     ErrorResult rv;
    4212           0 :     mWakeLock = pmService->NewWakeLock(
    4213           0 :       NS_LITERAL_STRING("audio-playing"), OwnerDoc()->GetInnerWindow(), rv);
    4214             :   }
    4215             : }
    4216             : 
    4217             : void
    4218           0 : HTMLMediaElement::WakeLockRelease()
    4219             : {
    4220           0 :   if (mWakeLock) {
    4221           0 :     ErrorResult rv;
    4222           0 :     mWakeLock->Unlock(rv);
    4223           0 :     rv.SuppressException();
    4224           0 :     mWakeLock = nullptr;
    4225             :   }
    4226           0 : }
    4227             : 
    4228           0 : HTMLMediaElement::OutputMediaStream::OutputMediaStream()
    4229             :   : mNextAvailableTrackID(1)
    4230             :   , mFinishWhenEnded(false)
    4231             :   , mCapturingAudioOnly(false)
    4232             :   , mCapturingDecoder(false)
    4233           0 :   , mCapturingMediaStream(false)
    4234             : {
    4235           0 : }
    4236             : 
    4237           0 : HTMLMediaElement::OutputMediaStream::~OutputMediaStream()
    4238             : {
    4239           0 :   for (auto pair : mTrackPorts) {
    4240           0 :     pair.second()->Destroy();
    4241             :   }
    4242           0 : }
    4243             : 
    4244             : void
    4245           0 : HTMLMediaElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
    4246             : {
    4247           0 :   if (!this->Controls() || !aVisitor.mEvent->mFlags.mIsTrusted) {
    4248           0 :     nsGenericHTMLElement::GetEventTargetParent(aVisitor);
    4249           0 :     return;
    4250             :   }
    4251             : 
    4252           0 :   HTMLInputElement* el = nullptr;
    4253           0 :   nsCOMPtr<nsINode> node;
    4254             : 
    4255             :   // We will need to trap pointer, touch, and mouse events within the media
    4256             :   // element, allowing media control exclusive consumption on these events,
    4257             :   // and preventing the content from handling them.
    4258           0 :   switch (aVisitor.mEvent->mMessage) {
    4259             :     case ePointerDown:
    4260             :     case ePointerUp:
    4261             :     case eTouchEnd:
    4262             :     // Always prevent touchmove captured in video element from being handled by
    4263             :     // content, since we always do that for touchstart.
    4264             :     case eTouchMove:
    4265             :     case eTouchStart:
    4266             :     case eMouseClick:
    4267             :     case eMouseDoubleClick:
    4268             :     case eMouseDown:
    4269             :     case eMouseUp:
    4270           0 :       aVisitor.mCanHandle = false;
    4271           0 :       return;
    4272             : 
    4273             :     // The *move events however are only comsumed when the range input is being
    4274             :     // dragged.
    4275             :     case ePointerMove:
    4276             :     case eMouseMove:
    4277           0 :       node = do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
    4278           0 :       if (node->IsInNativeAnonymousSubtree()) {
    4279           0 :         if (node->IsHTMLElement(nsGkAtoms::input)) {
    4280             :           // The node is a <input type="range">
    4281           0 :           el = static_cast<HTMLInputElement*>(node.get());
    4282           0 :         } else if (node->GetParentNode() &&
    4283           0 :                    node->GetParentNode()->IsHTMLElement(nsGkAtoms::input)) {
    4284             :           // The node is a child of <input type="range">
    4285           0 :           el = static_cast<HTMLInputElement*>(node->GetParentNode());
    4286             :         }
    4287             :       }
    4288           0 :       if (el && el->IsDraggingRange()) {
    4289           0 :         aVisitor.mCanHandle = false;
    4290           0 :         return;
    4291             :       }
    4292           0 :       nsGenericHTMLElement::GetEventTargetParent(aVisitor);
    4293           0 :       return;
    4294             : 
    4295             :     default:
    4296           0 :       nsGenericHTMLElement::GetEventTargetParent(aVisitor);
    4297           0 :       return;
    4298             :   }
    4299             : }
    4300             : 
    4301             : bool
    4302           0 : HTMLMediaElement::ParseAttribute(int32_t aNamespaceID,
    4303             :                                  nsAtom* aAttribute,
    4304             :                                  const nsAString& aValue,
    4305             :                                  nsIPrincipal* aMaybeScriptedPrincipal,
    4306             :                                  nsAttrValue& aResult)
    4307             : {
    4308             :   // Mappings from 'preload' attribute strings to an enumeration.
    4309             :   static const nsAttrValue::EnumTable kPreloadTable[] = {
    4310             :     { "", HTMLMediaElement::PRELOAD_ATTR_EMPTY },
    4311             :     { "none", HTMLMediaElement::PRELOAD_ATTR_NONE },
    4312             :     { "metadata", HTMLMediaElement::PRELOAD_ATTR_METADATA },
    4313             :     { "auto", HTMLMediaElement::PRELOAD_ATTR_AUTO },
    4314             :     { nullptr, 0 }
    4315             :   };
    4316             : 
    4317           1 :   if (aNamespaceID == kNameSpaceID_None) {
    4318           1 :     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
    4319             :       return true;
    4320             :     }
    4321           1 :     if (aAttribute == nsGkAtoms::crossorigin) {
    4322           0 :       ParseCORSValue(aValue, aResult);
    4323           0 :       return true;
    4324             :     }
    4325           1 :     if (aAttribute == nsGkAtoms::preload) {
    4326           0 :       return aResult.ParseEnumValue(aValue, kPreloadTable, false);
    4327             :     }
    4328             :   }
    4329             : 
    4330           0 :   return nsGenericHTMLElement::ParseAttribute(
    4331           1 :     aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult);
    4332             : }
    4333             : 
    4334             : void
    4335           0 : HTMLMediaElement::DoneCreatingElement()
    4336             : {
    4337           0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::muted)) {
    4338           0 :     mMuted |= MUTED_BY_CONTENT;
    4339             :   }
    4340           0 : }
    4341             : 
    4342             : bool
    4343           0 : HTMLMediaElement::IsHTMLFocusable(bool aWithMouse,
    4344             :                                   bool* aIsFocusable,
    4345             :                                   int32_t* aTabIndex)
    4346             : {
    4347           0 :   if (nsGenericHTMLElement::IsHTMLFocusable(
    4348             :         aWithMouse, aIsFocusable, aTabIndex)) {
    4349             :     return true;
    4350             :   }
    4351             : 
    4352           0 :   *aIsFocusable = true;
    4353           0 :   return false;
    4354             : }
    4355             : 
    4356             : int32_t
    4357           0 : HTMLMediaElement::TabIndexDefault()
    4358             : {
    4359           0 :   return 0;
    4360             : }
    4361             : 
    4362             : nsresult
    4363           1 : HTMLMediaElement::AfterSetAttr(int32_t aNameSpaceID,
    4364             :                                nsAtom* aName,
    4365             :                                const nsAttrValue* aValue,
    4366             :                                const nsAttrValue* aOldValue,
    4367             :                                nsIPrincipal* aMaybeScriptedPrincipal,
    4368             :                                bool aNotify)
    4369             : {
    4370           1 :   if (aNameSpaceID == kNameSpaceID_None) {
    4371           0 :     if (aName == nsGkAtoms::src) {
    4372           0 :       mSrcMediaSource = nullptr;
    4373             :       mSrcAttrTriggeringPrincipal = nsContentUtils::GetAttrTriggeringPrincipal(
    4374             :         this,
    4375           0 :         aValue ? aValue->GetStringValue() : EmptyString(),
    4376           0 :         aMaybeScriptedPrincipal);
    4377           0 :       if (aValue) {
    4378           0 :         nsString srcStr = aValue->GetStringValue();
    4379           0 :         nsCOMPtr<nsIURI> uri;
    4380           0 :         NewURIFromString(srcStr, getter_AddRefs(uri));
    4381           0 :         if (uri && IsMediaSourceURI(uri)) {
    4382             :           nsresult rv =
    4383           0 :             NS_GetSourceForMediaSourceURI(uri, getter_AddRefs(mSrcMediaSource));
    4384           0 :           if (NS_FAILED(rv)) {
    4385           0 :             nsAutoString spec;
    4386           0 :             GetCurrentSrc(spec);
    4387           0 :             const char16_t* params[] = { spec.get() };
    4388           0 :             ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
    4389             :           }
    4390             :         }
    4391             :       }
    4392           0 :     } else if (aName == nsGkAtoms::autoplay) {
    4393           0 :       if (aNotify) {
    4394           0 :         if (aValue) {
    4395           0 :           StopSuspendingAfterFirstFrame();
    4396           0 :           CheckAutoplayDataReady();
    4397             :         }
    4398             :         // This attribute can affect AddRemoveSelfReference
    4399           0 :         AddRemoveSelfReference();
    4400           0 :         UpdatePreloadAction();
    4401             :       }
    4402           1 :     } else if (aName == nsGkAtoms::preload) {
    4403           0 :       UpdatePreloadAction();
    4404           0 :     } else if (aName == nsGkAtoms::loop) {
    4405           0 :       if (mDecoder) {
    4406           0 :         mDecoder->SetLooping(!!aValue);
    4407             :       }
    4408             :     }
    4409             :   }
    4410             : 
    4411             :   // Since AfterMaybeChangeAttr may call DoLoad, make sure that it is called
    4412             :   // *after* any possible changes to mSrcMediaSource.
    4413           1 :   if (aValue) {
    4414           0 :     AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
    4415             :   }
    4416             : 
    4417           0 :   return nsGenericHTMLElement::AfterSetAttr(
    4418           0 :     aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
    4419             : }
    4420             : 
    4421             : nsresult
    4422           0 : HTMLMediaElement::OnAttrSetButNotChanged(int32_t aNamespaceID,
    4423             :                                          nsAtom* aName,
    4424             :                                          const nsAttrValueOrString& aValue,
    4425             :                                          bool aNotify)
    4426             : {
    4427           0 :   AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
    4428             : 
    4429           0 :   return nsGenericHTMLElement::OnAttrSetButNotChanged(
    4430           0 :     aNamespaceID, aName, aValue, aNotify);
    4431             : }
    4432             : 
    4433             : void
    4434           0 : HTMLMediaElement::AfterMaybeChangeAttr(int32_t aNamespaceID,
    4435             :                                        nsAtom* aName,
    4436             :                                        bool aNotify)
    4437             : {
    4438           1 :   if (aNamespaceID == kNameSpaceID_None) {
    4439           0 :     if (aName == nsGkAtoms::src) {
    4440           0 :       DoLoad();
    4441             :     }
    4442             :   }
    4443           1 : }
    4444             : 
    4445             : nsresult
    4446           0 : HTMLMediaElement::BindToTree(nsIDocument* aDocument,
    4447             :                              nsIContent* aParent,
    4448             :                              nsIContent* aBindingParent,
    4449             :                              bool aCompileEventHandlers)
    4450             : {
    4451           0 :   nsresult rv = nsGenericHTMLElement::BindToTree(
    4452           0 :     aDocument, aParent, aBindingParent, aCompileEventHandlers);
    4453             : 
    4454           1 :   mUnboundFromTree = false;
    4455             : 
    4456           1 :   if (aDocument) {
    4457             :     // The preload action depends on the value of the autoplay attribute.
    4458             :     // It's value may have changed, so update it.
    4459           1 :     UpdatePreloadAction();
    4460             :   }
    4461             : 
    4462           1 :   NotifyDecoderActivityChanges();
    4463             : 
    4464           0 :   return rv;
    4465             : }
    4466             : 
    4467             : /* static */
    4468             : void
    4469           0 : HTMLMediaElement::VideoDecodeSuspendTimerCallback(nsITimer* aTimer,
    4470             :                                                   void* aClosure)
    4471             : {
    4472           0 :   MOZ_ASSERT(NS_IsMainThread());
    4473           0 :   auto element = static_cast<HTMLMediaElement*>(aClosure);
    4474           0 :   element->mVideoDecodeSuspendTime.Start();
    4475           0 :   element->mVideoDecodeSuspendTimer = nullptr;
    4476           0 : }
    4477             : 
    4478             : void
    4479           0 : HTMLMediaElement::HiddenVideoStart()
    4480             : {
    4481           0 :   MOZ_ASSERT(NS_IsMainThread());
    4482           0 :   mHiddenPlayTime.Start();
    4483           0 :   if (mVideoDecodeSuspendTimer) {
    4484             :     // Already started, just keep it running.
    4485             :     return;
    4486             :   }
    4487           0 :   NS_NewTimerWithFuncCallback(
    4488           0 :     getter_AddRefs(mVideoDecodeSuspendTimer),
    4489             :     VideoDecodeSuspendTimerCallback,
    4490             :     this,
    4491             :     StaticPrefs::MediaSuspendBkgndVideoDelayMs(),
    4492             :     nsITimer::TYPE_ONE_SHOT,
    4493             :     "HTMLMediaElement::VideoDecodeSuspendTimerCallback",
    4494           0 :     mMainThreadEventTarget);
    4495             : }
    4496             : 
    4497             : void
    4498           2 : HTMLMediaElement::HiddenVideoStop()
    4499             : {
    4500           0 :   MOZ_ASSERT(NS_IsMainThread());
    4501           2 :   mHiddenPlayTime.Pause();
    4502           2 :   mVideoDecodeSuspendTime.Pause();
    4503           4 :   if (!mVideoDecodeSuspendTimer) {
    4504             :     return;
    4505             :   }
    4506           0 :   mVideoDecodeSuspendTimer->Cancel();
    4507           0 :   mVideoDecodeSuspendTimer = nullptr;
    4508             : }
    4509             : 
    4510             : void
    4511           0 : HTMLMediaElement::ReportTelemetry()
    4512             : {
    4513             :   // Report telemetry for videos when a page is unloaded. We
    4514             :   // want to know data on what state the video is at when
    4515             :   // the user has exited.
    4516             :   enum UnloadedState
    4517             :   {
    4518             :     ENDED = 0,
    4519             :     PAUSED = 1,
    4520             :     STALLED = 2,
    4521             :     SEEKING = 3,
    4522             :     OTHER = 4
    4523             :   };
    4524             : 
    4525           0 :   UnloadedState state = OTHER;
    4526           0 :   if (Seeking()) {
    4527             :     state = SEEKING;
    4528           0 :   } else if (Ended()) {
    4529             :     state = ENDED;
    4530           0 :   } else if (Paused()) {
    4531             :     state = PAUSED;
    4532             :   } else {
    4533             :     // For buffering we check if the current playback position is at the end
    4534             :     // of a buffered range, within a margin of error. We also consider to be
    4535             :     // buffering if the last frame status was buffering and the ready state is
    4536             :     // HAVE_CURRENT_DATA to account for times where we are in a buffering state
    4537             :     // regardless of what actual data we have buffered.
    4538           0 :     bool stalled = false;
    4539           0 :     RefPtr<TimeRanges> ranges = Buffered();
    4540           0 :     const double errorMargin = 0.05;
    4541           0 :     double t = CurrentTime();
    4542           0 :     TimeRanges::index_type index = ranges->Find(t, errorMargin);
    4543           0 :     stalled =
    4544           0 :       index != TimeRanges::NoIndex && (ranges->End(index) - t) < errorMargin;
    4545           0 :     stalled |= mDecoder &&
    4546           0 :                NextFrameStatus() ==
    4547           0 :                  MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING &&
    4548           0 :                mReadyState == HAVE_CURRENT_DATA;
    4549           0 :     if (stalled) {
    4550           0 :       state = STALLED;
    4551             :     }
    4552             :   }
    4553             : 
    4554           0 :   Telemetry::Accumulate(Telemetry::VIDEO_UNLOAD_STATE, state);
    4555           0 :   LOG(LogLevel::Debug, ("%p VIDEO_UNLOAD_STATE = %d", this, state));
    4556             : 
    4557           0 :   FrameStatisticsData data;
    4558             : 
    4559           0 :   if (HTMLVideoElement* vid = HTMLVideoElement::FromNodeOrNull(this)) {
    4560           0 :     FrameStatistics* stats = vid->GetFrameStatistics();
    4561           0 :     if (stats) {
    4562           0 :       data = stats->GetFrameStatisticsData();
    4563           0 :       if (data.mParsedFrames) {
    4564           0 :         MOZ_ASSERT(data.mDroppedFrames <= data.mParsedFrames);
    4565             :         // Dropped frames <= total frames, so 'percentage' cannot be higher than
    4566             :         // 100 and therefore can fit in a uint32_t (that Telemetry takes).
    4567           0 :         uint32_t percentage = 100 * data.mDroppedFrames / data.mParsedFrames;
    4568           0 :         LOG(LogLevel::Debug,
    4569             :             ("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK"));
    4570             :         Telemetry::Accumulate(Telemetry::VIDEO_DROPPED_FRAMES_PROPORTION,
    4571           0 :                               percentage);
    4572             :       }
    4573             :     }
    4574             :   }
    4575             : 
    4576           0 :   if (mMediaInfo.HasVideo() && mMediaInfo.mVideo.mImage.height > 0) {
    4577             :     // We have a valid video.
    4578           0 :     double playTime = mPlayTime.Total();
    4579           0 :     double hiddenPlayTime = mHiddenPlayTime.Total();
    4580           0 :     double videoDecodeSuspendTime = mVideoDecodeSuspendTime.Total();
    4581             : 
    4582           0 :     Telemetry::Accumulate(Telemetry::VIDEO_PLAY_TIME_MS,
    4583           0 :                           SECONDS_TO_MS(playTime));
    4584           0 :     LOG(LogLevel::Debug, ("%p VIDEO_PLAY_TIME_MS = %f", this, playTime));
    4585             : 
    4586           0 :     Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_MS,
    4587           0 :                           SECONDS_TO_MS(hiddenPlayTime));
    4588           0 :     LOG(LogLevel::Debug,
    4589             :         ("%p VIDEO_HIDDEN_PLAY_TIME_MS = %f", this, hiddenPlayTime));
    4590             : 
    4591           0 :     if (playTime > 0.0) {
    4592             :       // We have actually played something -> Report some valid-video telemetry.
    4593             : 
    4594             :       // Keyed by audio+video or video alone, and by a resolution range.
    4595           0 :       nsCString key(mMediaInfo.HasAudio() ? "AV," : "V,");
    4596             :       static const struct
    4597             :       {
    4598             :         int32_t mH;
    4599             :         const char* mRes;
    4600             :       } sResolutions[] = { { 240, "0<h<=240" },     { 480, "240<h<=480" },
    4601             :                            { 576, "480<h<=576" },   { 720, "576<h<=720" },
    4602             :                            { 1080, "720<h<=1080" }, { 2160, "1080<h<=2160" } };
    4603           0 :       const char* resolution = "h>2160";
    4604           0 :       int32_t height = mMediaInfo.mVideo.mImage.height;
    4605           0 :       for (const auto& res : sResolutions) {
    4606           0 :         if (height <= res.mH) {
    4607           0 :           resolution = res.mRes;
    4608           0 :           break;
    4609             :         }
    4610             :       }
    4611           0 :       key.AppendASCII(resolution);
    4612             : 
    4613             :       uint32_t hiddenPercentage =
    4614           0 :         uint32_t(hiddenPlayTime / playTime * 100.0 + 0.5);
    4615             :       Telemetry::Accumulate(
    4616           0 :         Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE, key, hiddenPercentage);
    4617             :       // Also accumulate all percentages in an "All" key.
    4618             :       Telemetry::Accumulate(Telemetry::VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE,
    4619           0 :                             NS_LITERAL_CSTRING("All"),
    4620           0 :                             hiddenPercentage);
    4621           0 :       LOG(LogLevel::Debug,
    4622             :           ("%p VIDEO_HIDDEN_PLAY_TIME_PERCENTAGE = %u, keys: '%s' and 'All'",
    4623             :            this,
    4624             :            hiddenPercentage,
    4625             :            key.get()));
    4626             : 
    4627             :       uint32_t videoDecodeSuspendPercentage =
    4628           0 :         uint32_t(videoDecodeSuspendTime / playTime * 100.0 + 0.5);
    4629             :       Telemetry::Accumulate(Telemetry::VIDEO_INFERRED_DECODE_SUSPEND_PERCENTAGE,
    4630             :                             key,
    4631           0 :                             videoDecodeSuspendPercentage);
    4632             :       Telemetry::Accumulate(Telemetry::VIDEO_INFERRED_DECODE_SUSPEND_PERCENTAGE,
    4633           0 :                             NS_LITERAL_CSTRING("All"),
    4634           0 :                             videoDecodeSuspendPercentage);
    4635           0 :       LOG(LogLevel::Debug,
    4636             :           ("%p VIDEO_INFERRED_DECODE_SUSPEND_PERCENTAGE = %u, keys: '%s' and "
    4637             :            "'All'",
    4638             :            this,
    4639             :            videoDecodeSuspendPercentage,
    4640             :            key.get()));
    4641             : 
    4642           0 :       if (data.mInterKeyframeCount != 0) {
    4643             :         uint32_t average_ms = uint32_t(
    4644           0 :           std::min<uint64_t>(double(data.mInterKeyframeSum_us) /
    4645           0 :                                  double(data.mInterKeyframeCount) / 1000.0 +
    4646             :                                0.5,
    4647           0 :                              UINT32_MAX));
    4648             :         Telemetry::Accumulate(
    4649           0 :           Telemetry::VIDEO_INTER_KEYFRAME_AVERAGE_MS, key, average_ms);
    4650             :         Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_AVERAGE_MS,
    4651           0 :                               NS_LITERAL_CSTRING("All"),
    4652           0 :                               average_ms);
    4653           0 :         LOG(LogLevel::Debug,
    4654             :             ("%p VIDEO_INTER_KEYFRAME_AVERAGE_MS = %u, keys: '%s' and 'All'",
    4655             :              this,
    4656             :              average_ms,
    4657             :              key.get()));
    4658             : 
    4659           0 :         uint32_t max_ms = uint32_t(std::min<uint64_t>(
    4660           0 :           (data.mInterKeyFrameMax_us + 500) / 1000, UINT32_MAX));
    4661             :         Telemetry::Accumulate(
    4662           0 :           Telemetry::VIDEO_INTER_KEYFRAME_MAX_MS, key, max_ms);
    4663             :         Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_MAX_MS,
    4664           0 :                               NS_LITERAL_CSTRING("All"),
    4665           0 :                               max_ms);
    4666           0 :         LOG(LogLevel::Debug,
    4667             :             ("%p VIDEO_INTER_KEYFRAME_MAX_MS = %u, keys: '%s' and 'All'",
    4668             :              this,
    4669             :              max_ms,
    4670             :              key.get()));
    4671             :       } else {
    4672             :         // Here, we have played *some* of the video, but didn't get more than 1
    4673             :         // keyframe. Report '0' if we have played for longer than the video-
    4674             :         // decode-suspend delay (showing recovery would be difficult).
    4675           0 :         uint32_t suspendDelay_ms = StaticPrefs::MediaSuspendBkgndVideoDelayMs();
    4676           0 :         if (uint32_t(playTime * 1000.0) > suspendDelay_ms) {
    4677           0 :           Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_MAX_MS, key, 0);
    4678             :           Telemetry::Accumulate(Telemetry::VIDEO_INTER_KEYFRAME_MAX_MS,
    4679           0 :                                 NS_LITERAL_CSTRING("All"),
    4680           0 :                                 0);
    4681           0 :           LOG(LogLevel::Debug,
    4682             :               ("%p VIDEO_INTER_KEYFRAME_MAX_MS = 0 (only 1 keyframe), keys: "
    4683             :                "'%s' and 'All'",
    4684             :                this,
    4685             :                key.get()));
    4686             :         }
    4687             :       }
    4688             :     }
    4689             :   }
    4690           0 : }
    4691             : 
    4692             : void
    4693           0 : HTMLMediaElement::UnbindFromTree(bool aDeep, bool aNullParent)
    4694             : {
    4695           0 :   mUnboundFromTree = true;
    4696           0 :   mVisibilityState = Visibility::UNTRACKED;
    4697             : 
    4698           0 :   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
    4699             : 
    4700           0 :   MOZ_ASSERT(IsHidden());
    4701           0 :   NotifyDecoderActivityChanges();
    4702             : 
    4703           0 :   RefPtr<HTMLMediaElement> self(this);
    4704             :   nsCOMPtr<nsIRunnable> task =
    4705           0 :     NS_NewRunnableFunction("dom::HTMLMediaElement::UnbindFromTree", [self]() {
    4706           0 :       if (self->mUnboundFromTree) {
    4707           0 :         self->Pause();
    4708             :       }
    4709           0 :     });
    4710           0 :   RunInStableState(task);
    4711           0 : }
    4712             : 
    4713             : static bool
    4714           0 : IsVP9InMP4(const MediaContainerType& aContainerType)
    4715             : {
    4716           0 :   const MediaContainerType mimeType(aContainerType.Type());
    4717           0 :   return DecoderTraits::IsMP4SupportedType(
    4718             :            mimeType,
    4719           0 :            /* DecoderDoctorDiagnostics* */ nullptr) &&
    4720           0 :          IsVP9CodecString(aContainerType.ExtendedType().Codecs().AsString());
    4721             : }
    4722             : 
    4723             : /* static */
    4724             : CanPlayStatus
    4725           0 : HTMLMediaElement::GetCanPlay(const nsAString& aType,
    4726             :                              DecoderDoctorDiagnostics* aDiagnostics)
    4727             : {
    4728           0 :   Maybe<MediaContainerType> containerType = MakeMediaContainerType(aType);
    4729           0 :   if (!containerType) {
    4730             :     return CANPLAY_NO;
    4731             :   }
    4732             :   CanPlayStatus status =
    4733           0 :     DecoderTraits::CanHandleContainerType(*containerType, aDiagnostics);
    4734           0 :   if (status == CANPLAY_YES && IsVP9InMP4(*containerType)) {
    4735             :     // We don't have a demuxer that can handle VP9 in non-fragmented MP4.
    4736             :     // So special-case VP9 in MP4 here, as we assume canPlayType() implies
    4737             :     // non-fragmented MP4 anyway. Note we report that we can play VP9
    4738             :     // in MP4 in MediaSource.isTypeSupported(), as the fragmented MP4
    4739             :     // demuxer can handle VP9 in fragmented MP4.
    4740             :     return CANPLAY_NO;
    4741             :   }
    4742           0 :   if (status == CANPLAY_YES &&
    4743           0 :       (*containerType).ExtendedType().Codecs().IsEmpty()) {
    4744             :     // Per spec: 'Generally, a user agent should never return "probably" for a
    4745             :     // type that allows the `codecs` parameter if that parameter is not
    4746             :     // present.' As all our currently-supported types allow for `codecs`, we can
    4747             :     // do this check here.
    4748             :     // TODO: Instead, missing `codecs` should be checked in each decoder's
    4749             :     // `IsSupportedType` call from `CanHandleCodecsType()`.
    4750             :     // See bug 1399023.
    4751             :     return CANPLAY_MAYBE;
    4752             :   }
    4753           0 :   return status;
    4754             : }
    4755             : 
    4756             : void
    4757           0 : HTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
    4758             : {
    4759           0 :   DecoderDoctorDiagnostics diagnostics;
    4760           0 :   CanPlayStatus canPlay = GetCanPlay(aType, &diagnostics);
    4761           0 :   diagnostics.StoreFormatDiagnostics(
    4762           0 :     OwnerDoc(), aType, canPlay != CANPLAY_NO, __func__);
    4763           0 :   switch (canPlay) {
    4764             :     case CANPLAY_NO:
    4765           0 :       aResult.Truncate();
    4766           0 :       break;
    4767             :     case CANPLAY_YES:
    4768           0 :       aResult.AssignLiteral("probably");
    4769           0 :       break;
    4770             :     case CANPLAY_MAYBE:
    4771           0 :       aResult.AssignLiteral("maybe");
    4772           0 :       break;
    4773             :     default:
    4774           0 :       MOZ_ASSERT_UNREACHABLE("Unexpected case.");
    4775             :       break;
    4776             :   }
    4777             : 
    4778           0 :   LOG(LogLevel::Debug,
    4779             :       ("%p CanPlayType(%s) = \"%s\"",
    4780             :        this,
    4781             :        NS_ConvertUTF16toUTF8(aType).get(),
    4782             :        NS_ConvertUTF16toUTF8(aResult).get()));
    4783           0 : }
    4784             : 
    4785             : void
    4786           0 : HTMLMediaElement::AssertReadyStateIsNothing()
    4787             : {
    4788             : #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    4789           0 :   if (mReadyState != HAVE_NOTHING) {
    4790             :     char buf[1024];
    4791           0 :     SprintfLiteral(buf,
    4792             :                    "readyState=%d networkState=%d mLoadWaitStatus=%d "
    4793             :                    "mSourceLoadCandidate=%d "
    4794             :                    "mIsLoadingFromSourceChildren=%d mPreloadAction=%d "
    4795             :                    "mSuspendedForPreloadNone=%d error=%d",
    4796           0 :                    int(mReadyState),
    4797           0 :                    int(mNetworkState),
    4798           0 :                    int(mLoadWaitStatus),
    4799           0 :                    !!mSourceLoadCandidate,
    4800           0 :                    mIsLoadingFromSourceChildren,
    4801           0 :                    int(mPreloadAction),
    4802           0 :                    mSuspendedForPreloadNone,
    4803           0 :                    GetError() ? GetError()->Code() : 0);
    4804           0 :     MOZ_CRASH_UNSAFE_PRINTF("ReadyState should be HAVE_NOTHING! %s", buf);
    4805             :   }
    4806             : #endif
    4807           0 : }
    4808             : 
    4809             : nsresult
    4810           0 : HTMLMediaElement::InitializeDecoderAsClone(ChannelMediaDecoder* aOriginal)
    4811             : {
    4812           0 :   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
    4813           0 :   NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
    4814           0 :   AssertReadyStateIsNothing();
    4815             : 
    4816             :   MediaDecoderInit decoderInit(this,
    4817           0 :                                mMuted ? 0.0 : mVolume,
    4818           0 :                                mPreservesPitch,
    4819             :                                mPlaybackRate,
    4820           0 :                                mPreloadAction ==
    4821             :                                  HTMLMediaElement::PRELOAD_METADATA,
    4822           0 :                                mHasSuspendTaint,
    4823           0 :                                HasAttr(kNameSpaceID_None, nsGkAtoms::loop),
    4824           0 :                                aOriginal->ContainerType());
    4825             : 
    4826           0 :   RefPtr<ChannelMediaDecoder> decoder = aOriginal->Clone(decoderInit);
    4827           0 :   if (!decoder)
    4828             :     return NS_ERROR_FAILURE;
    4829             : 
    4830           0 :   LOG(LogLevel::Debug,
    4831             :       ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
    4832             : 
    4833           0 :   return FinishDecoderSetup(decoder);
    4834             : }
    4835             : 
    4836             : template<typename DecoderType, typename... LoadArgs>
    4837             : nsresult
    4838           0 : HTMLMediaElement::SetupDecoder(DecoderType* aDecoder, LoadArgs&&... aArgs)
    4839             : {
    4840           0 :   LOG(LogLevel::Debug,
    4841             :       ("%p Created decoder %p for type %s",
    4842             :        this,
    4843             :        aDecoder,
    4844             :        aDecoder->ContainerType().OriginalString().Data()));
    4845             : 
    4846           0 :   nsresult rv = aDecoder->Load(std::forward<LoadArgs>(aArgs)...);
    4847           0 :   if (NS_FAILED(rv)) {
    4848           0 :     aDecoder->Shutdown();
    4849           0 :     LOG(LogLevel::Debug, ("%p Failed to load for decoder %p", this, aDecoder));
    4850             :     return rv;
    4851             :   }
    4852             : 
    4853           0 :   rv = FinishDecoderSetup(aDecoder);
    4854             :   // Only ChannelMediaDecoder supports resource cloning.
    4855           0 :   if (IsSame<DecoderType, ChannelMediaDecoder>::value && NS_SUCCEEDED(rv)) {
    4856           0 :     AddMediaElementToURITable();
    4857           0 :     NS_ASSERTION(
    4858             :       MediaElementTableCount(this, mLoadingSrc) == 1,
    4859             :       "Media element should have single table entry if decode initialized");
    4860             :   }
    4861             : 
    4862             :   return rv;
    4863             : }
    4864             : 
    4865             : nsresult
    4866           0 : HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
    4867             :                                               nsIStreamListener** aListener)
    4868             : {
    4869           0 :   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
    4870           0 :   AssertReadyStateIsNothing();
    4871             : 
    4872           0 :   DecoderDoctorDiagnostics diagnostics;
    4873             : 
    4874           0 :   nsAutoCString mimeType;
    4875           0 :   aChannel->GetContentType(mimeType);
    4876           0 :   NS_ASSERTION(!mimeType.IsEmpty(), "We should have the Content-Type.");
    4877           0 :   NS_ConvertUTF8toUTF16 mimeUTF16(mimeType);
    4878             : 
    4879           0 :   RefPtr<HTMLMediaElement> self = this;
    4880           0 :   auto reportCanPlay = [&, self](bool aCanPlay) {
    4881           0 :     diagnostics.StoreFormatDiagnostics(
    4882           0 :       self->OwnerDoc(), mimeUTF16, aCanPlay, __func__);
    4883           0 :     if (!aCanPlay) {
    4884           0 :       nsAutoString src;
    4885           0 :       self->GetCurrentSrc(src);
    4886           0 :       const char16_t* params[] = { mimeUTF16.get(), src.get() };
    4887           0 :       self->ReportLoadError(
    4888           0 :         "MediaLoadUnsupportedMimeType", params, ArrayLength(params));
    4889             :     }
    4890           0 :   };
    4891             : 
    4892           0 :   auto onExit = MakeScopeExit([self] {
    4893           0 :     if (self->mChannelLoader) {
    4894           0 :       self->mChannelLoader->Done();
    4895           0 :       self->mChannelLoader = nullptr;
    4896             :     }
    4897           0 :   });
    4898             : 
    4899           0 :   Maybe<MediaContainerType> containerType = MakeMediaContainerType(mimeType);
    4900           0 :   if (!containerType) {
    4901           0 :     reportCanPlay(false);
    4902           0 :     return NS_ERROR_FAILURE;
    4903             :   }
    4904             : 
    4905             :   MediaDecoderInit decoderInit(this,
    4906           0 :                                mMuted ? 0.0 : mVolume,
    4907           0 :                                mPreservesPitch,
    4908             :                                mPlaybackRate,
    4909           0 :                                mPreloadAction ==
    4910             :                                  HTMLMediaElement::PRELOAD_METADATA,
    4911           0 :                                mHasSuspendTaint,
    4912           0 :                                HasAttr(kNameSpaceID_None, nsGkAtoms::loop),
    4913           0 :                                *containerType);
    4914             : 
    4915             : #ifdef MOZ_ANDROID_HLS_SUPPORT
    4916             :   if (HLSDecoder::IsSupportedType(*containerType)) {
    4917             :     RefPtr<HLSDecoder> decoder = new HLSDecoder(decoderInit);
    4918             :     reportCanPlay(true);
    4919             :     return SetupDecoder(decoder.get(), aChannel);
    4920             :   }
    4921             : #endif
    4922             : 
    4923             :   RefPtr<ChannelMediaDecoder> decoder =
    4924           0 :     ChannelMediaDecoder::Create(decoderInit, &diagnostics);
    4925           0 :   if (!decoder) {
    4926           0 :     reportCanPlay(false);
    4927           0 :     return NS_ERROR_FAILURE;
    4928             :   }
    4929             : 
    4930           0 :   reportCanPlay(true);
    4931           0 :   bool isPrivateBrowsing = NodePrincipal()->GetPrivateBrowsingId() > 0;
    4932           0 :   return SetupDecoder(decoder.get(), aChannel, isPrivateBrowsing, aListener);
    4933             : }
    4934             : 
    4935             : nsresult
    4936           0 : HTMLMediaElement::FinishDecoderSetup(MediaDecoder* aDecoder)
    4937             : {
    4938           0 :   ChangeNetworkState(NETWORK_LOADING);
    4939             : 
    4940             :   // Set mDecoder now so if methods like GetCurrentSrc get called between
    4941             :   // here and Load(), they work.
    4942           0 :   SetDecoder(aDecoder);
    4943             : 
    4944             :   // Notify the decoder of the initial activity status.
    4945           0 :   NotifyDecoderActivityChanges();
    4946             : 
    4947             :   // Update decoder principal before we start decoding, since it
    4948             :   // can affect how we feed data to MediaStreams
    4949           0 :   NotifyDecoderPrincipalChanged();
    4950             : 
    4951           0 :   for (OutputMediaStream& ms : mOutputStreams) {
    4952           0 :     if (ms.mCapturingMediaStream) {
    4953           0 :       MOZ_ASSERT(!ms.mCapturingDecoder);
    4954             :       continue;
    4955             :     }
    4956             : 
    4957           0 :     ms.mCapturingDecoder = true;
    4958           0 :     aDecoder->AddOutputStream(ms.mStream->GetInputStream()->AsProcessedStream(),
    4959             :                               ms.mNextAvailableTrackID,
    4960           0 :                               ms.mFinishWhenEnded);
    4961             :   }
    4962             : 
    4963           0 :   if (mMediaKeys) {
    4964           0 :     if (mMediaKeys->GetCDMProxy()) {
    4965           0 :       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
    4966             :     } else {
    4967             :       // CDM must have crashed.
    4968           0 :       ShutdownDecoder();
    4969           0 :       return NS_ERROR_FAILURE;
    4970             :     }
    4971             :   }
    4972             : 
    4973           0 :   if (mChannelLoader) {
    4974           0 :     mChannelLoader->Done();
    4975           0 :     mChannelLoader = nullptr;
    4976             :   }
    4977             : 
    4978             :   // We may want to suspend the new stream now.
    4979             :   // This will also do an AddRemoveSelfReference.
    4980           0 :   NotifyOwnerDocumentActivityChanged();
    4981             : 
    4982           0 :   if (mPausedForInactiveDocumentOrChannel) {
    4983           0 :     mDecoder->Suspend();
    4984             :   }
    4985             : 
    4986           0 :   if (!mPaused) {
    4987           0 :     SetPlayedOrSeeked(true);
    4988           0 :     if (!mPausedForInactiveDocumentOrChannel) {
    4989           0 :       mDecoder->Play();
    4990             :     }
    4991             :   }
    4992             : 
    4993             :   return NS_OK;
    4994             : }
    4995             : 
    4996           0 : class HTMLMediaElement::StreamListener : public MediaStreamListener
    4997             : {
    4998             : public:
    4999           0 :   StreamListener(HTMLMediaElement* aElement, const char* aName)
    5000           0 :     : mElement(aElement)
    5001             :     , mHaveCurrentData(false)
    5002             :     , mFinished(false)
    5003             :     , mMutex(aName)
    5004           0 :     , mPendingNotifyOutput(false)
    5005             :   {
    5006           0 :   }
    5007           0 :   void Forget()
    5008             :   {
    5009           0 :     if (mElement) {
    5010           0 :       HTMLMediaElement* element = mElement;
    5011           0 :       mElement = nullptr;
    5012           0 :       element->UpdateReadyStateInternal();
    5013             :     }
    5014           0 :   }
    5015             : 
    5016             :   // Main thread
    5017             : 
    5018           0 :   MediaDecoderOwner::NextFrameStatus NextFrameStatus()
    5019             :   {
    5020           0 :     if (!mElement || !mHaveCurrentData || mFinished) {
    5021             :       return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
    5022             :     }
    5023           0 :     return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
    5024             :   }
    5025             : 
    5026           0 :   void DoNotifyOutput()
    5027             :   {
    5028             :     {
    5029           0 :       MutexAutoLock lock(mMutex);
    5030           0 :       mPendingNotifyOutput = false;
    5031             :     }
    5032           0 :     if (mElement && mHaveCurrentData) {
    5033           0 :       RefPtr<HTMLMediaElement> kungFuDeathGrip = mElement;
    5034           0 :       kungFuDeathGrip->FireTimeUpdate(true);
    5035             :     }
    5036           0 :   }
    5037           0 :   void DoNotifyHaveCurrentData()
    5038             :   {
    5039           0 :     mHaveCurrentData = true;
    5040           0 :     if (mElement) {
    5041           0 :       RefPtr<HTMLMediaElement> kungFuDeathGrip = mElement;
    5042           0 :       kungFuDeathGrip->FirstFrameLoaded();
    5043           0 :       kungFuDeathGrip->UpdateReadyStateInternal();
    5044             :     }
    5045           0 :     DoNotifyOutput();
    5046           0 :   }
    5047             : 
    5048             :   // These notifications run on the media graph thread so we need to
    5049             :   // dispatch events to the main thread.
    5050           0 :   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) override
    5051             :   {
    5052           0 :     MutexAutoLock lock(mMutex);
    5053           0 :     aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod(
    5054             :       "dom::HTMLMediaElement::StreamListener::DoNotifyHaveCurrentData",
    5055             :       this,
    5056           0 :       &StreamListener::DoNotifyHaveCurrentData));
    5057           0 :   }
    5058           0 :   virtual void NotifyOutput(MediaStreamGraph* aGraph,
    5059             :                             GraphTime aCurrentTime) override
    5060             :   {
    5061           0 :     MutexAutoLock lock(mMutex);
    5062           0 :     if (mPendingNotifyOutput)
    5063           0 :       return;
    5064           0 :     mPendingNotifyOutput = true;
    5065           0 :     aGraph->DispatchToMainThreadAfterStreamStateUpdate(
    5066           0 :       NewRunnableMethod("dom::HTMLMediaElement::StreamListener::DoNotifyOutput",
    5067             :                         this,
    5068           0 :                         &StreamListener::DoNotifyOutput));
    5069             :   }
    5070             : 
    5071             : private:
    5072             :   // These fields may only be accessed on the main thread
    5073             :   HTMLMediaElement* mElement;
    5074             :   bool mHaveCurrentData;
    5075             :   bool mFinished;
    5076             : 
    5077             :   // mMutex protects the fields below; they can be accessed on any thread
    5078             :   Mutex mMutex;
    5079             :   bool mPendingNotifyOutput;
    5080             : };
    5081             : 
    5082           0 : class HTMLMediaElement::MediaStreamTracksAvailableCallback
    5083             :   : public OnTracksAvailableCallback
    5084             : {
    5085             : public:
    5086           0 :   explicit MediaStreamTracksAvailableCallback(HTMLMediaElement* aElement)
    5087           0 :     : OnTracksAvailableCallback()
    5088           0 :     , mElement(aElement)
    5089             :   {
    5090           0 :   }
    5091           0 :   virtual void NotifyTracksAvailable(DOMMediaStream* aStream) override
    5092             :   {
    5093           0 :     NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    5094             : 
    5095           0 :     if (!mElement) {
    5096             :       return;
    5097             :     }
    5098           0 :     mElement->NotifyMediaStreamTracksAvailable(aStream);
    5099             :   }
    5100             : 
    5101             : private:
    5102             :   WeakPtr<HTMLMediaElement> mElement;
    5103             : };
    5104             : 
    5105           0 : class HTMLMediaElement::MediaStreamTrackListener
    5106             :   : public DOMMediaStream::TrackListener
    5107             : {
    5108             : public:
    5109             :   explicit MediaStreamTrackListener(HTMLMediaElement* aElement)
    5110           0 :     : mElement(aElement)
    5111             :   {
    5112             :   }
    5113             : 
    5114           0 :   void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override
    5115             :   {
    5116           0 :     mElement->NotifyMediaStreamTrackAdded(aTrack);
    5117           0 :   }
    5118             : 
    5119           0 :   void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) override
    5120             :   {
    5121           0 :     mElement->NotifyMediaStreamTrackRemoved(aTrack);
    5122           0 :   }
    5123             : 
    5124           0 :   void NotifyActive() override
    5125             :   {
    5126           0 :     LOG(LogLevel::Debug,
    5127             :         ("%p, mSrcStream %p became active",
    5128             :          mElement,
    5129             :          mElement->mSrcStream.get()));
    5130           0 :     mElement->CheckAutoplayDataReady();
    5131           0 :   }
    5132             : 
    5133           0 :   void NotifyInactive() override
    5134             :   {
    5135           0 :     LOG(LogLevel::Debug,
    5136             :         ("%p, mSrcStream %p became inactive",
    5137             :          mElement,
    5138             :          mElement->mSrcStream.get()));
    5139           0 :     MOZ_ASSERT(!mElement->mSrcStream->Active());
    5140           0 :     if (mElement->mMediaStreamListener) {
    5141           0 :       mElement->mMediaStreamListener->Forget();
    5142             :     }
    5143           0 :     mElement->PlaybackEnded();
    5144           0 :   }
    5145             : 
    5146             : protected:
    5147             :   HTMLMediaElement* const mElement;
    5148             : };
    5149             : 
    5150             : void
    5151           0 : HTMLMediaElement::UpdateSrcMediaStreamPlaying(uint32_t aFlags)
    5152             : {
    5153           0 :   if (!mSrcStream) {
    5154             :     return;
    5155             :   }
    5156             :   // We might be in cycle collection with mSrcStream->GetPlaybackStream()
    5157             :   // already returning null due to unlinking.
    5158             : 
    5159           0 :   MediaStream* stream = GetSrcMediaStream();
    5160           0 :   bool shouldPlay = !(aFlags & REMOVING_SRC_STREAM) && !mPaused &&
    5161           0 :                     !mPausedForInactiveDocumentOrChannel && stream;
    5162           0 :   if (shouldPlay == mSrcStreamIsPlaying) {
    5163             :     return;
    5164             :   }
    5165           0 :   mSrcStreamIsPlaying = shouldPlay;
    5166             : 
    5167           0 :   LOG(LogLevel::Debug,
    5168             :       ("MediaElement %p %s playback of DOMMediaStream %p",
    5169             :        this,
    5170             :        shouldPlay ? "Setting up" : "Removing",
    5171             :        mSrcStream.get()));
    5172             : 
    5173           0 :   if (shouldPlay) {
    5174           0 :     mSrcStreamPausedCurrentTime = -1;
    5175             : 
    5176             :     mMediaStreamListener =
    5177           0 :       new StreamListener(this, "HTMLMediaElement::mMediaStreamListener");
    5178           0 :     stream->AddListener(mMediaStreamListener);
    5179             : 
    5180           0 :     stream->AddAudioOutput(this);
    5181           0 :     SetVolumeInternal();
    5182             : 
    5183           0 :     VideoFrameContainer* container = GetVideoFrameContainer();
    5184           0 :     if (mSelectedVideoStreamTrack && container) {
    5185           0 :       mSelectedVideoStreamTrack->AddVideoOutput(container);
    5186             :     }
    5187             : 
    5188           0 :     SetCapturedOutputStreamsEnabled(true); // Unmute
    5189             :     // If the input is a media stream, we don't check its data and always regard
    5190             :     // it as audible when it's playing.
    5191           0 :     SetAudibleState(true);
    5192             :   } else {
    5193           0 :     if (stream) {
    5194           0 :       mSrcStreamPausedCurrentTime = CurrentTime();
    5195             : 
    5196           0 :       stream->RemoveListener(mMediaStreamListener);
    5197             : 
    5198           0 :       stream->RemoveAudioOutput(this);
    5199           0 :       VideoFrameContainer* container = GetVideoFrameContainer();
    5200           0 :       if (mSelectedVideoStreamTrack && container) {
    5201           0 :         mSelectedVideoStreamTrack->RemoveVideoOutput(container);
    5202             :       }
    5203             : 
    5204           0 :       SetCapturedOutputStreamsEnabled(false); // Mute
    5205             :     }
    5206             :     // If stream is null, then DOMMediaStream::Destroy must have been
    5207             :     // called and that will remove all listeners/outputs.
    5208             : 
    5209           0 :     mMediaStreamListener->Forget();
    5210           0 :     mMediaStreamListener = nullptr;
    5211             :   }
    5212             : }
    5213             : 
    5214             : void
    5215           0 : HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
    5216             : {
    5217           0 :   NS_ASSERTION(!mSrcStream && !mMediaStreamListener &&
    5218             :                  !mMediaStreamSizeListener,
    5219             :                "Should have been ended already");
    5220             : 
    5221           0 :   mSrcStream = aStream;
    5222             : 
    5223           0 :   nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
    5224           0 :   if (!window) {
    5225           0 :     return;
    5226             :   }
    5227             : 
    5228           0 :   UpdateSrcMediaStreamPlaying();
    5229             : 
    5230             :   // If we pause this media element, track changes in the underlying stream
    5231             :   // will continue to fire events at this element and alter its track list.
    5232             :   // That's simpler than delaying the events, but probably confusing...
    5233           0 :   nsTArray<RefPtr<MediaStreamTrack>> tracks;
    5234           0 :   mSrcStream->GetTracks(tracks);
    5235           0 :   for (const RefPtr<MediaStreamTrack>& track : tracks) {
    5236           0 :     NotifyMediaStreamTrackAdded(track);
    5237             :   }
    5238             : 
    5239           0 :   mSrcStream->OnTracksAvailable(new MediaStreamTracksAvailableCallback(this));
    5240           0 :   mMediaStreamTrackListener = new MediaStreamTrackListener(this);
    5241           0 :   mSrcStream->RegisterTrackListener(mMediaStreamTrackListener);
    5242             : 
    5243           0 :   mSrcStream->AddPrincipalChangeObserver(this);
    5244           0 :   mSrcStreamVideoPrincipal = mSrcStream->GetVideoPrincipal();
    5245             : 
    5246           0 :   ChangeNetworkState(NETWORK_IDLE);
    5247           0 :   ChangeDelayLoadStatus(false);
    5248           0 :   CheckAutoplayDataReady();
    5249             : 
    5250             :   // FirstFrameLoaded() will be called when the stream has current data.
    5251             : }
    5252             : 
    5253             : void
    5254           0 : HTMLMediaElement::EndSrcMediaStreamPlayback()
    5255             : {
    5256           0 :   MOZ_ASSERT(mSrcStream);
    5257             : 
    5258           0 :   UpdateSrcMediaStreamPlaying(REMOVING_SRC_STREAM);
    5259             : 
    5260           0 :   if (mMediaStreamSizeListener) {
    5261           0 :     MOZ_ASSERT(mSelectedVideoStreamTrack);
    5262           0 :     if (mSelectedVideoStreamTrack) {
    5263           0 :       mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
    5264             :     }
    5265           0 :     mMediaStreamSizeListener->Forget();
    5266             :   }
    5267           0 :   mSelectedVideoStreamTrack = nullptr;
    5268           0 :   mMediaStreamSizeListener = nullptr;
    5269             : 
    5270           0 :   mSrcStream->UnregisterTrackListener(mMediaStreamTrackListener);
    5271           0 :   mMediaStreamTrackListener = nullptr;
    5272           0 :   mSrcStreamTracksAvailable = false;
    5273             : 
    5274           0 :   mSrcStream->RemovePrincipalChangeObserver(this);
    5275           0 :   mSrcStreamVideoPrincipal = nullptr;
    5276             : 
    5277           0 :   for (OutputMediaStream& ms : mOutputStreams) {
    5278           0 :     for (auto pair : ms.mTrackPorts) {
    5279           0 :       pair.second()->Destroy();
    5280             :     }
    5281           0 :     ms.mTrackPorts.Clear();
    5282             :   }
    5283             : 
    5284           0 :   mSrcStream = nullptr;
    5285           0 : }
    5286             : 
    5287             : static already_AddRefed<AudioTrack>
    5288           0 : CreateAudioTrack(AudioStreamTrack* aStreamTrack, nsIGlobalObject* aOwnerGlobal)
    5289             : {
    5290           0 :   nsAutoString id;
    5291           0 :   nsAutoString label;
    5292           0 :   aStreamTrack->GetId(id);
    5293           0 :   aStreamTrack->GetLabel(label, CallerType::System);
    5294             : 
    5295             :   return MediaTrackList::CreateAudioTrack(
    5296           0 :     aOwnerGlobal, id, NS_LITERAL_STRING("main"), label, EmptyString(), true);
    5297             : }
    5298             : 
    5299             : static already_AddRefed<VideoTrack>
    5300           0 : CreateVideoTrack(VideoStreamTrack* aStreamTrack, nsIGlobalObject* aOwnerGlobal)
    5301             : {
    5302           0 :   nsAutoString id;
    5303           0 :   nsAutoString label;
    5304           0 :   aStreamTrack->GetId(id);
    5305           0 :   aStreamTrack->GetLabel(label, CallerType::System);
    5306             : 
    5307             :   return MediaTrackList::CreateVideoTrack(aOwnerGlobal,
    5308             :                                           id,
    5309           0 :                                           NS_LITERAL_STRING("main"),
    5310             :                                           label,
    5311           0 :                                           EmptyString(),
    5312           0 :                                           aStreamTrack);
    5313             : }
    5314             : 
    5315             : void
    5316           0 : HTMLMediaElement::NotifyMediaStreamTrackAdded(
    5317             :   const RefPtr<MediaStreamTrack>& aTrack)
    5318             : {
    5319           0 :   MOZ_ASSERT(aTrack);
    5320             : 
    5321           0 :   if (aTrack->Ended()) {
    5322           0 :     return;
    5323             :   }
    5324             : 
    5325             : #ifdef DEBUG
    5326           0 :   nsString id;
    5327           0 :   aTrack->GetId(id);
    5328             : 
    5329           0 :   LOG(LogLevel::Debug,
    5330             :       ("%p, Adding %sTrack with id %s",
    5331             :        this,
    5332             :        aTrack->AsAudioStreamTrack() ? "Audio" : "Video",
    5333             :        NS_ConvertUTF16toUTF8(id).get()));
    5334             : #endif
    5335             : 
    5336           0 :   if (AudioStreamTrack* t = aTrack->AsAudioStreamTrack()) {
    5337             :     RefPtr<AudioTrack> audioTrack =
    5338           0 :       CreateAudioTrack(t, AudioTracks()->GetOwnerGlobal());
    5339           0 :     AudioTracks()->AddTrack(audioTrack);
    5340           0 :   } else if (VideoStreamTrack* t = aTrack->AsVideoStreamTrack()) {
    5341             :     // TODO: Fix this per the spec on bug 1273443.
    5342           0 :     if (!IsVideo()) {
    5343           0 :       return;
    5344             :     }
    5345             :     RefPtr<VideoTrack> videoTrack =
    5346           0 :       CreateVideoTrack(t, VideoTracks()->GetOwnerGlobal());
    5347           0 :     VideoTracks()->AddTrack(videoTrack);
    5348             :     // New MediaStreamTrack added, set the new added video track as selected
    5349             :     // video track when there is no selected track.
    5350           0 :     if (VideoTracks()->SelectedIndex() == -1) {
    5351           0 :       MOZ_ASSERT(!mSelectedVideoStreamTrack);
    5352           0 :       videoTrack->SetEnabledInternal(true, MediaTrack::FIRE_NO_EVENTS);
    5353             :     }
    5354             :   }
    5355             : 
    5356           0 :   UpdateReadyStateInternal();
    5357             : }
    5358             : 
    5359             : void
    5360           0 : HTMLMediaElement::NotifyMediaStreamTrackRemoved(
    5361             :   const RefPtr<MediaStreamTrack>& aTrack)
    5362             : {
    5363           0 :   MOZ_ASSERT(aTrack);
    5364             : 
    5365           0 :   nsAutoString id;
    5366           0 :   aTrack->GetId(id);
    5367             : 
    5368           0 :   LOG(LogLevel::Debug,
    5369             :       ("%p, Removing %sTrack with id %s",
    5370             :        this,
    5371             :        aTrack->AsAudioStreamTrack() ? "Audio" : "Video",
    5372             :        NS_ConvertUTF16toUTF8(id).get()));
    5373             : 
    5374           0 :   if (MediaTrack* t = AudioTracks()->GetTrackById(id)) {
    5375           0 :     AudioTracks()->RemoveTrack(t);
    5376           0 :   } else if (MediaTrack* t = VideoTracks()->GetTrackById(id)) {
    5377           0 :     VideoTracks()->RemoveTrack(t);
    5378             :   } else {
    5379           0 :     NS_ASSERTION(aTrack->AsVideoStreamTrack() && !IsVideo(),
    5380             :                  "MediaStreamTrack ended but did not exist in track lists. "
    5381             :                  "This is only allowed if a video element ends and we are an "
    5382             :                  "audio element.");
    5383           0 :     return;
    5384             :   }
    5385             : }
    5386             : 
    5387             : void
    5388           0 : HTMLMediaElement::ProcessMediaFragmentURI()
    5389             : {
    5390           0 :   nsMediaFragmentURIParser parser(mLoadingSrc);
    5391             : 
    5392           0 :   if (mDecoder && parser.HasEndTime()) {
    5393           0 :     mFragmentEnd = parser.GetEndTime();
    5394             :   }
    5395             : 
    5396           0 :   if (parser.HasStartTime()) {
    5397           0 :     SetCurrentTime(parser.GetStartTime());
    5398           0 :     mFragmentStart = parser.GetStartTime();
    5399             :   }
    5400           0 : }
    5401             : 
    5402             : void
    5403           0 : HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
    5404             :                                  UniquePtr<const MetadataTags> aTags)
    5405             : {
    5406           0 :   MOZ_ASSERT(NS_IsMainThread());
    5407             : 
    5408           0 :   SetMediaInfo(*aInfo);
    5409             : 
    5410           0 :   mIsEncrypted =
    5411           0 :     aInfo->IsEncrypted() || mPendingEncryptedInitData.IsEncrypted();
    5412           0 :   mTags = std::move(aTags);
    5413           0 :   mLoadedDataFired = false;
    5414           0 :   ChangeReadyState(HAVE_METADATA);
    5415             : 
    5416           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
    5417           0 :   if (IsVideo() && HasVideo()) {
    5418           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("resize"));
    5419             :   }
    5420           0 :   NS_ASSERTION(!HasVideo() || (mMediaInfo.mVideo.mDisplay.width > 0 &&
    5421             :                                mMediaInfo.mVideo.mDisplay.height > 0),
    5422             :                "Video resolution must be known on 'loadedmetadata'");
    5423           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
    5424           0 :   if (mDecoder && mDecoder->IsTransportSeekable() &&
    5425           0 :       mDecoder->IsMediaSeekable()) {
    5426           0 :     ProcessMediaFragmentURI();
    5427           0 :     mDecoder->SetFragmentEndTime(mFragmentEnd);
    5428             :   }
    5429           0 :   if (mIsEncrypted) {
    5430             :     // We only support playback of encrypted content via MSE by default.
    5431           0 :     if (!mMediaSource && Preferences::GetBool("media.eme.mse-only", true)) {
    5432             :       DecodeError(
    5433           0 :         MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
    5434           0 :                     "Encrypted content not supported outside of MSE"));
    5435           0 :       return;
    5436             :     }
    5437             : 
    5438             :     // Dispatch a distinct 'encrypted' event for each initData we have.
    5439           0 :     for (const auto& initData : mPendingEncryptedInitData.mInitDatas) {
    5440           0 :       DispatchEncrypted(initData.mInitData, initData.mType);
    5441             :     }
    5442           0 :     mPendingEncryptedInitData.Reset();
    5443             :   }
    5444             : 
    5445           0 :   if (IsVideo() && aInfo->HasVideo()) {
    5446             :     // We are a video element playing video so update the screen wakelock
    5447           0 :     NotifyOwnerDocumentActivityChanged();
    5448             :   }
    5449             : 
    5450           0 :   if (mDefaultPlaybackStartPosition != 0.0) {
    5451           0 :     SetCurrentTime(mDefaultPlaybackStartPosition);
    5452           0 :     mDefaultPlaybackStartPosition = 0.0;
    5453             :   }
    5454             : 
    5455           0 :   UpdateReadyStateInternal();
    5456             : 
    5457           0 :   if (!mSrcStream) {
    5458             :     return;
    5459             :   }
    5460           0 :   for (OutputMediaStream& ms : mOutputStreams) {
    5461           0 :     for (size_t i = 0; i < AudioTracks()->Length(); ++i) {
    5462           0 :       AudioTrack* t = (*AudioTracks())[i];
    5463           0 :       if (t->Enabled()) {
    5464           0 :         AddCaptureMediaTrackToOutputStream(t, ms);
    5465             :       }
    5466             :     }
    5467           0 :     if (IsVideo() && !ms.mCapturingAudioOnly) {
    5468             :       // Only add video tracks if we're a video element and the output stream
    5469             :       // wants video.
    5470           0 :       for (size_t i = 0; i < VideoTracks()->Length(); ++i) {
    5471           0 :         VideoTrack* t = (*VideoTracks())[i];
    5472           0 :         if (t->Selected()) {
    5473           0 :           AddCaptureMediaTrackToOutputStream(t, ms);
    5474             :         }
    5475             :       }
    5476             :     }
    5477             :   }
    5478             : }
    5479             : 
    5480             : void
    5481           0 : HTMLMediaElement::FirstFrameLoaded()
    5482             : {
    5483           0 :   LOG(LogLevel::Debug,
    5484             :       ("%p, FirstFrameLoaded() mFirstFrameLoaded=%d mWaitingForKey=%d",
    5485             :        this,
    5486             :        mFirstFrameLoaded,
    5487             :        mWaitingForKey));
    5488             : 
    5489           0 :   NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
    5490             : 
    5491           0 :   if (!mFirstFrameLoaded) {
    5492           0 :     mFirstFrameLoaded = true;
    5493           0 :     UpdateReadyStateInternal();
    5494             :   }
    5495             : 
    5496           0 :   ChangeDelayLoadStatus(false);
    5497             : 
    5498           0 :   if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
    5499           0 :       !HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
    5500           0 :       mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
    5501           0 :     mSuspendedAfterFirstFrame = true;
    5502           0 :     mDecoder->Suspend();
    5503             :   }
    5504           0 : }
    5505             : 
    5506             : void
    5507           0 : HTMLMediaElement::NetworkError(const MediaResult& aError)
    5508             : {
    5509           0 :   if (mReadyState == HAVE_NOTHING) {
    5510           0 :     NoSupportedMediaSourceError(aError.Description());
    5511             :   } else {
    5512           0 :     Error(MEDIA_ERR_NETWORK);
    5513             :   }
    5514           0 : }
    5515             : 
    5516             : void
    5517           0 : HTMLMediaElement::DecodeError(const MediaResult& aError)
    5518             : {
    5519           0 :   nsAutoString src;
    5520           0 :   GetCurrentSrc(src);
    5521           0 :   const char16_t* params[] = { src.get() };
    5522           0 :   ReportLoadError("MediaLoadDecodeError", params, ArrayLength(params));
    5523             : 
    5524           0 :   DecoderDoctorDiagnostics diagnostics;
    5525           0 :   diagnostics.StoreDecodeError(OwnerDoc(), aError, src, __func__);
    5526             : 
    5527           0 :   AudioTracks()->EmptyTracks();
    5528           0 :   VideoTracks()->EmptyTracks();
    5529           0 :   if (mIsLoadingFromSourceChildren) {
    5530           0 :     mErrorSink->ResetError();
    5531           0 :     if (mSourceLoadCandidate) {
    5532           0 :       DispatchAsyncSourceError(mSourceLoadCandidate);
    5533           0 :       QueueLoadFromSourceTask();
    5534             :     } else {
    5535           0 :       NS_WARNING("Should know the source we were loading from!");
    5536             :     }
    5537           0 :   } else if (mReadyState == HAVE_NOTHING) {
    5538           0 :     NoSupportedMediaSourceError(aError.Description());
    5539             :   } else {
    5540           0 :     Error(MEDIA_ERR_DECODE, aError.Description());
    5541             :   }
    5542           0 : }
    5543             : 
    5544             : void
    5545           0 : HTMLMediaElement::DecodeWarning(const MediaResult& aError)
    5546             : {
    5547           0 :   nsAutoString src;
    5548           0 :   GetCurrentSrc(src);
    5549           0 :   DecoderDoctorDiagnostics diagnostics;
    5550           0 :   diagnostics.StoreDecodeWarning(OwnerDoc(), aError, src, __func__);
    5551           0 : }
    5552             : 
    5553             : bool
    5554           0 : HTMLMediaElement::HasError() const
    5555             : {
    5556           0 :   return GetError();
    5557             : }
    5558             : 
    5559             : void
    5560           0 : HTMLMediaElement::LoadAborted()
    5561             : {
    5562           0 :   Error(MEDIA_ERR_ABORTED);
    5563           0 : }
    5564             : 
    5565             : void
    5566           0 : HTMLMediaElement::Error(uint16_t aErrorCode, const nsACString& aErrorDetails)
    5567             : {
    5568           0 :   mErrorSink->SetError(aErrorCode, aErrorDetails);
    5569           0 :   ChangeDelayLoadStatus(false);
    5570           0 :   UpdateAudioChannelPlayingState();
    5571           0 : }
    5572             : 
    5573             : void
    5574           0 : HTMLMediaElement::PlaybackEnded()
    5575             : {
    5576             :   // We changed state which can affect AddRemoveSelfReference
    5577           0 :   AddRemoveSelfReference();
    5578             : 
    5579           0 :   NS_ASSERTION(!mDecoder || mDecoder->IsEnded(),
    5580             :                "Decoder fired ended, but not in ended state");
    5581             : 
    5582             :   // Discard all output streams that have finished now.
    5583           0 :   for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
    5584           0 :     if (mOutputStreams[i].mFinishWhenEnded) {
    5585           0 :       LOG(LogLevel::Debug,
    5586             :           ("Playback ended. Removing output stream %p",
    5587             :            mOutputStreams[i].mStream.get()));
    5588           0 :       mOutputStreams.RemoveElementAt(i);
    5589             :     }
    5590             :   }
    5591             : 
    5592           0 :   if (mSrcStream) {
    5593           0 :     LOG(LogLevel::Debug,
    5594             :         ("%p, got duration by reaching the end of the resource", this));
    5595           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
    5596             :   }
    5597             : 
    5598           0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::loop)) {
    5599           0 :     SetCurrentTime(0);
    5600           0 :     return;
    5601             :   }
    5602             : 
    5603           0 :   FireTimeUpdate(false);
    5604             : 
    5605           0 :   if (!mPaused) {
    5606           0 :     Pause();
    5607             :   }
    5608             : 
    5609           0 :   if (mSrcStream) {
    5610             :     // A MediaStream that goes from inactive to active shall be eligible for
    5611             :     // autoplay again according to the mediacapture-main spec.
    5612           0 :     mAutoplaying = true;
    5613             :   }
    5614             : 
    5615           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
    5616             : }
    5617             : 
    5618             : void
    5619           0 : HTMLMediaElement::SeekStarted()
    5620             : {
    5621           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
    5622           0 : }
    5623             : 
    5624             : void
    5625           0 : HTMLMediaElement::SeekCompleted()
    5626             : {
    5627           0 :   mPlayingBeforeSeek = false;
    5628           0 :   SetPlayedOrSeeked(true);
    5629           0 :   if (mTextTrackManager) {
    5630           0 :     mTextTrackManager->DidSeek();
    5631             :   }
    5632           0 :   FireTimeUpdate(false);
    5633           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
    5634             :   // We changed whether we're seeking so we need to AddRemoveSelfReference
    5635           0 :   AddRemoveSelfReference();
    5636           0 :   if (mCurrentPlayRangeStart == -1.0) {
    5637           0 :     mCurrentPlayRangeStart = CurrentTime();
    5638             :   }
    5639           0 : }
    5640             : 
    5641             : void
    5642           0 : HTMLMediaElement::NotifySuspendedByCache(bool aSuspendedByCache)
    5643             : {
    5644           0 :   mDownloadSuspendedByCache = aSuspendedByCache;
    5645           0 :   UpdateReadyStateInternal();
    5646           0 : }
    5647             : 
    5648             : void
    5649           0 : HTMLMediaElement::DownloadSuspended()
    5650             : {
    5651           0 :   if (mNetworkState == NETWORK_LOADING) {
    5652           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
    5653             :   }
    5654           0 :   ChangeNetworkState(NETWORK_IDLE);
    5655           0 : }
    5656             : 
    5657             : void
    5658           0 : HTMLMediaElement::DownloadResumed()
    5659             : {
    5660           0 :   ChangeNetworkState(NETWORK_LOADING);
    5661           0 : }
    5662             : 
    5663             : void
    5664           0 : HTMLMediaElement::CheckProgress(bool aHaveNewProgress)
    5665             : {
    5666           0 :   MOZ_ASSERT(NS_IsMainThread());
    5667           0 :   MOZ_ASSERT(mNetworkState == NETWORK_LOADING);
    5668             : 
    5669           0 :   TimeStamp now = TimeStamp::NowLoRes();
    5670             : 
    5671           0 :   if (aHaveNewProgress) {
    5672           0 :     mDataTime = now;
    5673             :   }
    5674             : 
    5675             :   // If this is the first progress, or PROGRESS_MS has passed since the last
    5676             :   // progress event fired and more data has arrived since then, fire a
    5677             :   // progress event.
    5678           0 :   NS_ASSERTION((mProgressTime.IsNull() && !aHaveNewProgress) ||
    5679             :                  !mDataTime.IsNull(),
    5680             :                "null TimeStamp mDataTime should not be used in comparison");
    5681           0 :   if (mProgressTime.IsNull()
    5682           0 :         ? aHaveNewProgress
    5683           0 :         : (now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS) &&
    5684           0 :            mDataTime > mProgressTime)) {
    5685           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
    5686             :     // Resolution() ensures that future data will have now > mProgressTime,
    5687             :     // and so will trigger another event.  mDataTime is not reset because it
    5688             :     // is still required to detect stalled; it is similarly offset by
    5689             :     // resolution to indicate the new data has not yet arrived.
    5690           0 :     mProgressTime = now - TimeDuration::Resolution();
    5691           0 :     if (mDataTime > mProgressTime) {
    5692           0 :       mDataTime = mProgressTime;
    5693             :     }
    5694           0 :     if (!mProgressTimer) {
    5695           0 :       NS_ASSERTION(aHaveNewProgress,
    5696             :                    "timer dispatched when there was no timer");
    5697             :       // Were stalled.  Restart timer.
    5698           0 :       StartProgressTimer();
    5699           0 :       if (!mLoadedDataFired) {
    5700           0 :         ChangeDelayLoadStatus(true);
    5701             :       }
    5702             :     }
    5703             :     // Download statistics may have been updated, force a recheck of the
    5704             :     // readyState.
    5705           0 :     UpdateReadyStateInternal();
    5706             :   }
    5707             : 
    5708           0 :   if (now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
    5709           0 :     if (!mMediaSource) {
    5710           0 :       DispatchAsyncEvent(NS_LITERAL_STRING("stalled"));
    5711             :     } else {
    5712           0 :       ChangeDelayLoadStatus(false);
    5713             :     }
    5714             : 
    5715           0 :     NS_ASSERTION(mProgressTimer, "detected stalled without timer");
    5716             :     // Stop timer events, which prevents repeated stalled events until there
    5717             :     // is more progress.
    5718           0 :     StopProgress();
    5719             :   }
    5720             : 
    5721           0 :   AddRemoveSelfReference();
    5722           0 : }
    5723             : 
    5724             : /* static */
    5725             : void
    5726           0 : HTMLMediaElement::ProgressTimerCallback(nsITimer* aTimer, void* aClosure)
    5727             : {
    5728           0 :   auto decoder = static_cast<HTMLMediaElement*>(aClosure);
    5729           0 :   decoder->CheckProgress(false);
    5730           0 : }
    5731             : 
    5732             : void
    5733           0 : HTMLMediaElement::StartProgressTimer()
    5734             : {
    5735           0 :   MOZ_ASSERT(NS_IsMainThread());
    5736           0 :   MOZ_ASSERT(mNetworkState == NETWORK_LOADING);
    5737           0 :   NS_ASSERTION(!mProgressTimer, "Already started progress timer.");
    5738             : 
    5739           0 :   NS_NewTimerWithFuncCallback(getter_AddRefs(mProgressTimer),
    5740             :                               ProgressTimerCallback,
    5741             :                               this,
    5742             :                               PROGRESS_MS,
    5743             :                               nsITimer::TYPE_REPEATING_SLACK,
    5744             :                               "HTMLMediaElement::ProgressTimerCallback",
    5745           0 :                               mMainThreadEventTarget);
    5746           0 : }
    5747             : 
    5748             : void
    5749           0 : HTMLMediaElement::StartProgress()
    5750             : {
    5751             :   // Record the time now for detecting stalled.
    5752           0 :   mDataTime = TimeStamp::NowLoRes();
    5753             :   // Reset mProgressTime so that mDataTime is not indicating bytes received
    5754             :   // after the last progress event.
    5755           0 :   mProgressTime = TimeStamp();
    5756           0 :   StartProgressTimer();
    5757           0 : }
    5758             : 
    5759             : void
    5760           0 : HTMLMediaElement::StopProgress()
    5761             : {
    5762           0 :   MOZ_ASSERT(NS_IsMainThread());
    5763           0 :   if (!mProgressTimer) {
    5764             :     return;
    5765             :   }
    5766             : 
    5767           0 :   mProgressTimer->Cancel();
    5768           0 :   mProgressTimer = nullptr;
    5769             : }
    5770             : 
    5771             : void
    5772           0 : HTMLMediaElement::DownloadProgressed()
    5773             : {
    5774           0 :   if (mNetworkState != NETWORK_LOADING) {
    5775             :     return;
    5776             :   }
    5777           0 :   CheckProgress(true);
    5778             : }
    5779             : 
    5780             : bool
    5781           0 : HTMLMediaElement::ShouldCheckAllowOrigin()
    5782             : {
    5783           0 :   return mCORSMode != CORS_NONE;
    5784             : }
    5785             : 
    5786             : bool
    5787           0 : HTMLMediaElement::IsCORSSameOrigin()
    5788             : {
    5789             :   bool subsumes;
    5790           0 :   RefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
    5791           0 :   return (NS_SUCCEEDED(NodePrincipal()->Subsumes(principal, &subsumes)) &&
    5792           0 :           subsumes) ||
    5793           0 :          ShouldCheckAllowOrigin();
    5794             : }
    5795             : 
    5796             : void
    5797           0 : HTMLMediaElement::UpdateReadyStateInternal()
    5798             : {
    5799           0 :   if (!mDecoder && !mSrcStream) {
    5800             :     // Not initialized - bail out.
    5801           0 :     LOG(LogLevel::Debug,
    5802             :         ("MediaElement %p UpdateReadyStateInternal() "
    5803             :          "Not initialized",
    5804             :          this));
    5805             :     return;
    5806             :   }
    5807             : 
    5808           0 :   if (mDecoder && mReadyState < HAVE_METADATA) {
    5809             :     // aNextFrame might have a next frame because the decoder can advance
    5810             :     // on its own thread before MetadataLoaded gets a chance to run.
    5811             :     // The arrival of more data can't change us out of this readyState.
    5812           0 :     LOG(LogLevel::Debug,
    5813             :         ("MediaElement %p UpdateReadyStateInternal() "
    5814             :          "Decoder ready state < HAVE_METADATA",
    5815             :          this));
    5816             :     return;
    5817             :   }
    5818             : 
    5819           0 :   if (mSrcStream && mReadyState < HAVE_METADATA) {
    5820           0 :     if (!mSrcStreamTracksAvailable) {
    5821           0 :       LOG(LogLevel::Debug,
    5822             :           ("MediaElement %p UpdateReadyStateInternal() "
    5823             :            "MediaStreamTracks not available yet",
    5824             :            this));
    5825           0 :       return;
    5826             :     }
    5827             : 
    5828           0 :     bool hasAudioTracks = !AudioTracks()->IsEmpty();
    5829           0 :     bool hasVideoTracks = !VideoTracks()->IsEmpty();
    5830           0 :     if (!hasAudioTracks && !hasVideoTracks) {
    5831           0 :       LOG(LogLevel::Debug,
    5832             :           ("MediaElement %p UpdateReadyStateInternal() "
    5833             :            "Stream with no tracks",
    5834             :            this));
    5835             :       return;
    5836             :     }
    5837             : 
    5838           0 :     if (IsVideo() && hasVideoTracks && !HasVideo()) {
    5839           0 :       LOG(LogLevel::Debug,
    5840             :           ("MediaElement %p UpdateReadyStateInternal() "
    5841             :            "Stream waiting for video",
    5842             :            this));
    5843             :       return;
    5844             :     }
    5845             : 
    5846           0 :     LOG(LogLevel::Debug,
    5847             :         ("MediaElement %p UpdateReadyStateInternal() Stream has "
    5848             :          "metadata; audioTracks=%d, videoTracks=%d, "
    5849             :          "hasVideoFrame=%d",
    5850             :          this,
    5851             :          AudioTracks()->Length(),
    5852             :          VideoTracks()->Length(),
    5853             :          HasVideo()));
    5854             : 
    5855             :     // We are playing a stream that has video and a video frame is now set.
    5856             :     // This means we have all metadata needed to change ready state.
    5857           0 :     MediaInfo mediaInfo = mMediaInfo;
    5858           0 :     if (hasAudioTracks) {
    5859           0 :       mediaInfo.EnableAudio();
    5860             :     }
    5861           0 :     if (hasVideoTracks) {
    5862           0 :       mediaInfo.EnableVideo();
    5863             :     }
    5864           0 :     MetadataLoaded(&mediaInfo, nullptr);
    5865             :   }
    5866             : 
    5867           0 :   if (mMediaSource) {
    5868             :     // readyState has changed, assuming it's following the pending mediasource
    5869             :     // operations. Notify the Mediasource that the operations have completed.
    5870           0 :     mMediaSource->CompletePendingTransactions();
    5871             :   }
    5872             : 
    5873           0 :   enum NextFrameStatus nextFrameStatus = NextFrameStatus();
    5874           0 :   if (mWaitingForKey == NOT_WAITING_FOR_KEY) {
    5875           0 :     if (nextFrameStatus == NEXT_FRAME_UNAVAILABLE && mDecoder &&
    5876           0 :         !mDecoder->IsEnded()) {
    5877           0 :       nextFrameStatus = mDecoder->NextFrameBufferedStatus();
    5878             :     }
    5879           0 :   } else if (mWaitingForKey == WAITING_FOR_KEY) {
    5880           0 :     if (nextFrameStatus == NEXT_FRAME_UNAVAILABLE ||
    5881           0 :         nextFrameStatus == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
    5882             :       // http://w3c.github.io/encrypted-media/#wait-for-key
    5883             :       // Continuing 7.3.4 Queue a "waitingforkey" Event
    5884             :       // 4. Queue a task to fire a simple event named waitingforkey
    5885             :       // at the media element.
    5886             :       // 5. Set the readyState of media element to HAVE_METADATA.
    5887             :       // NOTE: We'll change to HAVE_CURRENT_DATA or HAVE_METADATA
    5888             :       // depending on whether we've loaded the first frame or not
    5889             :       // below.
    5890             :       // 6. Suspend playback.
    5891             :       // Note: Playback will already be stalled, as the next frame is
    5892             :       // unavailable.
    5893           0 :       mWaitingForKey = WAITING_FOR_KEY_DISPATCHED;
    5894           0 :       DispatchAsyncEvent(NS_LITERAL_STRING("waitingforkey"));
    5895             :     }
    5896             :   } else {
    5897           0 :     MOZ_ASSERT(mWaitingForKey == WAITING_FOR_KEY_DISPATCHED);
    5898           0 :     if (nextFrameStatus == NEXT_FRAME_AVAILABLE) {
    5899             :       // We have new frames after dispatching "waitingforkey".
    5900             :       // This means we've got the key and can reset mWaitingForKey now.
    5901           0 :       mWaitingForKey = NOT_WAITING_FOR_KEY;
    5902             :     }
    5903             :   }
    5904             : 
    5905           0 :   if (nextFrameStatus == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_SEEKING) {
    5906           0 :     LOG(LogLevel::Debug,
    5907             :         ("MediaElement %p UpdateReadyStateInternal() "
    5908             :          "NEXT_FRAME_UNAVAILABLE_SEEKING; Forcing HAVE_METADATA",
    5909             :          this));
    5910           0 :     ChangeReadyState(HAVE_METADATA);
    5911           0 :     return;
    5912             :   }
    5913             : 
    5914           0 :   if (IsVideo() && HasVideo() && !IsPlaybackEnded() && GetImageContainer() &&
    5915           0 :       !GetImageContainer()->HasCurrentImage()) {
    5916             :     // Don't advance if we are playing video, but don't have a video frame.
    5917             :     // Also, if video became available after advancing to HAVE_CURRENT_DATA
    5918             :     // while we are still playing, we need to revert to HAVE_METADATA until
    5919             :     // a video frame is available.
    5920           0 :     LOG(LogLevel::Debug,
    5921             :         ("MediaElement %p UpdateReadyStateInternal() "
    5922             :          "Playing video but no video frame; Forcing HAVE_METADATA",
    5923             :          this));
    5924           0 :     ChangeReadyState(HAVE_METADATA);
    5925           0 :     return;
    5926             :   }
    5927             : 
    5928           0 :   if (!mFirstFrameLoaded) {
    5929             :     // We haven't yet loaded the first frame, making us unable to determine
    5930             :     // if we have enough valid data at the present stage.
    5931             :     return;
    5932             :   }
    5933             : 
    5934           0 :   if (nextFrameStatus == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
    5935             :     // Force HAVE_CURRENT_DATA when buffering.
    5936           0 :     ChangeReadyState(HAVE_CURRENT_DATA);
    5937           0 :     return;
    5938             :   }
    5939             : 
    5940             :   // TextTracks must be loaded for the HAVE_ENOUGH_DATA and
    5941             :   // HAVE_FUTURE_DATA.
    5942             :   // So force HAVE_CURRENT_DATA if text tracks not loaded.
    5943           0 :   if (mTextTrackManager && !mTextTrackManager->IsLoaded()) {
    5944           0 :     ChangeReadyState(HAVE_CURRENT_DATA);
    5945           0 :     return;
    5946             :   }
    5947             : 
    5948           0 :   if (mDownloadSuspendedByCache && mDecoder && !mDecoder->IsEnded()) {
    5949             :     // The decoder has signaled that the download has been suspended by the
    5950             :     // media cache. So move readyState into HAVE_ENOUGH_DATA, in case there's
    5951             :     // script waiting for a "canplaythrough" event; without this forced
    5952             :     // transition, we will never fire the "canplaythrough" event if the
    5953             :     // media cache is too small, and scripts are bound to fail. Don't force
    5954             :     // this transition if the decoder is in ended state; the readyState
    5955             :     // should remain at HAVE_CURRENT_DATA in this case.
    5956             :     // Note that this state transition includes the case where we finished
    5957             :     // downloaded the whole data stream.
    5958           0 :     LOG(LogLevel::Debug,
    5959             :         ("MediaElement %p UpdateReadyStateInternal() "
    5960             :          "Decoder download suspended by cache",
    5961             :          this));
    5962           0 :     ChangeReadyState(HAVE_ENOUGH_DATA);
    5963           0 :     return;
    5964             :   }
    5965             : 
    5966           0 :   if (nextFrameStatus != MediaDecoderOwner::NEXT_FRAME_AVAILABLE) {
    5967           0 :     LOG(LogLevel::Debug,
    5968             :         ("MediaElement %p UpdateReadyStateInternal() "
    5969             :          "Next frame not available",
    5970             :          this));
    5971           0 :     ChangeReadyState(HAVE_CURRENT_DATA);
    5972           0 :     return;
    5973             :   }
    5974             : 
    5975           0 :   if (mSrcStream) {
    5976           0 :     LOG(LogLevel::Debug,
    5977             :         ("MediaElement %p UpdateReadyStateInternal() "
    5978             :          "Stream HAVE_ENOUGH_DATA",
    5979             :          this));
    5980           0 :     ChangeReadyState(HAVE_ENOUGH_DATA);
    5981           0 :     return;
    5982             :   }
    5983             : 
    5984             :   // Now see if we should set HAVE_ENOUGH_DATA.
    5985             :   // If it's something we don't know the size of, then we can't
    5986             :   // make a real estimate, so we go straight to HAVE_ENOUGH_DATA once
    5987             :   // we've downloaded enough data that our download rate is considered
    5988             :   // reliable. We have to move to HAVE_ENOUGH_DATA at some point or
    5989             :   // autoplay elements for live streams will never play. Otherwise we
    5990             :   // move to HAVE_ENOUGH_DATA if we can play through the entire media
    5991             :   // without stopping to buffer.
    5992           0 :   if (mDecoder->CanPlayThrough()) {
    5993           0 :     LOG(LogLevel::Debug,
    5994             :         ("MediaElement %p UpdateReadyStateInternal() "
    5995             :          "Decoder can play through",
    5996             :          this));
    5997           0 :     ChangeReadyState(HAVE_ENOUGH_DATA);
    5998           0 :     return;
    5999             :   }
    6000           0 :   LOG(LogLevel::Debug,
    6001             :       ("MediaElement %p UpdateReadyStateInternal() "
    6002             :        "Default; Decoder has future data",
    6003             :        this));
    6004           0 :   ChangeReadyState(HAVE_FUTURE_DATA);
    6005             : }
    6006             : 
    6007             : static const char* const gReadyStateToString[] = { "HAVE_NOTHING",
    6008             :                                                    "HAVE_METADATA",
    6009             :                                                    "HAVE_CURRENT_DATA",
    6010             :                                                    "HAVE_FUTURE_DATA",
    6011             :                                                    "HAVE_ENOUGH_DATA" };
    6012             : 
    6013             : void
    6014           0 : HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
    6015             : {
    6016           0 :   if (mReadyState == aState) {
    6017             :     return;
    6018             :   }
    6019             : 
    6020           0 :   nsMediaReadyState oldState = mReadyState;
    6021           0 :   mReadyState = aState;
    6022           0 :   LOG(LogLevel::Debug,
    6023             :       ("%p Ready state changed to %s", this, gReadyStateToString[aState]));
    6024             : 
    6025           0 :   DDLOG(DDLogCategory::Property, "ready_state", gReadyStateToString[aState]);
    6026             : 
    6027           0 :   if (mNetworkState == NETWORK_EMPTY) {
    6028             :     return;
    6029             :   }
    6030             : 
    6031           0 :   UpdateAudioChannelPlayingState();
    6032             : 
    6033             :   // Handle raising of "waiting" event during seek (see 4.8.10.9)
    6034             :   // or
    6035             :   // 4.8.12.7 Ready states:
    6036             :   // "If the previous ready state was HAVE_FUTURE_DATA or more, and the new
    6037             :   // ready state is HAVE_CURRENT_DATA or less
    6038             :   // If the media element was potentially playing before its readyState
    6039             :   // attribute changed to a value lower than HAVE_FUTURE_DATA, and the element
    6040             :   // has not ended playback, and playback has not stopped due to errors,
    6041             :   // paused for user interaction, or paused for in-band content, the user agent
    6042             :   // must queue a task to fire a simple event named timeupdate at the element,
    6043             :   // and queue a task to fire a simple event named waiting at the element."
    6044           0 :   if (mPlayingBeforeSeek && mReadyState < HAVE_FUTURE_DATA) {
    6045           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    6046           0 :   } else if (oldState >= HAVE_FUTURE_DATA && mReadyState < HAVE_FUTURE_DATA &&
    6047           0 :              !Paused() && !Ended() && !mErrorSink->mError) {
    6048           0 :     FireTimeUpdate(false);
    6049           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    6050             :   }
    6051             : 
    6052           0 :   if (oldState < HAVE_CURRENT_DATA && mReadyState >= HAVE_CURRENT_DATA &&
    6053           0 :       !mLoadedDataFired) {
    6054           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
    6055           0 :     mLoadedDataFired = true;
    6056             :   }
    6057             : 
    6058           0 :   if (oldState < HAVE_FUTURE_DATA && mReadyState >= HAVE_FUTURE_DATA) {
    6059           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
    6060           0 :     if (!mPaused) {
    6061           0 :       if (mDecoder) {
    6062           0 :         mDecoder->Play();
    6063             :       }
    6064           0 :       NotifyAboutPlaying();
    6065             :     }
    6066             :   }
    6067             : 
    6068           0 :   CheckAutoplayDataReady();
    6069             : 
    6070           0 :   if (oldState < HAVE_ENOUGH_DATA && mReadyState >= HAVE_ENOUGH_DATA) {
    6071           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("canplaythrough"));
    6072             :   }
    6073             : }
    6074             : 
    6075             : static const char* const gNetworkStateToString[] = { "EMPTY",
    6076             :                                                      "IDLE",
    6077             :                                                      "LOADING",
    6078             :                                                      "NO_SOURCE" };
    6079             : 
    6080             : void
    6081           0 : HTMLMediaElement::ChangeNetworkState(nsMediaNetworkState aState)
    6082             : {
    6083           0 :   if (mNetworkState == aState) {
    6084             :     return;
    6085             :   }
    6086             : 
    6087           0 :   nsMediaNetworkState oldState = mNetworkState;
    6088           0 :   mNetworkState = aState;
    6089           0 :   LOG(LogLevel::Debug,
    6090             :       ("%p Network state changed to %s", this, gNetworkStateToString[aState]));
    6091           0 :   DDLOG(
    6092             :     DDLogCategory::Property, "network_state", gNetworkStateToString[aState]);
    6093             : 
    6094           0 :   if (oldState == NETWORK_LOADING) {
    6095             :     // Stop progress notification when exiting NETWORK_LOADING.
    6096           0 :     StopProgress();
    6097             :   }
    6098             : 
    6099           0 :   if (mNetworkState == NETWORK_LOADING) {
    6100             :     // Start progress notification when entering NETWORK_LOADING.
    6101           0 :     StartProgress();
    6102           0 :   } else if (mNetworkState == NETWORK_IDLE && !mErrorSink->mError) {
    6103             :     // Fire 'suspend' event when entering NETWORK_IDLE and no error presented.
    6104           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
    6105             :   }
    6106             : 
    6107             :   // Changing mNetworkState affects AddRemoveSelfReference().
    6108           0 :   AddRemoveSelfReference();
    6109             : }
    6110             : 
    6111             : bool
    6112           0 : HTMLMediaElement::CanActivateAutoplay()
    6113             : {
    6114             :   // For stream inputs, we activate autoplay on HAVE_NOTHING because
    6115             :   // this element itself might be blocking the stream from making progress by
    6116             :   // being paused. We only check that it has data by checking its active state.
    6117             :   // We also activate autoplay when playing a media source since the data
    6118             :   // download is controlled by the script and there is no way to evaluate
    6119             :   // MediaDecoder::CanPlayThrough().
    6120             : 
    6121           2 :   if (!AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this))) {
    6122             :     return false;
    6123             :   }
    6124             : 
    6125           0 :   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) {
    6126             :     return false;
    6127             :   }
    6128             : 
    6129           0 :   if (!mAutoplaying) {
    6130             :     return false;
    6131             :   }
    6132             : 
    6133           0 :   if (IsEditable()) {
    6134             :     return false;
    6135             :   }
    6136             : 
    6137           0 :   if (!mPaused) {
    6138             :     return false;
    6139             :   }
    6140             : 
    6141           0 :   if (mPausedForInactiveDocumentOrChannel) {
    6142             :     return false;
    6143             :   }
    6144             : 
    6145             :   // Static document is used for print preview and printing, should not be
    6146             :   // autoplay
    6147           0 :   if (OwnerDoc()->IsStaticDocument()) {
    6148             :     return false;
    6149             :   }
    6150             : 
    6151           0 :   if (mAudioChannelWrapper) {
    6152             :     // Note: SUSPENDED_PAUSE and SUSPENDED_BLOCK will be merged into one single
    6153             :     // state.
    6154           0 :     if (mAudioChannelWrapper->GetSuspendType() ==
    6155           0 :           nsISuspendedTypes::SUSPENDED_PAUSE ||
    6156           0 :         mAudioChannelWrapper->GetSuspendType() ==
    6157           0 :           nsISuspendedTypes::SUSPENDED_BLOCK ||
    6158           0 :         mAudioChannelWrapper->IsPlaybackBlocked()) {
    6159             :       return false;
    6160             :     }
    6161             :   }
    6162             : 
    6163           0 :   bool hasData = (mDecoder && mReadyState >= HAVE_ENOUGH_DATA) ||
    6164           0 :                  (mSrcStream && mSrcStream->Active());
    6165             : 
    6166             :   return hasData;
    6167             : }
    6168             : 
    6169             : void
    6170           0 : HTMLMediaElement::CheckAutoplayDataReady()
    6171             : {
    6172           0 :   if (!CanActivateAutoplay()) {
    6173             :     return;
    6174             :   }
    6175             : 
    6176           0 :   mPaused = false;
    6177             :   // We changed mPaused which can affect AddRemoveSelfReference
    6178           0 :   AddRemoveSelfReference();
    6179           0 :   UpdateSrcMediaStreamPlaying();
    6180           0 :   UpdateAudioChannelPlayingState();
    6181             : 
    6182           0 :   if (mDecoder) {
    6183           0 :     SetPlayedOrSeeked(true);
    6184           0 :     if (mCurrentPlayRangeStart == -1.0) {
    6185           0 :       mCurrentPlayRangeStart = CurrentTime();
    6186             :     }
    6187           0 :     mDecoder->Play();
    6188           0 :   } else if (mSrcStream) {
    6189           0 :     SetPlayedOrSeeked(true);
    6190             :   }
    6191             : 
    6192             :   // For blocked media, the event would be pending until it is resumed.
    6193           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("play"));
    6194             : 
    6195           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
    6196             : }
    6197             : 
    6198             : bool
    6199           0 : HTMLMediaElement::IsActive() const
    6200             : {
    6201           0 :   nsIDocument* ownerDoc = OwnerDoc();
    6202           8 :   return ownerDoc && ownerDoc->IsActive() && ownerDoc->IsVisible();
    6203             : }
    6204             : 
    6205             : bool
    6206           0 : HTMLMediaElement::IsHidden() const
    6207             : {
    6208             :   nsIDocument* ownerDoc;
    6209           0 :   return mUnboundFromTree || !(ownerDoc = OwnerDoc()) || ownerDoc->Hidden();
    6210             : }
    6211             : 
    6212             : VideoFrameContainer*
    6213           0 : HTMLMediaElement::GetVideoFrameContainer()
    6214             : {
    6215           0 :   if (mShuttingDown) {
    6216             :     return nullptr;
    6217             :   }
    6218             : 
    6219           0 :   if (mVideoFrameContainer)
    6220             :     return mVideoFrameContainer;
    6221             : 
    6222             :   // Only video frames need an image container.
    6223           0 :   if (!IsVideo()) {
    6224             :     return nullptr;
    6225             :   }
    6226             : 
    6227             :   mVideoFrameContainer = new VideoFrameContainer(
    6228           0 :     this, LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS));
    6229             : 
    6230           0 :   return mVideoFrameContainer;
    6231             : }
    6232             : 
    6233             : void
    6234           0 : HTMLMediaElement::PrincipalChanged(DOMMediaStream* aStream)
    6235             : {
    6236           0 :   LOG(LogLevel::Info, ("HTMLMediaElement %p Stream principal changed.", this));
    6237           0 :   nsContentUtils::CombineResourcePrincipals(&mSrcStreamVideoPrincipal,
    6238           0 :                                             aStream->GetVideoPrincipal());
    6239             : 
    6240           0 :   LOG(LogLevel::Debug,
    6241             :       ("HTMLMediaElement %p Stream video principal changed to "
    6242             :        "%p. Waiting for it to reach VideoFrameContainer before "
    6243             :        "setting.",
    6244             :        this,
    6245             :        aStream->GetVideoPrincipal()));
    6246           0 :   if (mVideoFrameContainer) {
    6247             :     UpdateSrcStreamVideoPrincipal(
    6248           0 :       mVideoFrameContainer->GetLastPrincipalHandle());
    6249             :   }
    6250           0 : }
    6251             : 
    6252             : void
    6253           0 : HTMLMediaElement::UpdateSrcStreamVideoPrincipal(
    6254             :   const PrincipalHandle& aPrincipalHandle)
    6255             : {
    6256           0 :   nsTArray<RefPtr<VideoStreamTrack>> videoTracks;
    6257           0 :   mSrcStream->GetVideoTracks(videoTracks);
    6258             : 
    6259           0 :   PrincipalHandle handle(aPrincipalHandle);
    6260           0 :   bool matchesTrackPrincipal = false;
    6261           0 :   for (const RefPtr<VideoStreamTrack>& track : videoTracks) {
    6262           0 :     if (PrincipalHandleMatches(handle, track->GetPrincipal()) &&
    6263           0 :         !track->Ended()) {
    6264             :       // When the PrincipalHandle for the VideoFrameContainer changes to that of
    6265             :       // a track in mSrcStream we know that a removed track was displayed but
    6266             :       // is no longer so.
    6267           0 :       matchesTrackPrincipal = true;
    6268           0 :       LOG(LogLevel::Debug,
    6269             :           ("HTMLMediaElement %p VideoFrameContainer's "
    6270             :            "PrincipalHandle matches track %p. That's all we "
    6271             :            "need.",
    6272             :            this,
    6273             :            track.get()));
    6274             :       break;
    6275             :     }
    6276             :   }
    6277             : 
    6278           0 :   if (matchesTrackPrincipal) {
    6279           0 :     mSrcStreamVideoPrincipal = mSrcStream->GetVideoPrincipal();
    6280             :   }
    6281           0 : }
    6282             : 
    6283             : void
    6284           0 : HTMLMediaElement::PrincipalHandleChangedForVideoFrameContainer(
    6285             :   VideoFrameContainer* aContainer,
    6286             :   const PrincipalHandle& aNewPrincipalHandle)
    6287             : {
    6288           0 :   MOZ_ASSERT(NS_IsMainThread());
    6289             : 
    6290           0 :   if (!mSrcStream) {
    6291             :     return;
    6292             :   }
    6293             : 
    6294           0 :   LOG(LogLevel::Debug,
    6295             :       ("HTMLMediaElement %p PrincipalHandle changed in "
    6296             :        "VideoFrameContainer.",
    6297             :        this));
    6298             : 
    6299           0 :   UpdateSrcStreamVideoPrincipal(aNewPrincipalHandle);
    6300             : }
    6301             : 
    6302             : nsresult
    6303           0 : HTMLMediaElement::DispatchEvent(const nsAString& aName)
    6304             : {
    6305           0 :   LOG_EVENT(
    6306             :     LogLevel::Debug,
    6307             :     ("%p Dispatching event %s", this, NS_ConvertUTF16toUTF8(aName).get()));
    6308             : 
    6309             :   // Save events that occur while in the bfcache. These will be dispatched
    6310             :   // if the page comes out of the bfcache.
    6311           0 :   if (mEventDeliveryPaused) {
    6312           0 :     mPendingEvents.AppendElement(aName);
    6313           0 :     return NS_OK;
    6314             :   }
    6315             : 
    6316           0 :   return nsContentUtils::DispatchTrustedEvent(
    6317           0 :     OwnerDoc(), static_cast<nsIContent*>(this), aName, false, false);
    6318             : }
    6319             : 
    6320             : void
    6321           0 : HTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
    6322             : {
    6323           0 :   LOG_EVENT(LogLevel::Debug,
    6324             :             ("%p Queuing event %s", this, NS_ConvertUTF16toUTF8(aName).get()));
    6325           0 :   DDLOG(DDLogCategory::Event,
    6326             :         "HTMLMediaElement",
    6327             :         nsCString(NS_ConvertUTF16toUTF8(aName)));
    6328             : 
    6329             :   // Save events that occur while in the bfcache. These will be dispatched
    6330             :   // if the page comes out of the bfcache.
    6331           0 :   if (mEventDeliveryPaused) {
    6332           0 :     mPendingEvents.AppendElement(aName);
    6333           0 :     return;
    6334             :   }
    6335             : 
    6336           0 :   nsCOMPtr<nsIRunnable> event;
    6337             : 
    6338           0 :   if (aName.EqualsLiteral("playing")) {
    6339           0 :     event = new nsNotifyAboutPlayingRunner(this, TakePendingPlayPromises());
    6340             :   } else {
    6341           0 :     event = new nsAsyncEventRunner(aName, this);
    6342             :   }
    6343             : 
    6344           0 :   mMainThreadEventTarget->Dispatch(event.forget());
    6345             : 
    6346           0 :   if ((aName.EqualsLiteral("play") || aName.EqualsLiteral("playing"))) {
    6347           0 :     mPlayTime.Start();
    6348           0 :     if (IsHidden()) {
    6349           0 :       HiddenVideoStart();
    6350             :     }
    6351           0 :   } else if (aName.EqualsLiteral("waiting")) {
    6352           0 :     mPlayTime.Pause();
    6353           0 :     HiddenVideoStop();
    6354           0 :   } else if (aName.EqualsLiteral("pause")) {
    6355           0 :     mPlayTime.Pause();
    6356           0 :     HiddenVideoStop();
    6357             :   }
    6358             : }
    6359             : 
    6360             : nsresult
    6361           0 : HTMLMediaElement::DispatchPendingMediaEvents()
    6362             : {
    6363           0 :   NS_ASSERTION(!mEventDeliveryPaused,
    6364             :                "Must not be in bfcache when dispatching pending media events");
    6365             : 
    6366           0 :   uint32_t count = mPendingEvents.Length();
    6367           0 :   for (uint32_t i = 0; i < count; ++i) {
    6368           0 :     DispatchAsyncEvent(mPendingEvents[i]);
    6369             :   }
    6370           0 :   mPendingEvents.Clear();
    6371             : 
    6372           0 :   return NS_OK;
    6373             : }
    6374             : 
    6375             : bool
    6376           0 : HTMLMediaElement::IsPotentiallyPlaying() const
    6377             : {
    6378             :   // TODO:
    6379             :   //   playback has not stopped due to errors,
    6380             :   //   and the element has not paused for user interaction
    6381           0 :   return !mPaused &&
    6382           0 :          (mReadyState == HAVE_ENOUGH_DATA || mReadyState == HAVE_FUTURE_DATA) &&
    6383           0 :          !IsPlaybackEnded();
    6384             : }
    6385             : 
    6386             : bool
    6387           0 : HTMLMediaElement::IsPlaybackEnded() const
    6388             : {
    6389             :   // TODO:
    6390             :   //   the current playback position is equal to the effective end of the media
    6391             :   //   resource. See bug 449157.
    6392           0 :   return mReadyState >= HAVE_METADATA && mDecoder && mDecoder->IsEnded();
    6393             : }
    6394             : 
    6395             : already_AddRefed<nsIPrincipal>
    6396           0 : HTMLMediaElement::GetCurrentPrincipal()
    6397             : {
    6398           0 :   if (mDecoder) {
    6399           0 :     return mDecoder->GetCurrentPrincipal();
    6400             :   }
    6401           0 :   if (mSrcStream) {
    6402           0 :     nsCOMPtr<nsIPrincipal> principal = mSrcStream->GetPrincipal();
    6403           0 :     return principal.forget();
    6404             :   }
    6405             :   return nullptr;
    6406             : }
    6407             : 
    6408             : already_AddRefed<nsIPrincipal>
    6409           0 : HTMLMediaElement::GetCurrentVideoPrincipal()
    6410             : {
    6411           0 :   if (mDecoder) {
    6412           0 :     return mDecoder->GetCurrentPrincipal();
    6413             :   }
    6414           0 :   if (mSrcStream) {
    6415           0 :     nsCOMPtr<nsIPrincipal> principal = mSrcStreamVideoPrincipal;
    6416           0 :     return principal.forget();
    6417             :   }
    6418             :   return nullptr;
    6419             : }
    6420             : 
    6421             : void
    6422           0 : HTMLMediaElement::NotifyDecoderPrincipalChanged()
    6423             : {
    6424           0 :   RefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
    6425             : 
    6426           0 :   mDecoder->UpdateSameOriginStatus(!principal || IsCORSSameOrigin());
    6427             : 
    6428           0 :   for (DecoderPrincipalChangeObserver* observer :
    6429           0 :        mDecoderPrincipalChangeObservers) {
    6430           0 :     observer->NotifyDecoderPrincipalChanged();
    6431             :   }
    6432           0 : }
    6433             : 
    6434             : void
    6435           0 : HTMLMediaElement::AddDecoderPrincipalChangeObserver(
    6436             :   DecoderPrincipalChangeObserver* aObserver)
    6437             : {
    6438           0 :   mDecoderPrincipalChangeObservers.AppendElement(aObserver);
    6439           0 : }
    6440             : 
    6441             : bool
    6442           0 : HTMLMediaElement::RemoveDecoderPrincipalChangeObserver(
    6443             :   DecoderPrincipalChangeObserver* aObserver)
    6444             : {
    6445           0 :   return mDecoderPrincipalChangeObservers.RemoveElement(aObserver);
    6446             : }
    6447             : 
    6448             : void
    6449           0 : HTMLMediaElement::Invalidate(bool aImageSizeChanged,
    6450             :                              Maybe<nsIntSize>& aNewIntrinsicSize,
    6451             :                              bool aForceInvalidate)
    6452             : {
    6453           0 :   nsIFrame* frame = GetPrimaryFrame();
    6454           0 :   if (aNewIntrinsicSize) {
    6455           0 :     UpdateMediaSize(aNewIntrinsicSize.value());
    6456           0 :     if (frame) {
    6457           0 :       nsPresContext* presContext = frame->PresContext();
    6458           0 :       nsIPresShell* presShell = presContext->PresShell();
    6459             :       presShell->FrameNeedsReflow(
    6460           0 :         frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
    6461             :     }
    6462             :   }
    6463             : 
    6464           0 :   RefPtr<ImageContainer> imageContainer = GetImageContainer();
    6465             :   bool asyncInvalidate =
    6466           0 :     imageContainer && imageContainer->IsAsync() && !aForceInvalidate;
    6467           0 :   if (frame) {
    6468           0 :     if (aImageSizeChanged) {
    6469           0 :       frame->InvalidateFrame();
    6470             :     } else {
    6471           0 :       frame->InvalidateLayer(DisplayItemType::TYPE_VIDEO,
    6472             :                              nullptr,
    6473             :                              nullptr,
    6474           0 :                              asyncInvalidate ? nsIFrame::UPDATE_IS_ASYNC : 0);
    6475             :     }
    6476             :   }
    6477             : 
    6478           0 :   SVGObserverUtils::InvalidateDirectRenderingObservers(this);
    6479           0 : }
    6480             : 
    6481             : void
    6482           0 : HTMLMediaElement::UpdateMediaSize(const nsIntSize& aSize)
    6483             : {
    6484           0 :   if (IsVideo() && mReadyState != HAVE_NOTHING &&
    6485           0 :       mMediaInfo.mVideo.mDisplay != aSize) {
    6486           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("resize"));
    6487             :   }
    6488             : 
    6489           0 :   mMediaInfo.mVideo.mDisplay = aSize;
    6490           0 :   UpdateReadyStateInternal();
    6491           0 : }
    6492             : 
    6493             : void
    6494           0 : HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize)
    6495             : {
    6496           0 :   if (!mMediaInfo.HasVideo()) {
    6497           0 :     UpdateMediaSize(aSize);
    6498             :   }
    6499             : 
    6500           0 :   if (!mMediaStreamSizeListener) {
    6501             :     return;
    6502             :   }
    6503             : 
    6504           0 :   if (!mSelectedVideoStreamTrack) {
    6505           0 :     MOZ_ASSERT(false);
    6506             :     return;
    6507             :   }
    6508             : 
    6509           0 :   mSelectedVideoStreamTrack->RemoveDirectListener(mMediaStreamSizeListener);
    6510           0 :   mMediaStreamSizeListener->Forget();
    6511           0 :   mMediaStreamSizeListener = nullptr;
    6512             : }
    6513             : 
    6514             : void
    6515           2 : HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement,
    6516             :                                          bool aSuspendEvents)
    6517             : {
    6518           0 :   LOG(LogLevel::Debug,
    6519             :       ("%p SuspendOrResumeElement(pause=%d, suspendEvents=%d) hidden=%d",
    6520             :        this,
    6521             :        aPauseElement,
    6522             :        aSuspendEvents,
    6523             :        OwnerDoc()->Hidden()));
    6524             : 
    6525           2 :   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
    6526           0 :     mPausedForInactiveDocumentOrChannel = aPauseElement;
    6527           0 :     UpdateSrcMediaStreamPlaying();
    6528           0 :     UpdateAudioChannelPlayingState();
    6529           0 :     if (aPauseElement) {
    6530           0 :       ReportTelemetry();
    6531             : 
    6532             :       // For EME content, we may force destruction of the CDM client (and CDM
    6533             :       // instance if this is the last client for that CDM instance) and
    6534             :       // the CDM's decoder. This ensures the CDM gets reliable and prompt
    6535             :       // shutdown notifications, as it may have book-keeping it needs
    6536             :       // to do on shutdown.
    6537           0 :       if (mMediaKeys) {
    6538           0 :         nsAutoString keySystem;
    6539           0 :         mMediaKeys->GetKeySystem(keySystem);
    6540             :       }
    6541           0 :       if (mDecoder) {
    6542           0 :         mDecoder->Pause();
    6543           0 :         mDecoder->Suspend();
    6544             :       }
    6545           0 :       mEventDeliveryPaused = aSuspendEvents;
    6546             :     } else {
    6547           0 :       if (mDecoder) {
    6548           0 :         mDecoder->Resume();
    6549           0 :         if (!mPaused && !mDecoder->IsEnded()) {
    6550           0 :           mDecoder->Play();
    6551             :         }
    6552             :       }
    6553           0 :       if (mEventDeliveryPaused) {
    6554           0 :         mEventDeliveryPaused = false;
    6555           0 :         DispatchPendingMediaEvents();
    6556             :       }
    6557             :     }
    6558             :   }
    6559           0 : }
    6560             : 
    6561             : bool
    6562           0 : HTMLMediaElement::IsBeingDestroyed()
    6563             : {
    6564           0 :   nsIDocument* ownerDoc = OwnerDoc();
    6565           0 :   nsIDocShell* docShell = ownerDoc ? ownerDoc->GetDocShell() : nullptr;
    6566           0 :   bool isBeingDestroyed = false;
    6567           0 :   if (docShell) {
    6568           0 :     docShell->IsBeingDestroyed(&isBeingDestroyed);
    6569             :   }
    6570           0 :   return isBeingDestroyed;
    6571             : }
    6572             : 
    6573             : void
    6574           2 : HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
    6575             : {
    6576           0 :   bool visible = !IsHidden();
    6577           0 :   if (visible) {
    6578             :     // Visible -> Just pause hidden play time (no-op if already paused).
    6579           2 :     HiddenVideoStop();
    6580           0 :   } else if (mPlayTime.IsStarted()) {
    6581             :     // Not visible, play time is running -> Start hidden play time if needed.
    6582           0 :     HiddenVideoStart();
    6583             :   }
    6584             : 
    6585           0 :   if (mDecoder && !IsBeingDestroyed()) {
    6586           0 :     NotifyDecoderActivityChanges();
    6587             :   }
    6588             : 
    6589           0 :   bool pauseElement = ShouldElementBePaused();
    6590           0 :   SuspendOrResumeElement(pauseElement, !IsActive());
    6591             : 
    6592             :   // If the owning document has become inactive we should shutdown the CDM.
    6593           0 :   if (!OwnerDoc()->IsCurrentActiveDocument() && mMediaKeys) {
    6594           0 :     mMediaKeys->Shutdown();
    6595           0 :     DDUNLINKCHILD(mMediaKeys.get());
    6596           0 :     mMediaKeys = nullptr;
    6597           0 :     if (mDecoder) {
    6598           0 :       ShutdownDecoder();
    6599             :     }
    6600             :   }
    6601             : 
    6602           0 :   AddRemoveSelfReference();
    6603           0 : }
    6604             : 
    6605             : void
    6606           2 : HTMLMediaElement::AddRemoveSelfReference()
    6607             : {
    6608             :   // XXX we could release earlier here in many situations if we examined
    6609             :   // which event listeners are attached. Right now we assume there is a
    6610             :   // potential listener for every event. We would also have to keep the
    6611             :   // element alive if it was playing and producing audio output --- right now
    6612             :   // that's covered by the !mPaused check.
    6613           0 :   nsIDocument* ownerDoc = OwnerDoc();
    6614             : 
    6615             :   // See the comment at the top of this file for the explanation of this
    6616             :   // boolean expression.
    6617             :   bool needSelfReference =
    6618           0 :     !mShuttingDown && ownerDoc->IsActive() &&
    6619           0 :     (mDelayingLoadEvent || (!mPaused && mDecoder && !mDecoder->IsEnded()) ||
    6620           0 :      (!mPaused && mSrcStream && !mSrcStream->IsFinished()) ||
    6621           0 :      (mDecoder && mDecoder->IsSeeking()) || CanActivateAutoplay() ||
    6622           6 :      (mMediaSource ? mProgressTimer : mNetworkState == NETWORK_LOADING));
    6623             : 
    6624           2 :   if (needSelfReference != mHasSelfReference) {
    6625           0 :     mHasSelfReference = needSelfReference;
    6626           0 :     if (needSelfReference) {
    6627             :       // The shutdown observer will hold a strong reference to us. This
    6628             :       // will do to keep us alive. We need to know about shutdown so that
    6629             :       // we can release our self-reference.
    6630           0 :       mShutdownObserver->AddRefMediaElement();
    6631             :     } else {
    6632             :       // Dispatch Release asynchronously so that we don't destroy this object
    6633             :       // inside a call stack of method calls on this object
    6634           0 :       mMainThreadEventTarget->Dispatch(
    6635           0 :         NewRunnableMethod("dom::HTMLMediaElement::DoRemoveSelfReference",
    6636             :                           this,
    6637           0 :                           &HTMLMediaElement::DoRemoveSelfReference));
    6638             :     }
    6639             :   }
    6640           2 : }
    6641             : 
    6642             : void
    6643           0 : HTMLMediaElement::DoRemoveSelfReference()
    6644             : {
    6645           0 :   mShutdownObserver->ReleaseMediaElement();
    6646           0 : }
    6647             : 
    6648             : void
    6649           0 : HTMLMediaElement::NotifyShutdownEvent()
    6650             : {
    6651           0 :   mShuttingDown = true;
    6652           0 :   ResetState();
    6653           0 :   AddRemoveSelfReference();
    6654           0 : }
    6655             : 
    6656             : void
    6657           0 : HTMLMediaElement::DispatchAsyncSourceError(nsIContent* aSourceElement)
    6658             : {
    6659           0 :   LOG_EVENT(LogLevel::Debug, ("%p Queuing simple source error event", this));
    6660             : 
    6661             :   nsCOMPtr<nsIRunnable> event =
    6662           0 :     new nsSourceErrorEventRunner(this, aSourceElement);
    6663           0 :   mMainThreadEventTarget->Dispatch(event.forget());
    6664           0 : }
    6665             : 
    6666             : void
    6667           0 : HTMLMediaElement::NotifyAddedSource()
    6668             : {
    6669             :   // If a source element is inserted as a child of a media element
    6670             :   // that has no src attribute and whose networkState has the value
    6671             :   // NETWORK_EMPTY, the user agent must invoke the media element's
    6672             :   // resource selection algorithm.
    6673           0 :   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
    6674           0 :       mNetworkState == NETWORK_EMPTY) {
    6675           0 :     AssertReadyStateIsNothing();
    6676           0 :     QueueSelectResourceTask();
    6677             :   }
    6678             : 
    6679             :   // A load was paused in the resource selection algorithm, waiting for
    6680             :   // a new source child to be added, resume the resource selection algorithm.
    6681           0 :   if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
    6682             :     // Rest the flag so we don't queue multiple LoadFromSourceTask() when
    6683             :     // multiple <source> are attached in an event loop.
    6684           0 :     mLoadWaitStatus = NOT_WAITING;
    6685           0 :     QueueLoadFromSourceTask();
    6686             :   }
    6687           0 : }
    6688             : 
    6689             : Element*
    6690           0 : HTMLMediaElement::GetNextSource()
    6691             : {
    6692           0 :   mSourceLoadCandidate = nullptr;
    6693             : 
    6694             :   while (true) {
    6695           0 :     if (mSourcePointer == nsINode::GetLastChild()) {
    6696             :       return nullptr; // no more children
    6697             :     }
    6698             : 
    6699           0 :     if (!mSourcePointer) {
    6700           0 :       mSourcePointer = nsINode::GetFirstChild();
    6701             :     } else {
    6702           0 :       mSourcePointer = mSourcePointer->GetNextSibling();
    6703             :     }
    6704           0 :     nsIContent* child = mSourcePointer;
    6705             : 
    6706             :     // If child is a <source> element, it is the next candidate.
    6707           0 :     if (child && child->IsHTMLElement(nsGkAtoms::source)) {
    6708           0 :       mSourceLoadCandidate = child;
    6709           0 :       return child->AsElement();
    6710             :     }
    6711             :   }
    6712             :   NS_NOTREACHED("Execution should not reach here!");
    6713             :   return nullptr;
    6714             : }
    6715             : 
    6716             : void
    6717           0 : HTMLMediaElement::ChangeDelayLoadStatus(bool aDelay)
    6718             : {
    6719           0 :   if (mDelayingLoadEvent == aDelay)
    6720             :     return;
    6721             : 
    6722           0 :   mDelayingLoadEvent = aDelay;
    6723             : 
    6724           0 :   LOG(LogLevel::Debug,
    6725             :       ("%p ChangeDelayLoadStatus(%d) doc=0x%p",
    6726             :        this,
    6727             :        aDelay,
    6728             :        mLoadBlockedDoc.get()));
    6729           0 :   if (mDecoder) {
    6730           0 :     mDecoder->SetLoadInBackground(!aDelay);
    6731             :   }
    6732           0 :   if (aDelay) {
    6733           0 :     mLoadBlockedDoc = OwnerDoc();
    6734           0 :     mLoadBlockedDoc->BlockOnload();
    6735             :   } else {
    6736             :     // mLoadBlockedDoc might be null due to GC unlinking
    6737           0 :     if (mLoadBlockedDoc) {
    6738           0 :       mLoadBlockedDoc->UnblockOnload(false);
    6739           0 :       mLoadBlockedDoc = nullptr;
    6740             :     }
    6741             :   }
    6742             : 
    6743             :   // We changed mDelayingLoadEvent which can affect AddRemoveSelfReference
    6744           0 :   AddRemoveSelfReference();
    6745             : }
    6746             : 
    6747             : already_AddRefed<nsILoadGroup>
    6748           0 : HTMLMediaElement::GetDocumentLoadGroup()
    6749             : {
    6750           0 :   if (!OwnerDoc()->IsActive()) {
    6751           0 :     NS_WARNING("Load group requested for media element in inactive document.");
    6752             :   }
    6753           0 :   return OwnerDoc()->GetDocumentLoadGroup();
    6754             : }
    6755             : 
    6756             : nsresult
    6757           0 : HTMLMediaElement::CopyInnerTo(Element* aDest, bool aPreallocateChildren)
    6758             : {
    6759           0 :   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest, aPreallocateChildren);
    6760           0 :   NS_ENSURE_SUCCESS(rv, rv);
    6761           0 :   if (aDest->OwnerDoc()->IsStaticDocument()) {
    6762           0 :     HTMLMediaElement* dest = static_cast<HTMLMediaElement*>(aDest);
    6763           0 :     dest->SetMediaInfo(mMediaInfo);
    6764             :   }
    6765             :   return rv;
    6766             : }
    6767             : 
    6768             : already_AddRefed<TimeRanges>
    6769           0 : HTMLMediaElement::Buffered() const
    6770             : {
    6771             :   media::TimeIntervals buffered =
    6772           0 :     mDecoder ? mDecoder->GetBuffered() : media::TimeIntervals();
    6773           0 :   RefPtr<TimeRanges> ranges = new TimeRanges(ToSupports(OwnerDoc()), buffered);
    6774           0 :   return ranges.forget();
    6775             : }
    6776             : 
    6777             : void
    6778           0 : HTMLMediaElement::SetRequestHeaders(nsIHttpChannel* aChannel)
    6779             : {
    6780             :   // Send Accept header for video and audio types only (Bug 489071)
    6781           0 :   SetAcceptHeader(aChannel);
    6782             : 
    6783             :   // Apache doesn't send Content-Length when gzip transfer encoding is used,
    6784             :   // which prevents us from estimating the video length (if explicit
    6785             :   // Content-Duration and a length spec in the container are not present either)
    6786             :   // and from seeking. So, disable the standard "Accept-Encoding: gzip,deflate"
    6787             :   // that we usually send. See bug 614760.
    6788           0 :   DebugOnly<nsresult> rv = aChannel->SetRequestHeader(
    6789           0 :     NS_LITERAL_CSTRING("Accept-Encoding"), EmptyCString(), false);
    6790           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    6791             : 
    6792             :   // Set the Referer header
    6793           0 :   rv = aChannel->SetReferrerWithPolicy(OwnerDoc()->GetDocumentURI(),
    6794           0 :                                        OwnerDoc()->GetReferrerPolicy());
    6795           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    6796           0 : }
    6797             : 
    6798             : void
    6799           0 : HTMLMediaElement::FireTimeUpdate(bool aPeriodic)
    6800             : {
    6801           0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    6802             : 
    6803           0 :   TimeStamp now = TimeStamp::Now();
    6804           0 :   double time = CurrentTime();
    6805             : 
    6806             :   // Fire a timeupdate event if this is not a periodic update (i.e. it's a
    6807             :   // timeupdate event mandated by the spec), or if it's a periodic update
    6808             :   // and TIMEUPDATE_MS has passed since the last timeupdate event fired and
    6809             :   // the time has changed.
    6810           0 :   if (!aPeriodic || (mLastCurrentTime != time &&
    6811           0 :                      (mTimeUpdateTime.IsNull() ||
    6812           0 :                       now - mTimeUpdateTime >=
    6813           0 :                         TimeDuration::FromMilliseconds(TIMEUPDATE_MS)))) {
    6814           0 :     DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
    6815           0 :     mTimeUpdateTime = now;
    6816           0 :     mLastCurrentTime = time;
    6817             :   }
    6818           0 :   if (mFragmentEnd >= 0.0 && time >= mFragmentEnd) {
    6819           0 :     Pause();
    6820           0 :     mFragmentEnd = -1.0;
    6821           0 :     mFragmentStart = -1.0;
    6822           0 :     mDecoder->SetFragmentEndTime(mFragmentEnd);
    6823             :   }
    6824             : 
    6825             :   // Update the cues displaying on the video.
    6826             :   // Here mTextTrackManager can be null if the cycle collector has unlinked
    6827             :   // us before our parent. In that case UnbindFromTree will call us
    6828             :   // when our parent is unlinked.
    6829           0 :   if (mTextTrackManager) {
    6830           0 :     mTextTrackManager->TimeMarchesOn();
    6831             :   }
    6832           0 : }
    6833             : 
    6834             : MediaStream*
    6835           0 : HTMLMediaElement::GetSrcMediaStream() const
    6836             : {
    6837           0 :   if (!mSrcStream) {
    6838             :     return nullptr;
    6839             :   }
    6840           0 :   return mSrcStream->GetPlaybackStream();
    6841             : }
    6842             : 
    6843             : MediaError*
    6844           0 : HTMLMediaElement::GetError() const
    6845             : {
    6846           0 :   return mErrorSink->mError;
    6847             : }
    6848             : 
    6849             : void
    6850           0 : HTMLMediaElement::OpenUnsupportedMediaWithExternalAppIfNeeded() const
    6851             : {
    6852           0 :   mErrorSink->MaybeOpenUnsupportedMediaForOwner();
    6853           0 : }
    6854             : 
    6855             : void
    6856           0 : HTMLMediaElement::GetCurrentSpec(nsCString& aString)
    6857             : {
    6858           0 :   if (mLoadingSrc) {
    6859           0 :     mLoadingSrc->GetSpec(aString);
    6860             :   } else {
    6861           0 :     aString.Truncate();
    6862             :   }
    6863           0 : }
    6864             : 
    6865             : double
    6866           0 : HTMLMediaElement::MozFragmentEnd()
    6867             : {
    6868           0 :   double duration = Duration();
    6869             : 
    6870             :   // If there is no end fragment, or the fragment end is greater than the
    6871             :   // duration, return the duration.
    6872           0 :   return (mFragmentEnd < 0.0 || mFragmentEnd > duration) ? duration
    6873           0 :                                                          : mFragmentEnd;
    6874             : }
    6875             : 
    6876             : static double
    6877           0 : ClampPlaybackRate(double aPlaybackRate)
    6878             : {
    6879           0 :   MOZ_ASSERT(aPlaybackRate >= 0.0);
    6880             : 
    6881           0 :   if (aPlaybackRate == 0.0) {
    6882             :     return aPlaybackRate;
    6883             :   }
    6884           0 :   if (aPlaybackRate < MIN_PLAYBACKRATE) {
    6885             :     return MIN_PLAYBACKRATE;
    6886             :   }
    6887           0 :   if (aPlaybackRate > MAX_PLAYBACKRATE) {
    6888             :     return MAX_PLAYBACKRATE;
    6889             :   }
    6890           0 :   return aPlaybackRate;
    6891             : }
    6892             : 
    6893             : void
    6894           0 : HTMLMediaElement::SetDefaultPlaybackRate(double aDefaultPlaybackRate,
    6895             :                                          ErrorResult& aRv)
    6896             : {
    6897           0 :   if (aDefaultPlaybackRate < 0) {
    6898           0 :     aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
    6899           0 :     return;
    6900             :   }
    6901             : 
    6902           0 :   mDefaultPlaybackRate = ClampPlaybackRate(aDefaultPlaybackRate);
    6903           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("ratechange"));
    6904             : }
    6905             : 
    6906             : void
    6907           0 : HTMLMediaElement::SetPlaybackRate(double aPlaybackRate, ErrorResult& aRv)
    6908             : {
    6909             :   // Changing the playback rate of a media that has more than two channels is
    6910             :   // not supported.
    6911           0 :   if (aPlaybackRate < 0) {
    6912           0 :     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
    6913           0 :     return;
    6914             :   }
    6915             : 
    6916           0 :   if (mPlaybackRate == aPlaybackRate) {
    6917             :     return;
    6918             :   }
    6919             : 
    6920           0 :   mPlaybackRate = aPlaybackRate;
    6921             : 
    6922           0 :   if (mPlaybackRate != 0.0 &&
    6923           0 :       (mPlaybackRate > THRESHOLD_HIGH_PLAYBACKRATE_AUDIO ||
    6924             :        mPlaybackRate < THRESHOLD_LOW_PLAYBACKRATE_AUDIO)) {
    6925           0 :     SetMutedInternal(mMuted | MUTED_BY_INVALID_PLAYBACK_RATE);
    6926             :   } else {
    6927           0 :     SetMutedInternal(mMuted & ~MUTED_BY_INVALID_PLAYBACK_RATE);
    6928             :   }
    6929             : 
    6930           0 :   if (mDecoder) {
    6931           0 :     mDecoder->SetPlaybackRate(ClampPlaybackRate(mPlaybackRate));
    6932             :   }
    6933           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("ratechange"));
    6934             : }
    6935             : 
    6936             : void
    6937           0 : HTMLMediaElement::SetMozPreservesPitch(bool aPreservesPitch)
    6938             : {
    6939           0 :   mPreservesPitch = aPreservesPitch;
    6940           0 :   if (mDecoder) {
    6941           0 :     mDecoder->SetPreservesPitch(mPreservesPitch);
    6942             :   }
    6943           0 : }
    6944             : 
    6945             : ImageContainer*
    6946           0 : HTMLMediaElement::GetImageContainer()
    6947             : {
    6948           0 :   VideoFrameContainer* container = GetVideoFrameContainer();
    6949           0 :   return container ? container->GetImageContainer() : nullptr;
    6950             : }
    6951             : 
    6952             : void
    6953           0 : HTMLMediaElement::UpdateAudioChannelPlayingState(bool aForcePlaying)
    6954             : {
    6955           0 :   if (mAudioChannelWrapper) {
    6956           0 :     mAudioChannelWrapper->UpdateAudioChannelPlayingState(aForcePlaying);
    6957             :   }
    6958           0 : }
    6959             : 
    6960             : bool
    6961           0 : HTMLMediaElement::IsAllowedToPlay()
    6962             : {
    6963           0 :   if (!AutoplayPolicy::IsMediaElementAllowedToPlay(WrapNotNull(this))) {
    6964             : #if defined(MOZ_WIDGET_ANDROID)
    6965             :     nsContentUtils::DispatchTrustedEvent(
    6966             :       OwnerDoc(),
    6967             :       static_cast<nsIContent*>(this),
    6968             :       NS_LITERAL_STRING("MozAutoplayMediaBlocked"),
    6969             :       false,
    6970             :       false);
    6971             : #endif
    6972           0 :     LOG(LogLevel::Debug,
    6973             :         ("%p %s AutoplayPolicy blocked autoplay.", this, __func__));
    6974             :     return false;
    6975             :   }
    6976             : 
    6977           0 :   LOG(LogLevel::Debug,
    6978             :       ("%p %s AutoplayPolicy did not block autoplay.", this, __func__));
    6979             : 
    6980             :   // Check our custom playback policy.
    6981           0 :   if (mAudioChannelWrapper) {
    6982             :     // Note: SUSPENDED_PAUSE and SUSPENDED_BLOCK will be merged into one single
    6983             :     // state.
    6984           0 :     if (mAudioChannelWrapper->GetSuspendType() ==
    6985           0 :           nsISuspendedTypes::SUSPENDED_PAUSE ||
    6986           0 :         mAudioChannelWrapper->GetSuspendType() ==
    6987             :           nsISuspendedTypes::SUSPENDED_BLOCK) {
    6988           0 :       LOG(LogLevel::Debug,
    6989             :           ("%p IsAllowedToPlay() returning false due to AudioChannelAgent.",
    6990             :            this));
    6991             :       return false;
    6992             :     }
    6993             : 
    6994           0 :     LOG(LogLevel::Debug, ("%p IsAllowedToPlay() returning true.", this));
    6995             :     return true;
    6996             :   }
    6997             : 
    6998             :   // If the mAudioChannelWrapper doesn't exist that means the CC happened.
    6999           0 :   LOG(LogLevel::Debug,
    7000             :       ("%p IsAllowedToPlay() returning false due to null AudioChannelAgent.",
    7001             :        this));
    7002             :   return false;
    7003             : }
    7004             : 
    7005             : static const char*
    7006           0 : VisibilityString(Visibility aVisibility)
    7007             : {
    7008           0 :   switch (aVisibility) {
    7009             :     case Visibility::UNTRACKED: {
    7010             :       return "UNTRACKED";
    7011             :     }
    7012             :     case Visibility::APPROXIMATELY_NONVISIBLE: {
    7013           0 :       return "APPROXIMATELY_NONVISIBLE";
    7014             :     }
    7015             :     case Visibility::APPROXIMATELY_VISIBLE: {
    7016           0 :       return "APPROXIMATELY_VISIBLE";
    7017             :     }
    7018             :   }
    7019             : 
    7020           0 :   return "NAN";
    7021             : }
    7022             : 
    7023             : void
    7024           0 : HTMLMediaElement::OnVisibilityChange(Visibility aNewVisibility)
    7025             : {
    7026           0 :   LOG(LogLevel::Debug,
    7027             :       ("OnVisibilityChange(): %s\n", VisibilityString(aNewVisibility)));
    7028             : 
    7029           0 :   mVisibilityState = aNewVisibility;
    7030             : 
    7031           0 :   if (!mDecoder) {
    7032             :     return;
    7033             :   }
    7034             : 
    7035           0 :   switch (aNewVisibility) {
    7036             :     case Visibility::UNTRACKED: {
    7037           0 :       MOZ_ASSERT_UNREACHABLE("Shouldn't notify for untracked visibility");
    7038             :       return;
    7039             :     }
    7040             :     case Visibility::APPROXIMATELY_NONVISIBLE: {
    7041           0 :       if (mPlayTime.IsStarted()) {
    7042             :         // Not visible, play time is running -> Start hidden play time if
    7043             :         // needed.
    7044           0 :         HiddenVideoStart();
    7045             :       }
    7046             :       break;
    7047             :     }
    7048             :     case Visibility::APPROXIMATELY_VISIBLE: {
    7049             :       // Visible -> Just pause hidden play time (no-op if already paused).
    7050           0 :       HiddenVideoStop();
    7051           0 :       break;
    7052             :     }
    7053             :   }
    7054             : 
    7055           0 :   NotifyDecoderActivityChanges();
    7056             : }
    7057             : 
    7058             : MediaKeys*
    7059           0 : HTMLMediaElement::GetMediaKeys() const
    7060             : {
    7061           0 :   return mMediaKeys;
    7062             : }
    7063             : 
    7064             : bool
    7065           0 : HTMLMediaElement::ContainsRestrictedContent()
    7066             : {
    7067           0 :   return GetMediaKeys() != nullptr;
    7068             : }
    7069             : 
    7070             : void
    7071           0 : HTMLMediaElement::SetCDMProxyFailure(const MediaResult& aResult)
    7072             : {
    7073           0 :   LOG(LogLevel::Debug, ("%s", __func__));
    7074           0 :   MOZ_ASSERT(mSetMediaKeysDOMPromise);
    7075             : 
    7076           0 :   ResetSetMediaKeysTempVariables();
    7077             : 
    7078           0 :   mSetMediaKeysDOMPromise->MaybeReject(aResult.Code(), aResult.Message());
    7079           0 : }
    7080             : 
    7081             : void
    7082           0 : HTMLMediaElement::RemoveMediaKeys()
    7083             : {
    7084           0 :   LOG(LogLevel::Debug, ("%s", __func__));
    7085             :   // 5.2.3 Stop using the CDM instance represented by the mediaKeys attribute
    7086             :   // to decrypt media data and remove the association with the media element.
    7087           0 :   if (mMediaKeys) {
    7088           0 :     mMediaKeys->Unbind();
    7089             :   }
    7090           0 :   mMediaKeys = nullptr;
    7091           0 : }
    7092             : 
    7093             : bool
    7094           0 : HTMLMediaElement::TryRemoveMediaKeysAssociation()
    7095             : {
    7096           0 :   MOZ_ASSERT(mMediaKeys);
    7097           0 :   LOG(LogLevel::Debug, ("%s", __func__));
    7098             :   // 5.2.1 If the user agent or CDM do not support removing the association,
    7099             :   // let this object's attaching media keys value be false and reject promise
    7100             :   // with a new DOMException whose name is NotSupportedError.
    7101             :   // 5.2.2 If the association cannot currently be removed, let this object's
    7102             :   // attaching media keys value be false and reject promise with a new
    7103             :   // DOMException whose name is InvalidStateError.
    7104           0 :   if (mDecoder) {
    7105           0 :     RefPtr<HTMLMediaElement> self = this;
    7106           0 :     mDecoder->SetCDMProxy(nullptr)
    7107           0 :       ->Then(mAbstractMainThread,
    7108             :              __func__,
    7109           0 :              [self]() {
    7110           0 :                self->mSetCDMRequest.Complete();
    7111             : 
    7112           0 :                self->RemoveMediaKeys();
    7113           0 :                if (self->AttachNewMediaKeys()) {
    7114             :                  // No incoming MediaKeys object or MediaDecoder is not created
    7115             :                  // yet.
    7116           0 :                  self->MakeAssociationWithCDMResolved();
    7117             :                }
    7118           0 :              },
    7119           0 :              [self](const MediaResult& aResult) {
    7120           0 :                self->mSetCDMRequest.Complete();
    7121             :                // 5.2.4 If the preceding step failed, let this object's
    7122             :                // attaching media keys value be false and reject promise with a
    7123             :                // new DOMException whose name is the appropriate error name.
    7124           0 :                self->SetCDMProxyFailure(aResult);
    7125           0 :              })
    7126           0 :       ->Track(mSetCDMRequest);
    7127             :     return false;
    7128             :   }
    7129             : 
    7130           0 :   RemoveMediaKeys();
    7131           0 :   return true;
    7132             : }
    7133             : 
    7134             : bool
    7135           0 : HTMLMediaElement::DetachExistingMediaKeys()
    7136             : {
    7137           0 :   LOG(LogLevel::Debug, ("%s", __func__));
    7138           0 :   MOZ_ASSERT(mSetMediaKeysDOMPromise);
    7139             :   // 5.1 If mediaKeys is not null, CDM instance represented by mediaKeys is
    7140             :   // already in use by another media element, and the user agent is unable
    7141             :   // to use it with this element, let this object's attaching media keys
    7142             :   // value be false and reject promise with a new DOMException whose name
    7143             :   // is QuotaExceededError.
    7144           0 :   if (mIncomingMediaKeys && mIncomingMediaKeys->IsBoundToMediaElement()) {
    7145           0 :     SetCDMProxyFailure(MediaResult(
    7146             :       NS_ERROR_DOM_QUOTA_EXCEEDED_ERR,
    7147           0 :       "MediaKeys object is already bound to another HTMLMediaElement"));
    7148           0 :     return false;
    7149             :   }
    7150             : 
    7151             :   // 5.2 If the mediaKeys attribute is not null, run the following steps:
    7152           0 :   if (mMediaKeys) {
    7153           0 :     return TryRemoveMediaKeysAssociation();
    7154             :   }
    7155             :   return true;
    7156             : }
    7157             : 
    7158             : void
    7159           0 : HTMLMediaElement::MakeAssociationWithCDMResolved()
    7160             : {
    7161           0 :   LOG(LogLevel::Debug, ("%s", __func__));
    7162           0 :   MOZ_ASSERT(mSetMediaKeysDOMPromise);
    7163             : 
    7164             :   // 5.4 Set the mediaKeys attribute to mediaKeys.
    7165           0 :   mMediaKeys = mIncomingMediaKeys;
    7166             :   // 5.5 Let this object's attaching media keys value be false.
    7167           0 :   ResetSetMediaKeysTempVariables();
    7168             :   // 5.6 Resolve promise.
    7169           0 :   mSetMediaKeysDOMPromise->MaybeResolveWithUndefined();
    7170           0 :   mSetMediaKeysDOMPromise = nullptr;
    7171           0 : }
    7172             : 
    7173             : bool
    7174           0 : HTMLMediaElement::TryMakeAssociationWithCDM(CDMProxy* aProxy)
    7175             : {
    7176           0 :   LOG(LogLevel::Debug, ("%s", __func__));
    7177           0 :   MOZ_ASSERT(aProxy);
    7178             : 
    7179             :   // 5.3.3 Queue a task to run the "Attempt to Resume Playback If Necessary"
    7180             :   // algorithm on the media element.
    7181             :   // Note: Setting the CDMProxy on the MediaDecoder will unblock playback.
    7182           0 :   if (mDecoder) {
    7183             :     // CDMProxy is set asynchronously in MediaFormatReader, once it's done,
    7184             :     // HTMLMediaElement should resolve or reject the DOM promise.
    7185           0 :     RefPtr<HTMLMediaElement> self = this;
    7186           0 :     mDecoder->SetCDMProxy(aProxy)
    7187           0 :       ->Then(mAbstractMainThread,
    7188             :              __func__,
    7189           0 :              [self]() {
    7190           0 :                self->mSetCDMRequest.Complete();
    7191           0 :                self->MakeAssociationWithCDMResolved();
    7192           0 :              },
    7193           0 :              [self](const MediaResult& aResult) {
    7194           0 :                self->mSetCDMRequest.Complete();
    7195           0 :                self->SetCDMProxyFailure(aResult);
    7196           0 :              })
    7197           0 :       ->Track(mSetCDMRequest);
    7198             :     return false;
    7199             :   }
    7200             :   return true;
    7201             : }
    7202             : 
    7203             : bool
    7204           0 : HTMLMediaElement::AttachNewMediaKeys()
    7205             : {
    7206           0 :   LOG(LogLevel::Debug,
    7207             :       ("%s incoming MediaKeys(%p)", __func__, mIncomingMediaKeys.get()));
    7208           0 :   MOZ_ASSERT(mSetMediaKeysDOMPromise);
    7209             : 
    7210             :   // 5.3. If mediaKeys is not null, run the following steps:
    7211           0 :   if (mIncomingMediaKeys) {
    7212           0 :     auto cdmProxy = mIncomingMediaKeys->GetCDMProxy();
    7213           0 :     if (!cdmProxy) {
    7214           0 :       SetCDMProxyFailure(MediaResult(
    7215             :         NS_ERROR_DOM_INVALID_STATE_ERR,
    7216           0 :         "CDM crashed before binding MediaKeys object to HTMLMediaElement"));
    7217           0 :       return false;
    7218             :     }
    7219             : 
    7220             :     // 5.3.1 Associate the CDM instance represented by mediaKeys with the
    7221             :     // media element for decrypting media data.
    7222           0 :     if (NS_FAILED(mIncomingMediaKeys->Bind(this))) {
    7223             :       // 5.3.2 If the preceding step failed, run the following steps:
    7224             : 
    7225             :       // 5.3.2.1 Set the mediaKeys attribute to null.
    7226           0 :       mMediaKeys = nullptr;
    7227             :       // 5.3.2.2 Let this object's attaching media keys value be false.
    7228             :       // 5.3.2.3 Reject promise with a new DOMException whose name is
    7229             :       // the appropriate error name.
    7230             :       SetCDMProxyFailure(
    7231           0 :         MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
    7232           0 :                     "Failed to bind MediaKeys object to HTMLMediaElement"));
    7233           0 :       return false;
    7234             :     }
    7235           0 :     return TryMakeAssociationWithCDM(cdmProxy);
    7236             :   }
    7237             :   return true;
    7238             : }
    7239             : 
    7240             : void
    7241           0 : HTMLMediaElement::ResetSetMediaKeysTempVariables()
    7242             : {
    7243           0 :   mAttachingMediaKey = false;
    7244           0 :   mIncomingMediaKeys = nullptr;
    7245           0 : }
    7246             : 
    7247             : already_AddRefed<Promise>
    7248           0 : HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
    7249             :                                ErrorResult& aRv)
    7250             : {
    7251           0 :   LOG(LogLevel::Debug,
    7252             :       ("%p SetMediaKeys(%p) mMediaKeys=%p mDecoder=%p",
    7253             :        this,
    7254             :        aMediaKeys,
    7255             :        mMediaKeys.get(),
    7256             :        mDecoder.get()));
    7257             : 
    7258           0 :   if (MozAudioCaptured()) {
    7259           0 :     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
    7260             :     return nullptr;
    7261             :   }
    7262             : 
    7263           0 :   nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
    7264           0 :   if (!win) {
    7265           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
    7266             :     return nullptr;
    7267             :   }
    7268           0 :   RefPtr<DetailedPromise> promise = DetailedPromise::Create(
    7269           0 :     win->AsGlobal(), aRv, NS_LITERAL_CSTRING("HTMLMediaElement.setMediaKeys"));
    7270           0 :   if (aRv.Failed()) {
    7271             :     return nullptr;
    7272             :   }
    7273             : 
    7274             :   // 1. If mediaKeys and the mediaKeys attribute are the same object,
    7275             :   // return a resolved promise.
    7276           0 :   if (mMediaKeys == aMediaKeys) {
    7277           0 :     promise->MaybeResolveWithUndefined();
    7278           0 :     return promise.forget();
    7279             :   }
    7280             : 
    7281             :   // 2. If this object's attaching media keys value is true, return a
    7282             :   // promise rejected with a new DOMException whose name is InvalidStateError.
    7283           0 :   if (mAttachingMediaKey) {
    7284           0 :     promise->MaybeReject(
    7285             :       NS_ERROR_DOM_INVALID_STATE_ERR,
    7286           0 :       NS_LITERAL_CSTRING("A MediaKeys object is in attaching operation."));
    7287           0 :     return promise.forget();
    7288             :   }
    7289             : 
    7290             :   // 3. Let this object's attaching media keys value be true.
    7291           0 :   mAttachingMediaKey = true;
    7292           0 :   mIncomingMediaKeys = aMediaKeys;
    7293             : 
    7294             :   // 4. Let promise be a new promise.
    7295           0 :   mSetMediaKeysDOMPromise = promise;
    7296             : 
    7297             :   // 5. Run the following steps in parallel:
    7298             : 
    7299             :   // 5.1 & 5.2 & 5.3
    7300           0 :   if (!DetachExistingMediaKeys() || !AttachNewMediaKeys()) {
    7301           0 :     return promise.forget();
    7302             :   }
    7303             : 
    7304             :   // 5.4, 5.5, 5.6
    7305           0 :   MakeAssociationWithCDMResolved();
    7306             : 
    7307             :   // 6. Return promise.
    7308           0 :   return promise.forget();
    7309             : }
    7310             : 
    7311             : EventHandlerNonNull*
    7312           0 : HTMLMediaElement::GetOnencrypted()
    7313             : {
    7314           0 :   return EventTarget::GetEventHandler(nsGkAtoms::onencrypted, EmptyString());
    7315             : }
    7316             : 
    7317             : void
    7318           0 : HTMLMediaElement::SetOnencrypted(EventHandlerNonNull* aCallback)
    7319             : {
    7320           0 :   EventTarget::SetEventHandler(
    7321           0 :     nsGkAtoms::onencrypted, EmptyString(), aCallback);
    7322           0 : }
    7323             : 
    7324             : EventHandlerNonNull*
    7325           0 : HTMLMediaElement::GetOnwaitingforkey()
    7326             : {
    7327           0 :   return EventTarget::GetEventHandler(nsGkAtoms::onwaitingforkey,
    7328           0 :                                       EmptyString());
    7329             : }
    7330             : 
    7331             : void
    7332           0 : HTMLMediaElement::SetOnwaitingforkey(EventHandlerNonNull* aCallback)
    7333             : {
    7334           0 :   EventTarget::SetEventHandler(
    7335           0 :     nsGkAtoms::onwaitingforkey, EmptyString(), aCallback);
    7336           0 : }
    7337             : 
    7338             : void
    7339           0 : HTMLMediaElement::DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
    7340             :                                     const nsAString& aInitDataType)
    7341             : {
    7342           0 :   LOG(LogLevel::Debug,
    7343             :       ("%p DispatchEncrypted initDataType='%s'",
    7344             :        this,
    7345             :        NS_ConvertUTF16toUTF8(aInitDataType).get()));
    7346             : 
    7347           0 :   if (mReadyState == HAVE_NOTHING) {
    7348             :     // Ready state not HAVE_METADATA (yet), don't dispatch encrypted now.
    7349             :     // Queueing for later dispatch in MetadataLoaded.
    7350           0 :     mPendingEncryptedInitData.AddInitData(aInitDataType, aInitData);
    7351           0 :     return;
    7352             :   }
    7353             : 
    7354           0 :   RefPtr<MediaEncryptedEvent> event;
    7355           0 :   if (IsCORSSameOrigin()) {
    7356           0 :     event = MediaEncryptedEvent::Constructor(this, aInitDataType, aInitData);
    7357             :   } else {
    7358           0 :     event = MediaEncryptedEvent::Constructor(this);
    7359             :   }
    7360             : 
    7361             :   RefPtr<AsyncEventDispatcher> asyncDispatcher =
    7362           0 :     new AsyncEventDispatcher(this, event);
    7363           0 :   asyncDispatcher->PostDOMEvent();
    7364             : }
    7365             : 
    7366             : bool
    7367           0 : HTMLMediaElement::IsEventAttributeNameInternal(nsAtom* aName)
    7368             : {
    7369           0 :   return aName == nsGkAtoms::onencrypted ||
    7370           0 :          nsGenericHTMLElement::IsEventAttributeNameInternal(aName);
    7371             : }
    7372             : 
    7373             : already_AddRefed<nsIPrincipal>
    7374           0 : HTMLMediaElement::GetTopLevelPrincipal()
    7375             : {
    7376           0 :   RefPtr<nsIPrincipal> principal;
    7377           0 :   nsCOMPtr<nsPIDOMWindowInner> window = OwnerDoc()->GetInnerWindow();
    7378           0 :   if (!window) {
    7379             :     return nullptr;
    7380             :   }
    7381             :   // XXXkhuey better hope we always have an outer ...
    7382           0 :   nsCOMPtr<nsPIDOMWindowOuter> top = window->GetOuterWindow()->GetTop();
    7383           0 :   if (!top) {
    7384             :     return nullptr;
    7385             :   }
    7386           0 :   nsIDocument* doc = top->GetExtantDoc();
    7387           0 :   if (!doc) {
    7388             :     return nullptr;
    7389             :   }
    7390           0 :   principal = doc->NodePrincipal();
    7391             :   return principal.forget();
    7392             : }
    7393             : 
    7394             : void
    7395           0 : HTMLMediaElement::NotifyWaitingForKey()
    7396             : {
    7397           0 :   LOG(LogLevel::Debug, ("%p, NotifyWaitingForKey()", this));
    7398             : 
    7399             :   // http://w3c.github.io/encrypted-media/#wait-for-key
    7400             :   // 7.3.4 Queue a "waitingforkey" Event
    7401             :   // 1. Let the media element be the specified HTMLMediaElement object.
    7402             :   // 2. If the media element's waiting for key value is true, abort these steps.
    7403           0 :   if (mWaitingForKey == NOT_WAITING_FOR_KEY) {
    7404             :     // 3. Set the media element's waiting for key value to true.
    7405             :     // Note: algorithm continues in UpdateReadyStateInternal() when all decoded
    7406             :     // data enqueued in the MDSM is consumed.
    7407           0 :     mWaitingForKey = WAITING_FOR_KEY;
    7408           0 :     UpdateReadyStateInternal();
    7409             :   }
    7410           0 : }
    7411             : 
    7412             : AudioTrackList*
    7413           0 : HTMLMediaElement::AudioTracks()
    7414             : {
    7415           0 :   if (!mAudioTrackList) {
    7416             :     nsCOMPtr<nsPIDOMWindowInner> window =
    7417           0 :       do_QueryInterface(OwnerDoc()->GetParentObject());
    7418           0 :     mAudioTrackList = new AudioTrackList(window, this);
    7419             :   }
    7420           0 :   return mAudioTrackList;
    7421             : }
    7422             : 
    7423             : VideoTrackList*
    7424           0 : HTMLMediaElement::VideoTracks()
    7425             : {
    7426           0 :   if (!mVideoTrackList) {
    7427             :     nsCOMPtr<nsPIDOMWindowInner> window =
    7428           0 :       do_QueryInterface(OwnerDoc()->GetParentObject());
    7429           0 :     mVideoTrackList = new VideoTrackList(window, this);
    7430             :   }
    7431           0 :   return mVideoTrackList;
    7432             : }
    7433             : 
    7434             : TextTrackList*
    7435           0 : HTMLMediaElement::GetTextTracks()
    7436             : {
    7437           0 :   return GetOrCreateTextTrackManager()->GetTextTracks();
    7438             : }
    7439             : 
    7440             : already_AddRefed<TextTrack>
    7441           0 : HTMLMediaElement::AddTextTrack(TextTrackKind aKind,
    7442             :                                const nsAString& aLabel,
    7443             :                                const nsAString& aLanguage)
    7444             : {
    7445             :   return GetOrCreateTextTrackManager()->AddTextTrack(
    7446             :     aKind,
    7447             :     aLabel,
    7448             :     aLanguage,
    7449             :     TextTrackMode::Hidden,
    7450             :     TextTrackReadyState::Loaded,
    7451           0 :     TextTrackSource::AddTextTrack);
    7452             : }
    7453             : 
    7454             : void
    7455           0 : HTMLMediaElement::PopulatePendingTextTrackList()
    7456             : {
    7457           0 :   if (mTextTrackManager) {
    7458           0 :     mTextTrackManager->PopulatePendingList();
    7459             :   }
    7460           0 : }
    7461             : 
    7462             : TextTrackManager*
    7463           0 : HTMLMediaElement::GetOrCreateTextTrackManager()
    7464             : {
    7465           0 :   if (!mTextTrackManager) {
    7466           0 :     mTextTrackManager = new TextTrackManager(this);
    7467           0 :     mTextTrackManager->AddListeners();
    7468             :   }
    7469           0 :   return mTextTrackManager;
    7470             : }
    7471             : 
    7472             : MediaDecoderOwner::NextFrameStatus
    7473           0 : HTMLMediaElement::NextFrameStatus()
    7474             : {
    7475           0 :   if (mDecoder) {
    7476           0 :     return mDecoder->NextFrameStatus();
    7477           0 :   } else if (mMediaStreamListener) {
    7478           0 :     return mMediaStreamListener->NextFrameStatus();
    7479             :   }
    7480             :   return NEXT_FRAME_UNINITIALIZED;
    7481             : }
    7482             : 
    7483             : void
    7484           0 : HTMLMediaElement::SetDecoder(MediaDecoder* aDecoder)
    7485             : {
    7486           0 :   MOZ_ASSERT(aDecoder); // Use ShutdownDecoder() to clear.
    7487           0 :   if (mDecoder) {
    7488           0 :     ShutdownDecoder();
    7489             :   }
    7490           0 :   mDecoder = aDecoder;
    7491           0 :   DDLINKCHILD("decoder", mDecoder.get());
    7492           0 :   if (mDecoder && mForcedHidden) {
    7493           0 :     mDecoder->SetForcedHidden(mForcedHidden);
    7494             :   }
    7495           0 : }
    7496             : 
    7497             : float
    7498           0 : HTMLMediaElement::ComputedVolume() const
    7499             : {
    7500           0 :   return mMuted
    7501           0 :            ? 0.0f
    7502           0 :            : mAudioChannelWrapper ? mAudioChannelWrapper->GetEffectiveVolume()
    7503           0 :                                   : mVolume;
    7504             : }
    7505             : 
    7506             : bool
    7507           0 : HTMLMediaElement::ComputedMuted() const
    7508             : {
    7509           0 :   return (mMuted & MUTED_BY_AUDIO_CHANNEL);
    7510             : }
    7511             : 
    7512             : nsSuspendedTypes
    7513           0 : HTMLMediaElement::ComputedSuspended() const
    7514             : {
    7515           0 :   return mAudioChannelWrapper ? mAudioChannelWrapper->GetSuspendType()
    7516           0 :                               : nsISuspendedTypes::NONE_SUSPENDED;
    7517             : }
    7518             : 
    7519             : bool
    7520           0 : HTMLMediaElement::IsCurrentlyPlaying() const
    7521             : {
    7522             :   // We have playable data, but we still need to check whether data is "real"
    7523             :   // current data.
    7524           0 :   return mReadyState >= HAVE_CURRENT_DATA && !IsPlaybackEnded();
    7525             : }
    7526             : 
    7527             : void
    7528           0 : HTMLMediaElement::SetAudibleState(bool aAudible)
    7529             : {
    7530           0 :   if (mIsAudioTrackAudible != aAudible) {
    7531           0 :     mIsAudioTrackAudible = aAudible;
    7532             :     NotifyAudioPlaybackChanged(
    7533           0 :       AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
    7534             :   }
    7535           0 : }
    7536             : 
    7537             : void
    7538           0 : HTMLMediaElement::NotifyAudioPlaybackChanged(AudibleChangedReasons aReason)
    7539             : {
    7540           0 :   if (mAudioChannelWrapper) {
    7541           0 :     mAudioChannelWrapper->NotifyAudioPlaybackChanged(aReason);
    7542             :   }
    7543             :   // only request wake lock for audible media.
    7544           0 :   UpdateWakeLock();
    7545           0 : }
    7546             : 
    7547             : bool
    7548           2 : HTMLMediaElement::ShouldElementBePaused()
    7549             : {
    7550             :   // Bfcached page or inactive document.
    7551           0 :   if (!IsActive()) {
    7552             :     return true;
    7553             :   }
    7554             : 
    7555           2 :   return false;
    7556             : }
    7557             : 
    7558             : void
    7559           0 : HTMLMediaElement::SetMediaInfo(const MediaInfo& aInfo)
    7560             : {
    7561           0 :   const bool oldHasAudio = mMediaInfo.HasAudio();
    7562           0 :   mMediaInfo = aInfo;
    7563           0 :   if (aInfo.HasAudio() != oldHasAudio) {
    7564           0 :     UpdateAudioChannelPlayingState();
    7565             :     NotifyAudioPlaybackChanged(
    7566           0 :       AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
    7567             :   }
    7568           0 :   if (mAudioChannelWrapper) {
    7569           0 :     mAudioChannelWrapper->AudioCaptureStreamChangeIfNeeded();
    7570             :   }
    7571           0 : }
    7572             : 
    7573             : void
    7574           0 : HTMLMediaElement::AudioCaptureStreamChange(bool aCapture)
    7575             : {
    7576             :   // No need to capture a silence media element.
    7577           0 :   if (!HasAudio()) {
    7578             :     return;
    7579             :   }
    7580             : 
    7581           0 :   if (aCapture && !mCaptureStreamPort) {
    7582           0 :     nsCOMPtr<nsPIDOMWindowInner> window = OwnerDoc()->GetInnerWindow();
    7583           0 :     if (!OwnerDoc()->GetInnerWindow()) {
    7584           0 :       return;
    7585             :     }
    7586             : 
    7587           0 :     uint64_t id = window->WindowID();
    7588           0 :     MediaStreamGraph* msg = MediaStreamGraph::GetInstance(
    7589             :       MediaStreamGraph::AUDIO_THREAD_DRIVER,
    7590             :       window,
    7591           0 :       MediaStreamGraph::REQUEST_DEFAULT_SAMPLE_RATE);
    7592             : 
    7593           0 :     if (GetSrcMediaStream()) {
    7594           0 :       mCaptureStreamPort = msg->ConnectToCaptureStream(id, GetSrcMediaStream());
    7595             :     } else {
    7596             :       RefPtr<DOMMediaStream> stream =
    7597           0 :         CaptureStreamInternal(StreamCaptureBehavior::CONTINUE_WHEN_ENDED,
    7598             :                               StreamCaptureType::CAPTURE_AUDIO,
    7599           0 :                               msg);
    7600             :       mCaptureStreamPort =
    7601           0 :         msg->ConnectToCaptureStream(id, stream->GetPlaybackStream());
    7602             :     }
    7603           0 :   } else if (!aCapture && mCaptureStreamPort) {
    7604           0 :     if (mDecoder) {
    7605             :       ProcessedMediaStream* ps =
    7606           0 :         mCaptureStreamPort->GetSource()->AsProcessedStream();
    7607           0 :       MOZ_ASSERT(ps);
    7608             : 
    7609           0 :       for (uint32_t i = 0; i < mOutputStreams.Length(); i++) {
    7610           0 :         if (mOutputStreams[i].mStream->GetPlaybackStream() == ps) {
    7611           0 :           mOutputStreams.RemoveElementAt(i);
    7612           0 :           break;
    7613             :         }
    7614             :       }
    7615           0 :       mDecoder->RemoveOutputStream(ps);
    7616             :     }
    7617           0 :     mCaptureStreamPort->Destroy();
    7618           0 :     mCaptureStreamPort = nullptr;
    7619             :   }
    7620             : }
    7621             : 
    7622             : void
    7623           0 : HTMLMediaElement::NotifyCueDisplayStatesChanged()
    7624             : {
    7625           0 :   if (!mTextTrackManager) {
    7626             :     return;
    7627             :   }
    7628             : 
    7629           0 :   mTextTrackManager->DispatchUpdateCueDisplay();
    7630             : }
    7631             : 
    7632             : void
    7633           0 : HTMLMediaElement::MarkAsContentSource(CallerAPI aAPI)
    7634             : {
    7635           0 :   const bool isVisible = mVisibilityState == Visibility::APPROXIMATELY_VISIBLE;
    7636             : 
    7637           0 :   if (isVisible) {
    7638             :     // 0 = ALL_VISIBLE
    7639           0 :     Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 0);
    7640             :   } else {
    7641             :     // 1 = ALL_INVISIBLE
    7642           0 :     Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 1);
    7643             : 
    7644           0 :     if (IsInUncomposedDoc()) {
    7645             :       // 0 = ALL_IN_TREE
    7646             :       Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT,
    7647           0 :                             0);
    7648             :     } else {
    7649             :       // 1 = ALL_NOT_IN_TREE
    7650             :       Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT,
    7651           0 :                             1);
    7652             :     }
    7653             :   }
    7654             : 
    7655           0 :   switch (aAPI) {
    7656             :     case CallerAPI::DRAW_IMAGE: {
    7657           0 :       if (isVisible) {
    7658             :         // 2 = drawImage_VISIBLE
    7659           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 2);
    7660             :       } else {
    7661             :         // 3 = drawImage_INVISIBLE
    7662           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 3);
    7663             : 
    7664           0 :         if (IsInUncomposedDoc()) {
    7665             :           // 2 = drawImage_IN_TREE
    7666             :           Telemetry::Accumulate(
    7667           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 2);
    7668             :         } else {
    7669             :           // 3 = drawImage_NOT_IN_TREE
    7670             :           Telemetry::Accumulate(
    7671           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 3);
    7672             :         }
    7673             :       }
    7674             :       break;
    7675             :     }
    7676             :     case CallerAPI::CREATE_PATTERN: {
    7677           0 :       if (isVisible) {
    7678             :         // 4 = createPattern_VISIBLE
    7679           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 4);
    7680             :       } else {
    7681             :         // 5 = createPattern_INVISIBLE
    7682           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 5);
    7683             : 
    7684           0 :         if (IsInUncomposedDoc()) {
    7685             :           // 4 = createPattern_IN_TREE
    7686             :           Telemetry::Accumulate(
    7687           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 4);
    7688             :         } else {
    7689             :           // 5 = createPattern_NOT_IN_TREE
    7690             :           Telemetry::Accumulate(
    7691           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 5);
    7692             :         }
    7693             :       }
    7694             :       break;
    7695             :     }
    7696             :     case CallerAPI::CREATE_IMAGEBITMAP: {
    7697           0 :       if (isVisible) {
    7698             :         // 6 = createImageBitmap_VISIBLE
    7699           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 6);
    7700             :       } else {
    7701             :         // 7 = createImageBitmap_INVISIBLE
    7702           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 7);
    7703             : 
    7704           0 :         if (IsInUncomposedDoc()) {
    7705             :           // 6 = createImageBitmap_IN_TREE
    7706             :           Telemetry::Accumulate(
    7707           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 6);
    7708             :         } else {
    7709             :           // 7 = createImageBitmap_NOT_IN_TREE
    7710             :           Telemetry::Accumulate(
    7711           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 7);
    7712             :         }
    7713             :       }
    7714             :       break;
    7715             :     }
    7716             :     case CallerAPI::CAPTURE_STREAM: {
    7717           0 :       if (isVisible) {
    7718             :         // 8 = captureStream_VISIBLE
    7719           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 8);
    7720             :       } else {
    7721             :         // 9 = captureStream_INVISIBLE
    7722           0 :         Telemetry::Accumulate(Telemetry::VIDEO_AS_CONTENT_SOURCE, 9);
    7723             : 
    7724           0 :         if (IsInUncomposedDoc()) {
    7725             :           // 8 = captureStream_IN_TREE
    7726             :           Telemetry::Accumulate(
    7727           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 8);
    7728             :         } else {
    7729             :           // 9 = captureStream_NOT_IN_TREE
    7730             :           Telemetry::Accumulate(
    7731           0 :             Telemetry::VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT, 9);
    7732             :         }
    7733             :       }
    7734             :       break;
    7735             :     }
    7736             :   }
    7737             : 
    7738           0 :   LOG(LogLevel::Debug,
    7739             :       ("%p Log VIDEO_AS_CONTENT_SOURCE: visibility = %u, API: '%d' and 'All'",
    7740             :        this,
    7741             :        isVisible,
    7742             :        static_cast<int>(aAPI)));
    7743             : 
    7744           0 :   if (!isVisible) {
    7745           0 :     LOG(LogLevel::Debug,
    7746             :         ("%p Log VIDEO_AS_CONTENT_SOURCE_IN_TREE_OR_NOT: inTree = %u, API: "
    7747             :          "'%d' and 'All'",
    7748             :          this,
    7749             :          IsInUncomposedDoc(),
    7750             :          static_cast<int>(aAPI)));
    7751             :   }
    7752           0 : }
    7753             : 
    7754             : void
    7755           0 : HTMLMediaElement::UpdateCustomPolicyAfterPlayed()
    7756             : {
    7757           0 :   OpenUnsupportedMediaWithExternalAppIfNeeded();
    7758           0 :   if (mAudioChannelWrapper) {
    7759           0 :     mAudioChannelWrapper->NotifyPlayStateChanged();
    7760             :   }
    7761           0 : }
    7762             : 
    7763             : AbstractThread*
    7764           0 : HTMLMediaElement::AbstractMainThread() const
    7765             : {
    7766           0 :   MOZ_ASSERT(mAbstractMainThread);
    7767             : 
    7768           0 :   return mAbstractMainThread;
    7769             : }
    7770             : 
    7771             : nsTArray<RefPtr<PlayPromise>>
    7772           0 : HTMLMediaElement::TakePendingPlayPromises()
    7773             : {
    7774           0 :   return std::move(mPendingPlayPromises);
    7775             : }
    7776             : 
    7777             : void
    7778           0 : HTMLMediaElement::NotifyAboutPlaying()
    7779             : {
    7780             :   // Stick to the DispatchAsyncEvent() call path for now because we want to
    7781             :   // trigger some telemetry-related codes in the DispatchAsyncEvent() method.
    7782           0 :   DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
    7783           0 : }
    7784             : 
    7785             : already_AddRefed<PlayPromise>
    7786           0 : HTMLMediaElement::CreatePlayPromise(ErrorResult& aRv) const
    7787             : {
    7788           0 :   nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
    7789             : 
    7790           0 :   if (!win) {
    7791           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
    7792             :     return nullptr;
    7793             :   }
    7794             : 
    7795           0 :   RefPtr<PlayPromise> promise = PlayPromise::Create(win->AsGlobal(), aRv);
    7796           0 :   LOG(LogLevel::Debug, ("%p created PlayPromise %p", this, promise.get()));
    7797             : 
    7798           0 :   return promise.forget();
    7799             : }
    7800             : 
    7801             : already_AddRefed<Promise>
    7802           0 : HTMLMediaElement::CreateDOMPromise(ErrorResult& aRv) const
    7803             : {
    7804           0 :   nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
    7805             : 
    7806           0 :   if (!win) {
    7807           0 :     aRv.Throw(NS_ERROR_UNEXPECTED);
    7808             :     return nullptr;
    7809             :   }
    7810             : 
    7811           0 :   return Promise::Create(win->AsGlobal(), aRv);
    7812             : }
    7813             : 
    7814             : void
    7815           0 : HTMLMediaElement::AsyncResolvePendingPlayPromises()
    7816             : {
    7817           0 :   if (mShuttingDown) {
    7818           0 :     return;
    7819             :   }
    7820             : 
    7821             :   nsCOMPtr<nsIRunnable> event = new nsResolveOrRejectPendingPlayPromisesRunner(
    7822           0 :     this, TakePendingPlayPromises());
    7823             : 
    7824           0 :   mMainThreadEventTarget->Dispatch(event.forget());
    7825             : }
    7826             : 
    7827             : void
    7828           0 : HTMLMediaElement::AsyncRejectPendingPlayPromises(nsresult aError)
    7829             : {
    7830           0 :   if (mShuttingDown) {
    7831           0 :     return;
    7832             :   }
    7833             : 
    7834             :   nsCOMPtr<nsIRunnable> event = new nsResolveOrRejectPendingPlayPromisesRunner(
    7835           0 :     this, TakePendingPlayPromises(), aError);
    7836             : 
    7837           0 :   mMainThreadEventTarget->Dispatch(event.forget());
    7838             : }
    7839             : 
    7840             : void
    7841           0 : HTMLMediaElement::GetEMEInfo(nsString& aEMEInfo)
    7842             : {
    7843           0 :   if (!mMediaKeys) {
    7844           0 :     return;
    7845             :   }
    7846             : 
    7847           0 :   nsString keySystem;
    7848           0 :   mMediaKeys->GetKeySystem(keySystem);
    7849             : 
    7850           0 :   nsString sessionsInfo;
    7851           0 :   mMediaKeys->GetSessionsInfo(sessionsInfo);
    7852             : 
    7853           0 :   aEMEInfo.AppendLiteral("Key System=");
    7854           0 :   aEMEInfo.Append(keySystem);
    7855           0 :   aEMEInfo.AppendLiteral(" SessionsInfo=");
    7856           0 :   aEMEInfo.Append(sessionsInfo);
    7857             : }
    7858             : 
    7859             : void
    7860           0 : HTMLMediaElement::NotifyDecoderActivityChanges() const
    7861             : {
    7862           2 :   if (mDecoder) {
    7863           0 :     mDecoder->NotifyOwnerActivityChanged(
    7864           0 :       !IsHidden(), mVisibilityState, IsInUncomposedDoc());
    7865             :   }
    7866           1 : }
    7867             : 
    7868             : nsIDocument*
    7869           0 : HTMLMediaElement::GetDocument() const
    7870             : {
    7871           0 :   return OwnerDoc();
    7872             : }
    7873             : 
    7874             : void
    7875           0 : HTMLMediaElement::ConstructMediaTracks(const MediaInfo* aInfo)
    7876             : {
    7877           0 :   if (mMediaTracksConstructed || !aInfo) {
    7878             :     return;
    7879             :   }
    7880             : 
    7881           0 :   mMediaTracksConstructed = true;
    7882             : 
    7883           0 :   AudioTrackList* audioList = AudioTracks();
    7884           0 :   if (audioList && aInfo->HasAudio()) {
    7885           0 :     const TrackInfo& info = aInfo->mAudio;
    7886             :     RefPtr<AudioTrack> track =
    7887           0 :       MediaTrackList::CreateAudioTrack(audioList->GetOwnerGlobal(),
    7888             :                                        info.mId,
    7889             :                                        info.mKind,
    7890             :                                        info.mLabel,
    7891             :                                        info.mLanguage,
    7892           0 :                                        info.mEnabled);
    7893             : 
    7894           0 :     audioList->AddTrack(track);
    7895             :   }
    7896             : 
    7897           0 :   VideoTrackList* videoList = VideoTracks();
    7898           0 :   if (videoList && aInfo->HasVideo()) {
    7899           0 :     const TrackInfo& info = aInfo->mVideo;
    7900             :     RefPtr<VideoTrack> track =
    7901           0 :       MediaTrackList::CreateVideoTrack(videoList->GetOwnerGlobal(),
    7902             :                                        info.mId,
    7903             :                                        info.mKind,
    7904             :                                        info.mLabel,
    7905           0 :                                        info.mLanguage);
    7906             : 
    7907           0 :     videoList->AddTrack(track);
    7908           0 :     track->SetEnabledInternal(info.mEnabled, MediaTrack::FIRE_NO_EVENTS);
    7909             :   }
    7910             : }
    7911             : 
    7912             : void
    7913           0 : HTMLMediaElement::RemoveMediaTracks()
    7914             : {
    7915           0 :   if (mAudioTrackList) {
    7916           0 :     mAudioTrackList->RemoveTracks();
    7917             :   }
    7918             : 
    7919           0 :   if (mVideoTrackList) {
    7920           0 :     mVideoTrackList->RemoveTracks();
    7921             :   }
    7922             : 
    7923           0 :   mMediaTracksConstructed = false;
    7924             : 
    7925           0 :   for (OutputMediaStream& ms : mOutputStreams) {
    7926           0 :     if (!ms.mCapturingDecoder) {
    7927             :       continue;
    7928             :     }
    7929           0 :     for (RefPtr<MediaStreamTrack>& t : ms.mPreCreatedTracks) {
    7930           0 :       if (t->Ended()) {
    7931             :         continue;
    7932             :       }
    7933           0 :       mAbstractMainThread->Dispatch(NewRunnableMethod(
    7934             :         "dom::HTMLMediaElement::RemoveMediaTracks",
    7935           0 :         t, &MediaStreamTrack::OverrideEnded));
    7936             :     }
    7937           0 :     ms.mPreCreatedTracks.Clear();
    7938             :   }
    7939           0 : }
    7940             : 
    7941           0 : class MediaElementGMPCrashHelper : public GMPCrashHelper
    7942             : {
    7943             : public:
    7944           0 :   explicit MediaElementGMPCrashHelper(HTMLMediaElement* aElement)
    7945           0 :     : mElement(aElement)
    7946             :   {
    7947           0 :     MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
    7948           0 :   }
    7949           0 :   already_AddRefed<nsPIDOMWindowInner> GetPluginCrashedEventTarget() override
    7950             :   {
    7951           0 :     MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
    7952           0 :     if (!mElement) {
    7953             :       return nullptr;
    7954             :     }
    7955           0 :     return do_AddRef(mElement->OwnerDoc()->GetInnerWindow());
    7956             :   }
    7957             : 
    7958             : private:
    7959             :   WeakPtr<HTMLMediaElement> mElement;
    7960             : };
    7961             : 
    7962             : already_AddRefed<GMPCrashHelper>
    7963           0 : HTMLMediaElement::CreateGMPCrashHelper()
    7964             : {
    7965           0 :   return MakeAndAddRef<MediaElementGMPCrashHelper>(this);
    7966             : }
    7967             : 
    7968             : void
    7969           0 : HTMLMediaElement::MarkAsTainted()
    7970             : {
    7971           0 :   mHasSuspendTaint = true;
    7972             : 
    7973           0 :   if (mDecoder) {
    7974           0 :     mDecoder->SetSuspendTaint(true);
    7975             :   }
    7976           0 : }
    7977             : 
    7978             : bool
    7979           0 : HasDebuggerOrTabsPrivilege(JSContext* aCx, JSObject* aObj)
    7980             : {
    7981           0 :   return nsContentUtils::CallerHasPermission(aCx, nsGkAtoms::debugger) ||
    7982           0 :          nsContentUtils::CallerHasPermission(aCx, nsGkAtoms::tabs);
    7983             : }
    7984             : 
    7985             : void
    7986           0 : HTMLMediaElement::AsyncResolveSeekDOMPromiseIfExists()
    7987             : {
    7988           0 :   MOZ_ASSERT(NS_IsMainThread());
    7989           0 :   if (mSeekDOMPromise) {
    7990           0 :     RefPtr<dom::Promise> promise = mSeekDOMPromise.forget();
    7991           0 :     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    7992             :       "dom::HTMLMediaElement::AsyncResolveSeekDOMPromiseIfExists",
    7993           0 :       [=]() { promise->MaybeResolveWithUndefined(); });
    7994           0 :     mAbstractMainThread->Dispatch(r.forget());
    7995           0 :     mSeekDOMPromise = nullptr;
    7996             :   }
    7997           0 : }
    7998             : 
    7999             : void
    8000           0 : HTMLMediaElement::AsyncRejectSeekDOMPromiseIfExists()
    8001             : {
    8002           0 :   MOZ_ASSERT(NS_IsMainThread());
    8003           0 :   if (mSeekDOMPromise) {
    8004           0 :     RefPtr<dom::Promise> promise = mSeekDOMPromise.forget();
    8005           0 :     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    8006             :       "dom::HTMLMediaElement::AsyncRejectSeekDOMPromiseIfExists",
    8007           0 :       [=]() { promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); });
    8008           0 :     mAbstractMainThread->Dispatch(r.forget());
    8009           0 :     mSeekDOMPromise = nullptr;
    8010             :   }
    8011           0 : }
    8012             : 
    8013             : void
    8014           0 : HTMLMediaElement::ReportCanPlayTelemetry()
    8015             : {
    8016           0 :   LOG(LogLevel::Debug, ("%s", __func__));
    8017             : 
    8018           0 :   RefPtr<nsIThread> thread;
    8019           0 :   nsresult rv = NS_NewNamedThread("MediaTelemetry", getter_AddRefs(thread));
    8020           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    8021           0 :     return;
    8022             :   }
    8023             : 
    8024           0 :   RefPtr<AbstractThread> abstractThread = mAbstractMainThread;
    8025             : 
    8026           0 :   thread->Dispatch(
    8027           0 :     NS_NewRunnableFunction(
    8028             :       "dom::HTMLMediaElement::ReportCanPlayTelemetry",
    8029           0 :       [thread, abstractThread]() {
    8030             : #if XP_WIN
    8031             :         // Windows Media Foundation requires MSCOM to be inited.
    8032             :         DebugOnly<HRESULT> hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    8033             :         MOZ_ASSERT(hr == S_OK);
    8034             : #endif
    8035             :         bool aac = MP4Decoder::IsSupportedType(
    8036           0 :           MediaContainerType(MEDIAMIMETYPE(AUDIO_MP4)), nullptr);
    8037             :         bool h264 = MP4Decoder::IsSupportedType(
    8038           0 :           MediaContainerType(MEDIAMIMETYPE(VIDEO_MP4)), nullptr);
    8039             : #if XP_WIN
    8040             :         CoUninitialize();
    8041             : #endif
    8042           0 :         abstractThread->Dispatch(NS_NewRunnableFunction(
    8043             :           "dom::HTMLMediaElement::ReportCanPlayTelemetry",
    8044           0 :           [thread, aac, h264]() {
    8045           0 :             LOG(LogLevel::Debug, ("MediaTelemetry aac=%d h264=%d", aac, h264));
    8046           0 :             Telemetry::Accumulate(
    8047           0 :               Telemetry::HistogramID::VIDEO_CAN_CREATE_AAC_DECODER, aac);
    8048           0 :             Telemetry::Accumulate(
    8049           0 :               Telemetry::HistogramID::VIDEO_CAN_CREATE_H264_DECODER, h264);
    8050           0 :             thread->AsyncShutdown();
    8051           0 :           }));
    8052           0 :       }),
    8053           0 :     NS_DISPATCH_NORMAL);
    8054             : }
    8055             : 
    8056             : } // namespace dom
    8057             : } // namespace mozilla
    8058             : 
    8059             : #undef LOG
    8060             : #undef LOG_EVENT

Generated by: LCOV version 1.13-14-ga5dd952