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

Generated by: LCOV version 1.13