LCOV - code coverage report
Current view: directory - content/html/content/src - nsHTMLMediaElement.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1301 2 0.2 %
Date: 2012-06-02 Functions: 171 2 1.2 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is the Mozilla Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2007
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *  Chris Double <chris.double@double.co.nz>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "mozilla/Util.h"
      40                 : 
      41                 : #include "nsIDOMHTMLMediaElement.h"
      42                 : #include "nsIDOMHTMLSourceElement.h"
      43                 : #include "nsHTMLMediaElement.h"
      44                 : #include "nsTimeRanges.h"
      45                 : #include "nsGenericHTMLElement.h"
      46                 : #include "nsPresContext.h"
      47                 : #include "nsIPresShell.h"
      48                 : #include "nsGkAtoms.h"
      49                 : #include "nsSize.h"
      50                 : #include "nsIFrame.h"
      51                 : #include "nsIDocument.h"
      52                 : #include "nsIDOMDocument.h"
      53                 : #include "nsDOMError.h"
      54                 : #include "nsNodeInfoManager.h"
      55                 : #include "nsNetUtil.h"
      56                 : #include "nsXPCOMStrings.h"
      57                 : #include "nsThreadUtils.h"
      58                 : #include "nsIThreadInternal.h"
      59                 : #include "nsContentUtils.h"
      60                 : #include "nsFrameManager.h"
      61                 : #include "nsIScriptSecurityManager.h"
      62                 : #include "nsIXPConnect.h"
      63                 : #include "jsapi.h"
      64                 : 
      65                 : #include "nsITimer.h"
      66                 : 
      67                 : #include "nsEventDispatcher.h"
      68                 : #include "nsMediaError.h"
      69                 : #include "nsICategoryManager.h"
      70                 : #include "nsCharSeparatedTokenizer.h"
      71                 : #include "MediaResource.h"
      72                 : 
      73                 : #include "nsIDOMHTMLVideoElement.h"
      74                 : #include "nsIContentPolicy.h"
      75                 : #include "nsContentPolicyUtils.h"
      76                 : #include "nsContentErrors.h"
      77                 : #include "nsCrossSiteListenerProxy.h"
      78                 : #include "nsCycleCollectionParticipant.h"
      79                 : #include "nsICachingChannel.h"
      80                 : #include "nsLayoutUtils.h"
      81                 : #include "nsVideoFrame.h"
      82                 : #include "BasicLayers.h"
      83                 : #include <limits>
      84                 : #include "nsIDocShellTreeItem.h"
      85                 : #include "nsIAsyncVerifyRedirectCallback.h"
      86                 : #include "nsIAppShell.h"
      87                 : #include "nsWidgetsCID.h"
      88                 : 
      89                 : #include "nsIPrivateDOMEvent.h"
      90                 : #include "nsIDOMNotifyAudioAvailableEvent.h"
      91                 : #include "nsMediaFragmentURIParser.h"
      92                 : #include "nsURIHashKey.h"
      93                 : #include "nsContentUtils.h"
      94                 : #include "nsIScriptError.h"
      95                 : 
      96                 : #ifdef MOZ_OGG
      97                 : #include "nsOggDecoder.h"
      98                 : #endif
      99                 : #ifdef MOZ_WAVE
     100                 : #include "nsWaveDecoder.h"
     101                 : #endif
     102                 : #ifdef MOZ_WEBM
     103                 : #include "nsWebMDecoder.h"
     104                 : #endif
     105                 : #ifdef MOZ_RAW
     106                 : #include "nsRawDecoder.h"
     107                 : #endif
     108                 : 
     109                 : #ifdef PR_LOGGING
     110                 : static PRLogModuleInfo* gMediaElementLog;
     111                 : static PRLogModuleInfo* gMediaElementEventsLog;
     112                 : #define LOG(type, msg) PR_LOG(gMediaElementLog, type, msg)
     113                 : #define LOG_EVENT(type, msg) PR_LOG(gMediaElementEventsLog, type, msg)
     114                 : #else
     115                 : #define LOG(type, msg)
     116                 : #define LOG_EVENT(type, msg)
     117                 : #endif
     118                 : 
     119                 : #include "nsIContentSecurityPolicy.h"
     120                 : #include "nsIChannelPolicy.h"
     121                 : #include "nsChannelPolicy.h"
     122                 : 
     123                 : #include "mozilla/Preferences.h"
     124                 : 
     125                 : using namespace mozilla;
     126                 : using namespace mozilla::dom;
     127                 : using namespace mozilla::layers;
     128                 : 
     129                 : // Number of milliseconds between timeupdate events as defined by spec
     130                 : #define TIMEUPDATE_MS 250
     131                 : 
     132                 : // Under certain conditions there may be no-one holding references to
     133                 : // a media element from script, DOM parent, etc, but the element may still
     134                 : // fire meaningful events in the future so we can't destroy it yet:
     135                 : // 1) If the element is delaying the load event (or would be, if it were
     136                 : // in a document), then events up to loadeddata or error could be fired,
     137                 : // so we need to stay alive.
     138                 : // 2) If the element is not paused and playback has not ended, then
     139                 : // we will (or might) play, sending timeupdate and ended events and possibly
     140                 : // audio output, so we need to stay alive.
     141                 : // 3) if the element is seeking then we will fire seeking events and possibly
     142                 : // start playing afterward, so we need to stay alive.
     143                 : // 4) If autoplay could start playback in this element (if we got enough data),
     144                 : // then we need to stay alive.
     145                 : // 5) if the element is currently loading and not suspended,
     146                 : // script might be waiting for progress events or a 'suspend' event,
     147                 : // so we need to stay alive. If we're already suspended then (all other
     148                 : // conditions being met) it's OK to just disappear without firing any more
     149                 : // events, since we have the freedom to remain suspended indefinitely. Note
     150                 : // that we could use this 'suspended' loophole to garbage-collect a suspended
     151                 : // element in case 4 even if it had 'autoplay' set, but we choose not to.
     152                 : // If someone throws away all references to a loading 'autoplay' element
     153                 : // sound should still eventually play.
     154                 : //
     155                 : // Media elements owned by inactive documents (i.e. documents not contained in any
     156                 : // document viewer) should never hold a self-reference because none of the
     157                 : // above conditions are allowed: the element will stop loading and playing
     158                 : // and never resume loading or playing unless its owner document changes to
     159                 : // an active document (which can only happen if there is an external reference
     160                 : // to the element).
     161                 : // Media elements with no owner doc should be able to hold a self-reference.
     162                 : // Something native must have created the element and may expect it to
     163                 : // stay alive to play.
     164                 : 
     165                 : // It's very important that any change in state which could change the value of
     166                 : // needSelfReference in AddRemoveSelfReference be followed by a call to
     167                 : // AddRemoveSelfReference before this element could die!
     168                 : // It's especially important if needSelfReference would change to 'true',
     169                 : // since if we neglect to add a self-reference, this element might be
     170                 : // garbage collected while there are still event listeners that should
     171                 : // receive events. If we neglect to remove the self-reference then the element
     172                 : // just lives longer than it needs to.
     173                 : 
     174                 : class nsMediaEvent : public nsRunnable
     175                 : {
     176                 : public:
     177                 : 
     178               0 :   nsMediaEvent(nsHTMLMediaElement* aElement) :
     179                 :     mElement(aElement),
     180               0 :     mLoadID(mElement->GetCurrentLoadID()) {}
     181               0 :   ~nsMediaEvent() {}
     182                 : 
     183                 :   NS_IMETHOD Run() = 0;
     184                 : 
     185                 : protected:
     186               0 :   bool IsCancelled() {
     187               0 :     return mElement->GetCurrentLoadID() != mLoadID;
     188                 :   }
     189                 : 
     190                 :   nsRefPtr<nsHTMLMediaElement> mElement;
     191                 :   PRUint32 mLoadID;
     192                 : };
     193                 : 
     194                 : class nsAsyncEventRunner : public nsMediaEvent
     195               0 : {
     196                 : private:
     197                 :   nsString mName;
     198                 : 
     199                 : public:
     200               0 :   nsAsyncEventRunner(const nsAString& aName, nsHTMLMediaElement* aElement) :
     201               0 :     nsMediaEvent(aElement), mName(aName)
     202                 :   {
     203               0 :   }
     204                 : 
     205               0 :   NS_IMETHOD Run()
     206                 :   {
     207                 :     // Silently cancel if our load has been cancelled.
     208               0 :     if (IsCancelled())
     209               0 :       return NS_OK;
     210                 : 
     211               0 :     return mElement->DispatchEvent(mName);
     212                 :   }
     213                 : };
     214                 : 
     215                 : class nsSourceErrorEventRunner : public nsMediaEvent
     216               0 : {
     217                 : private:
     218                 :   nsCOMPtr<nsIContent> mSource;
     219                 : public:
     220               0 :   nsSourceErrorEventRunner(nsHTMLMediaElement* aElement,
     221                 :                            nsIContent* aSource)
     222                 :     : nsMediaEvent(aElement),
     223               0 :       mSource(aSource)
     224                 :   {
     225               0 :   }
     226                 : 
     227               0 :   NS_IMETHOD Run() {
     228                 :     // Silently cancel if our load has been cancelled.
     229               0 :     if (IsCancelled())
     230               0 :       return NS_OK;
     231               0 :     LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching simple event source error", mElement.get()));
     232               0 :     return nsContentUtils::DispatchTrustedEvent(mElement->OwnerDoc(),
     233                 :                                                 mSource,
     234               0 :                                                 NS_LITERAL_STRING("error"),
     235                 :                                                 false,
     236               0 :                                                 true);
     237                 :   }
     238                 : };
     239                 : 
     240                 : /**
     241                 :  * There is a reference cycle involving this class: MediaLoadListener
     242                 :  * holds a reference to the nsHTMLMediaElement, which holds a reference
     243                 :  * to an nsIChannel, which holds a reference to this listener.
     244                 :  * We break the reference cycle in OnStartRequest by clearing mElement.
     245                 :  */
     246                 : class nsHTMLMediaElement::MediaLoadListener MOZ_FINAL : public nsIStreamListener,
     247                 :                                                         public nsIChannelEventSink,
     248                 :                                                         public nsIInterfaceRequestor,
     249                 :                                                         public nsIObserver
     250               0 : {
     251                 :   NS_DECL_ISUPPORTS
     252                 :   NS_DECL_NSIREQUESTOBSERVER
     253                 :   NS_DECL_NSISTREAMLISTENER
     254                 :   NS_DECL_NSICHANNELEVENTSINK
     255                 :   NS_DECL_NSIOBSERVER
     256                 :   NS_DECL_NSIINTERFACEREQUESTOR
     257                 : 
     258                 : public:
     259               0 :   MediaLoadListener(nsHTMLMediaElement* aElement)
     260                 :     : mElement(aElement),
     261               0 :       mLoadID(aElement->GetCurrentLoadID())
     262                 :   {
     263               0 :     NS_ABORT_IF_FALSE(mElement, "Must pass an element to call back");
     264               0 :   }
     265                 : 
     266                 : private:
     267                 :   nsRefPtr<nsHTMLMediaElement> mElement;
     268                 :   nsCOMPtr<nsIStreamListener> mNextListener;
     269                 :   PRUint32 mLoadID;
     270                 : };
     271                 : 
     272               0 : NS_IMPL_ISUPPORTS5(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver,
     273                 :                    nsIStreamListener, nsIChannelEventSink,
     274                 :                    nsIInterfaceRequestor, nsIObserver)
     275                 : 
     276                 : NS_IMETHODIMP
     277               0 : nsHTMLMediaElement::MediaLoadListener::Observe(nsISupports* aSubject,
     278                 :                                                const char* aTopic, const PRUnichar* aData)
     279                 : {
     280               0 :   nsContentUtils::UnregisterShutdownObserver(this);
     281                 : 
     282                 :   // Clear mElement to break cycle so we don't leak on shutdown
     283               0 :   mElement = nsnull;
     284               0 :   return NS_OK;
     285                 : }
     286                 : 
     287               0 : void nsHTMLMediaElement::ReportLoadError(const char* aMsg,
     288                 :                                          const PRUnichar** aParams,
     289                 :                                          PRUint32 aParamCount)
     290                 : {
     291                 :   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     292                 :                                   "Media",
     293                 :                                   OwnerDoc(),
     294                 :                                   nsContentUtils::eDOM_PROPERTIES,
     295                 :                                   aMsg,
     296                 :                                   aParams,
     297               0 :                                   aParamCount);
     298               0 : }
     299                 : 
     300                 : 
     301               0 : NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
     302                 : {
     303               0 :   nsContentUtils::UnregisterShutdownObserver(this);
     304                 : 
     305               0 :   if (!mElement) {
     306                 :     // We've been notified by the shutdown observer, and are shutting down.
     307               0 :     return NS_BINDING_ABORTED;
     308                 :   }
     309                 : 
     310                 :   // The element is only needed until we've had a chance to call
     311                 :   // InitializeDecoderForChannel. So make sure mElement is cleared here.
     312               0 :   nsRefPtr<nsHTMLMediaElement> element;
     313               0 :   element.swap(mElement);
     314                 : 
     315               0 :   if (mLoadID != element->GetCurrentLoadID()) {
     316                 :     // The channel has been cancelled before we had a chance to create
     317                 :     // a decoder. Abort, don't dispatch an "error" event, as the new load
     318                 :     // may not be in an error state.
     319               0 :     return NS_BINDING_ABORTED;
     320                 :   }
     321                 : 
     322                 :   // Don't continue to load if the request failed or has been canceled.
     323                 :   nsresult status;
     324               0 :   nsresult rv = aRequest->GetStatus(&status);
     325               0 :   NS_ENSURE_SUCCESS(rv, rv);
     326               0 :   if (NS_FAILED(status)) {
     327               0 :     if (element)
     328               0 :       element->NotifyLoadError();
     329               0 :     return status;
     330                 :   }
     331                 : 
     332               0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
     333                 :   bool succeeded;
     334               0 :   if (hc && NS_SUCCEEDED(hc->GetRequestSucceeded(&succeeded)) && !succeeded) {
     335               0 :     element->NotifyLoadError();
     336               0 :     PRUint32 responseStatus = 0;
     337               0 :     hc->GetResponseStatus(&responseStatus);
     338               0 :     nsAutoString code;
     339               0 :     code.AppendInt(responseStatus);
     340               0 :     nsAutoString src;
     341               0 :     element->GetCurrentSrc(src);
     342               0 :     const PRUnichar* params[] = { code.get(), src.get() };
     343               0 :     element->ReportLoadError("MediaLoadHttpError", params, ArrayLength(params));
     344               0 :     return NS_BINDING_ABORTED;
     345                 :   }
     346                 : 
     347               0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     348               0 :   if (channel &&
     349               0 :       element &&
     350               0 :       NS_SUCCEEDED(rv = element->InitializeDecoderForChannel(channel, getter_AddRefs(mNextListener))) &&
     351               0 :       mNextListener) {
     352               0 :     rv = mNextListener->OnStartRequest(aRequest, aContext);
     353                 :   } else {
     354                 :     // If InitializeDecoderForChannel() returned an error, fire a network
     355                 :     // error.
     356               0 :     if (NS_FAILED(rv) && !mNextListener && element) {
     357                 :       // Load failed, attempt to load the next candidate resource. If there
     358                 :       // are none, this will trigger a MEDIA_ERR_SRC_NOT_SUPPORTED error.
     359               0 :       element->NotifyLoadError();
     360                 :     }
     361                 :     // If InitializeDecoderForChannel did not return a listener (but may
     362                 :     // have otherwise succeeded), we abort the connection since we aren't
     363                 :     // interested in keeping the channel alive ourselves.
     364               0 :     rv = NS_BINDING_ABORTED;
     365                 :   }
     366                 : 
     367               0 :   return rv;
     368                 : }
     369                 : 
     370               0 : NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
     371                 :                                                                    nsresult aStatus)
     372                 : {
     373               0 :   if (mNextListener) {
     374               0 :     return mNextListener->OnStopRequest(aRequest, aContext, aStatus);
     375                 :   }
     376               0 :   return NS_OK;
     377                 : }
     378                 : 
     379               0 : NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
     380                 :                                                                      nsIInputStream* aStream, PRUint32 aOffset,
     381                 :                                                                      PRUint32 aCount)
     382                 : {
     383               0 :   if (!mNextListener) {
     384               0 :     NS_ERROR("Must have a chained listener; OnStartRequest should have canceled this request");
     385               0 :     return NS_BINDING_ABORTED;
     386                 :   }
     387               0 :   return mNextListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount);
     388                 : }
     389                 : 
     390               0 : NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
     391                 :                                                                             nsIChannel* aNewChannel,
     392                 :                                                                             PRUint32 aFlags,
     393                 :                                                                             nsIAsyncVerifyRedirectCallback* cb)
     394                 : {
     395                 :   // TODO is this really correct?? See bug #579329.
     396               0 :   if (mElement)
     397               0 :     mElement->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
     398               0 :   nsCOMPtr<nsIChannelEventSink> sink = do_QueryInterface(mNextListener);
     399               0 :   if (sink)
     400               0 :     return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
     401                 : 
     402               0 :   cb->OnRedirectVerifyCallback(NS_OK);
     403               0 :   return NS_OK;
     404                 : }
     405                 : 
     406               0 : NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::GetInterface(const nsIID & aIID, void **aResult)
     407                 : {
     408               0 :   return QueryInterface(aIID, aResult);
     409                 : }
     410                 : 
     411               0 : NS_IMPL_ADDREF_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
     412               0 : NS_IMPL_RELEASE_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
     413                 : 
     414            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLMediaElement)
     415                 : 
     416               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
     417               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSourcePointer)
     418               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLoadBlockedDoc)
     419               0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSourceLoadCandidate)
     420               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     421                 : 
     422               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
     423               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSourcePointer)
     424               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLoadBlockedDoc)
     425               0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSourceLoadCandidate)
     426               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     427                 : 
     428               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsHTMLMediaElement)
     429               0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     430               0 : NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
     431                 : 
     432                 : // nsIDOMHTMLMediaElement
     433               0 : NS_IMPL_URI_ATTR(nsHTMLMediaElement, Src, src)
     434               0 : NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Controls, controls)
     435               0 : NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Autoplay, autoplay)
     436               0 : NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Loop, loop)
     437               0 : NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, DefaultMuted, muted)
     438               0 : NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLMediaElement, Preload, preload, NULL)
     439                 : 
     440                 : /* readonly attribute nsIDOMHTMLMediaElement mozAutoplayEnabled; */
     441               0 : NS_IMETHODIMP nsHTMLMediaElement::GetMozAutoplayEnabled(bool *aAutoplayEnabled)
     442                 : {
     443               0 :   *aAutoplayEnabled = mAutoplayEnabled;
     444                 : 
     445               0 :   return NS_OK;
     446                 : }
     447                 : 
     448                 : /* readonly attribute nsIDOMMediaError error; */
     449               0 : NS_IMETHODIMP nsHTMLMediaElement::GetError(nsIDOMMediaError * *aError)
     450                 : {
     451               0 :   NS_IF_ADDREF(*aError = mError);
     452                 : 
     453               0 :   return NS_OK;
     454                 : }
     455                 : 
     456                 : /* readonly attribute boolean ended; */
     457               0 : NS_IMETHODIMP nsHTMLMediaElement::GetEnded(bool *aEnded)
     458                 : {
     459               0 :   *aEnded = mDecoder ? mDecoder->IsEnded() : false;
     460                 : 
     461               0 :   return NS_OK;
     462                 : }
     463                 : 
     464                 : /* readonly attribute DOMString currentSrc; */
     465               0 : NS_IMETHODIMP nsHTMLMediaElement::GetCurrentSrc(nsAString & aCurrentSrc)
     466                 : {
     467               0 :   nsCAutoString src;
     468               0 :   GetCurrentSpec(src);
     469               0 :   aCurrentSrc = NS_ConvertUTF8toUTF16(src);
     470               0 :   return NS_OK;
     471                 : }
     472                 : 
     473                 : /* readonly attribute unsigned short networkState; */
     474               0 : NS_IMETHODIMP nsHTMLMediaElement::GetNetworkState(PRUint16 *aNetworkState)
     475                 : {
     476               0 :   *aNetworkState = mNetworkState;
     477                 : 
     478               0 :   return NS_OK;
     479                 : }
     480                 : 
     481                 : nsresult
     482               0 : nsHTMLMediaElement::OnChannelRedirect(nsIChannel *aChannel,
     483                 :                                       nsIChannel *aNewChannel,
     484                 :                                       PRUint32 aFlags)
     485                 : {
     486               0 :   NS_ASSERTION(aChannel == mChannel, "Channels should match!");
     487               0 :   mChannel = aNewChannel;
     488                 : 
     489                 :   // Handle forwarding of Range header so that the intial detection
     490                 :   // of seeking support (via result code 206) works across redirects.
     491               0 :   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
     492               0 :   NS_ENSURE_STATE(http);
     493                 : 
     494               0 :   NS_NAMED_LITERAL_CSTRING(rangeHdr, "Range");
     495                 :  
     496               0 :   nsCAutoString rangeVal;
     497               0 :   if (NS_SUCCEEDED(http->GetRequestHeader(rangeHdr, rangeVal))) {
     498               0 :     NS_ENSURE_STATE(!rangeVal.IsEmpty());
     499                 : 
     500               0 :     http = do_QueryInterface(aNewChannel);
     501               0 :     NS_ENSURE_STATE(http);
     502                 :  
     503               0 :     nsresult rv = http->SetRequestHeader(rangeHdr, rangeVal, false);
     504               0 :     NS_ENSURE_SUCCESS(rv, rv);
     505                 :   }
     506                 :  
     507               0 :   return NS_OK;
     508                 : }
     509                 : 
     510               0 : void nsHTMLMediaElement::AbortExistingLoads()
     511                 : {
     512                 :   // Abort any already-running instance of the resource selection algorithm.
     513               0 :   mLoadWaitStatus = NOT_WAITING;
     514                 : 
     515                 :   // Set a new load ID. This will cause events which were enqueued
     516                 :   // with a different load ID to silently be cancelled.
     517               0 :   mCurrentLoadID++;
     518                 : 
     519               0 :   bool fireTimeUpdate = false;
     520                 : 
     521               0 :   if (mDecoder) {
     522               0 :     RemoveMediaElementFromURITable();
     523               0 :     fireTimeUpdate = mDecoder->GetCurrentTime() != 0.0;
     524               0 :     mDecoder->Shutdown();
     525               0 :     mDecoder = nsnull;
     526                 :   }
     527               0 :   mLoadingSrc = nsnull;
     528                 : 
     529               0 :   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
     530                 :       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
     531                 :   {
     532               0 :     DispatchEvent(NS_LITERAL_STRING("abort"));
     533                 :   }
     534                 : 
     535               0 :   mError = nsnull;
     536               0 :   mLoadedFirstFrame = false;
     537               0 :   mAutoplaying = true;
     538               0 :   mIsLoadingFromSourceChildren = false;
     539               0 :   mSuspendedAfterFirstFrame = false;
     540               0 :   mAllowSuspendAfterFirstFrame = true;
     541               0 :   mHaveQueuedSelectResource = false;
     542               0 :   mLoadIsSuspended = false;
     543               0 :   mSourcePointer = nsnull;
     544                 : 
     545                 :   // TODO: The playback rate must be set to the default playback rate.
     546                 : 
     547               0 :   if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     548               0 :     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
     549               0 :     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
     550               0 :     mPaused = true;
     551                 : 
     552               0 :     if (fireTimeUpdate) {
     553                 :       // Since we destroyed the decoder above, the current playback position
     554                 :       // will now be reported as 0. The playback position was non-zero when
     555                 :       // we destroyed the decoder, so fire a timeupdate event so that the
     556                 :       // change will be reflected in the controls.
     557               0 :       FireTimeUpdate(false);
     558                 :     }
     559               0 :     DispatchEvent(NS_LITERAL_STRING("emptied"));
     560                 :   }
     561                 : 
     562                 :   // We may have changed mPaused, mAutoplaying, mNetworkState and other
     563                 :   // things which can affect AddRemoveSelfReference
     564               0 :   AddRemoveSelfReference();
     565                 : 
     566               0 :   mIsRunningSelectResource = false;
     567               0 : }
     568                 : 
     569               0 : void nsHTMLMediaElement::NoSupportedMediaSourceError()
     570                 : {
     571               0 :   NS_ASSERTION(mDelayingLoadEvent, "Load event not delayed during source selection?");
     572                 : 
     573               0 :   mError = new nsMediaError(nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
     574               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
     575               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("error"));
     576                 :   // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
     577               0 :   ChangeDelayLoadStatus(false);
     578               0 : }
     579                 : 
     580                 : typedef void (nsHTMLMediaElement::*SyncSectionFn)();
     581                 : 
     582                 : // Runs a "synchronous section", a function that must run once the event loop
     583                 : // has reached a "stable state". See:
     584                 : // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
     585                 : class nsSyncSection : public nsMediaEvent
     586               0 : {
     587                 : private:
     588                 :   SyncSectionFn mClosure;
     589                 : public:
     590               0 :   nsSyncSection(nsHTMLMediaElement* aElement,
     591                 :                 SyncSectionFn aClosure) :
     592                 :     nsMediaEvent(aElement),
     593               0 :     mClosure(aClosure)
     594                 :   {
     595               0 :   }
     596                 : 
     597               0 :   NS_IMETHOD Run() {
     598                 :     // Silently cancel if our load has been cancelled.
     599               0 :     if (IsCancelled())
     600               0 :       return NS_OK;
     601               0 :     (mElement.get()->*mClosure)();
     602               0 :     return NS_OK;
     603                 :   }
     604                 : };
     605                 : 
     606                 : static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
     607                 : 
     608                 : // Asynchronously awaits a stable state, whereupon aClosure runs on the main
     609                 : // thread. This adds an event which run aClosure to the appshell's list of 
     610                 : // sections synchronous the next time control returns to the event loop.
     611               0 : void AsyncAwaitStableState(nsHTMLMediaElement* aElement,
     612                 :                            SyncSectionFn aClosure)
     613                 : {
     614               0 :   nsCOMPtr<nsIRunnable> event = new nsSyncSection(aElement, aClosure);
     615               0 :   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
     616               0 :   appShell->RunInStableState(event);
     617               0 : }
     618                 : 
     619               0 : void nsHTMLMediaElement::QueueLoadFromSourceTask()
     620                 : {
     621               0 :   ChangeDelayLoadStatus(true);
     622               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
     623               0 :   AsyncAwaitStableState(this, &nsHTMLMediaElement::LoadFromSourceChildren);
     624               0 : }
     625                 : 
     626               0 : void nsHTMLMediaElement::QueueSelectResourceTask()
     627                 : {
     628                 :   // Don't allow multiple async select resource calls to be queued.
     629               0 :   if (mHaveQueuedSelectResource)
     630               0 :     return;
     631               0 :   mHaveQueuedSelectResource = true;
     632               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
     633               0 :   AsyncAwaitStableState(this, &nsHTMLMediaElement::SelectResourceWrapper);
     634                 : }
     635                 : 
     636                 : /* void load (); */
     637               0 : NS_IMETHODIMP nsHTMLMediaElement::Load()
     638                 : {
     639               0 :   if (mIsRunningLoadMethod)
     640               0 :     return NS_OK;
     641               0 :   SetPlayedOrSeeked(false);
     642               0 :   mIsRunningLoadMethod = true;
     643               0 :   AbortExistingLoads();
     644               0 :   QueueSelectResourceTask();
     645               0 :   mIsRunningLoadMethod = false;
     646               0 :   return NS_OK;
     647                 : }
     648                 : 
     649               0 : static bool HasSourceChildren(nsIContent *aElement)
     650                 : {
     651               0 :   for (nsIContent* child = aElement->GetFirstChild();
     652                 :        child;
     653               0 :        child = child->GetNextSibling()) {
     654               0 :     if (child->IsHTML(nsGkAtoms::source))
     655                 :     {
     656               0 :       return true;
     657                 :     }
     658                 :   }
     659               0 :   return false;
     660                 : }
     661                 : 
     662               0 : void nsHTMLMediaElement::SelectResourceWrapper()
     663                 : {
     664               0 :   SelectResource();
     665               0 :   mIsRunningSelectResource = false;
     666               0 :   mHaveQueuedSelectResource = false;
     667               0 : }
     668                 : 
     669               0 : void nsHTMLMediaElement::SelectResource()
     670                 : {
     671               0 :   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::src) && !HasSourceChildren(this)) {
     672                 :     // The media element has neither a src attribute nor any source
     673                 :     // element children, abort the load.
     674               0 :     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
     675                 :     // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
     676               0 :     ChangeDelayLoadStatus(false);
     677               0 :     return;
     678                 :   }
     679                 : 
     680               0 :   ChangeDelayLoadStatus(true);
     681                 : 
     682               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
     683                 :   // Load event was delayed, and still is, so no need to call
     684                 :   // AddRemoveSelfReference, since it must still be held
     685               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
     686                 : 
     687                 :   // Delay setting mIsRunningSeletResource until after UpdatePreloadAction
     688                 :   // so that we don't lose our state change by bailing out of the preload
     689                 :   // state update
     690               0 :   UpdatePreloadAction();
     691               0 :   mIsRunningSelectResource = true;
     692                 : 
     693                 :   // If we have a 'src' attribute, use that exclusively.
     694               0 :   nsAutoString src;
     695               0 :   if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
     696               0 :     nsCOMPtr<nsIURI> uri;
     697               0 :     nsresult rv = NewURIFromString(src, getter_AddRefs(uri));
     698               0 :     if (NS_SUCCEEDED(rv)) {
     699               0 :       LOG(PR_LOG_DEBUG, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
     700               0 :       NS_ASSERTION(!mIsLoadingFromSourceChildren,
     701                 :         "Should think we're not loading from source children by default");
     702               0 :       mLoadingSrc = uri;
     703               0 :       if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) {
     704                 :         // preload:none media, suspend the load here before we make any
     705                 :         // network requests.
     706               0 :         SuspendLoad();
     707                 :         return;
     708                 :       }
     709                 : 
     710               0 :       rv = LoadResource();
     711               0 :       if (NS_SUCCEEDED(rv)) {
     712                 :         return;
     713                 :       }
     714                 :     } else {
     715               0 :       const PRUnichar* params[] = { src.get() };
     716               0 :       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
     717                 :     }
     718               0 :     NoSupportedMediaSourceError();
     719                 :   } else {
     720                 :     // Otherwise, the source elements will be used.
     721               0 :     mIsLoadingFromSourceChildren = true;
     722               0 :     LoadFromSourceChildren();
     723                 :   }
     724                 : }
     725                 : 
     726               0 : void nsHTMLMediaElement::NotifyLoadError()
     727                 : {
     728               0 :   if (!mIsLoadingFromSourceChildren) {
     729               0 :     LOG(PR_LOG_DEBUG, ("NotifyLoadError(), no supported media error"));
     730               0 :     NoSupportedMediaSourceError();
     731               0 :   } else if (mSourceLoadCandidate) {
     732               0 :     DispatchAsyncSourceError(mSourceLoadCandidate);
     733               0 :     QueueLoadFromSourceTask();
     734                 :   } else {
     735               0 :     NS_WARNING("Should know the source we were loading from!");
     736                 :   }
     737               0 : }
     738                 : 
     739               0 : void nsHTMLMediaElement::NotifyAudioAvailable(float* aFrameBuffer,
     740                 :                                               PRUint32 aFrameBufferLength,
     741                 :                                               float aTime)
     742                 : {
     743                 :   // Auto manage the memory for the frame buffer, so that if we add an early
     744                 :   // return-on-error here in future, we won't forget to release the memory.
     745                 :   // Otherwise we hand ownership of the memory over to the event created by 
     746                 :   // DispatchAudioAvailableEvent().
     747               0 :   nsAutoArrayPtr<float> frameBuffer(aFrameBuffer);
     748                 :   // Do same-origin check on element and media before allowing MozAudioAvailable events.
     749               0 :   if (!mMediaSecurityVerified) {
     750               0 :     nsCOMPtr<nsIPrincipal> principal = GetCurrentPrincipal();
     751               0 :     nsresult rv = NodePrincipal()->Subsumes(principal, &mAllowAudioData);
     752               0 :     if (NS_FAILED(rv)) {
     753               0 :       mAllowAudioData = false;
     754                 :     }
     755                 :   }
     756                 : 
     757               0 :   DispatchAudioAvailableEvent(frameBuffer.forget(), aFrameBufferLength, aTime);
     758               0 : }
     759                 : 
     760               0 : void nsHTMLMediaElement::LoadFromSourceChildren()
     761                 : {
     762               0 :   NS_ASSERTION(mDelayingLoadEvent,
     763                 :                "Should delay load event (if in document) during load");
     764               0 :   NS_ASSERTION(mIsLoadingFromSourceChildren,
     765                 :                "Must remember we're loading from source children");
     766               0 :   while (true) {
     767               0 :     nsIContent* child = GetNextSource();
     768               0 :     if (!child) {
     769                 :       // Exhausted candidates, wait for more candidates to be appended to
     770                 :       // the media element.
     771               0 :       mLoadWaitStatus = WAITING_FOR_SOURCE;
     772               0 :       mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
     773               0 :       ChangeDelayLoadStatus(false);
     774               0 :       ReportLoadError("MediaLoadExhaustedCandidates");
     775               0 :       return;
     776                 :     }
     777                 : 
     778                 :     // Must have src attribute.
     779               0 :     nsAutoString src;
     780               0 :     if (!child->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
     781               0 :       ReportLoadError("MediaLoadSourceMissingSrc");
     782               0 :       DispatchAsyncSourceError(child);
     783               0 :       continue;
     784                 :     }
     785                 : 
     786                 :     // If we have a type attribute, it must be a supported type.
     787               0 :     nsAutoString type;
     788               0 :     if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
     789               0 :         GetCanPlay(type) == CANPLAY_NO) {
     790               0 :       DispatchAsyncSourceError(child);
     791               0 :       const PRUnichar* params[] = { type.get(), src.get() };
     792               0 :       ReportLoadError("MediaLoadUnsupportedType", params, ArrayLength(params));
     793               0 :       continue;
     794                 :     }
     795               0 :     LOG(PR_LOG_DEBUG, ("%p Trying load from <source>=%s type=%s", this,
     796                 :       NS_ConvertUTF16toUTF8(src).get(), NS_ConvertUTF16toUTF8(type).get()));
     797                 : 
     798               0 :     nsCOMPtr<nsIURI> uri;
     799               0 :     NewURIFromString(src, getter_AddRefs(uri));
     800               0 :     if (!uri) {
     801               0 :       DispatchAsyncSourceError(child);
     802               0 :       const PRUnichar* params[] = { src.get() };
     803               0 :       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
     804               0 :       continue;
     805                 :     }
     806                 : 
     807               0 :     mLoadingSrc = uri;
     808               0 :     NS_ASSERTION(mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING,
     809                 :                  "Network state should be loading");
     810                 : 
     811               0 :     if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) {
     812                 :       // preload:none media, suspend the load here before we make any
     813                 :       // network requests.
     814               0 :       SuspendLoad();
     815                 :       return;
     816                 :     }
     817                 : 
     818               0 :     if (NS_SUCCEEDED(LoadResource())) {
     819                 :       return;
     820                 :     }
     821                 : 
     822                 :     // If we fail to load, loop back and try loading the next resource.
     823               0 :     DispatchAsyncSourceError(child);
     824                 :   }
     825                 :   NS_NOTREACHED("Execution should not reach here!");
     826                 : }
     827                 : 
     828               0 : void nsHTMLMediaElement::SuspendLoad()
     829                 : {
     830               0 :   mLoadIsSuspended = true;
     831               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
     832               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
     833               0 :   ChangeDelayLoadStatus(false);
     834               0 : }
     835                 : 
     836               0 : void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction)
     837                 : {
     838               0 :   NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one");
     839               0 :   mLoadIsSuspended = false;
     840               0 :   mPreloadAction = aAction;
     841               0 :   ChangeDelayLoadStatus(true);
     842               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
     843               0 :   if (!mIsLoadingFromSourceChildren) {
     844                 :     // We were loading from the element's src attribute.
     845               0 :     if (NS_FAILED(LoadResource())) {
     846               0 :       NoSupportedMediaSourceError();
     847                 :     }
     848                 :   } else {
     849                 :     // We were loading from a child <source> element. Try to resume the
     850                 :     // load of that child, and if that fails, try the next child.
     851               0 :     if (NS_FAILED(LoadResource())) {
     852               0 :       LoadFromSourceChildren();
     853                 :     }
     854                 :   }
     855               0 : }
     856                 : 
     857               0 : static bool IsAutoplayEnabled()
     858                 : {
     859               0 :   return Preferences::GetBool("media.autoplay.enabled");
     860                 : }
     861                 : 
     862               0 : void nsHTMLMediaElement::UpdatePreloadAction()
     863                 : {
     864               0 :   PreloadAction nextAction = PRELOAD_UNDEFINED;
     865                 :   // If autoplay is set, or we're playing, we should always preload data,
     866                 :   // as we'll need it to play.
     867               0 :   if ((IsAutoplayEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) ||
     868               0 :       !mPaused)
     869                 :   {
     870               0 :     nextAction = nsHTMLMediaElement::PRELOAD_ENOUGH;
     871                 :   } else {
     872                 :     // Find the appropriate preload action by looking at the attribute.
     873                 :     const nsAttrValue* val = mAttrsAndChildren.GetAttr(nsGkAtoms::preload,
     874               0 :                                                        kNameSpaceID_None);
     875                 :     PRUint32 preloadDefault =
     876                 :       Preferences::GetInt("media.preload.default",
     877               0 :                           nsHTMLMediaElement::PRELOAD_ATTR_METADATA);
     878                 :     PRUint32 preloadAuto =
     879                 :       Preferences::GetInt("media.preload.auto",
     880               0 :                           nsHTMLMediaElement::PRELOAD_ENOUGH);
     881               0 :     if (!val) {
     882                 :       // Attribute is not set. Use the preload action specified by the 
     883                 :       // media.preload.default pref, or just preload metadata if not present.
     884               0 :       nextAction = static_cast<PreloadAction>(preloadDefault);
     885               0 :     } else if (val->Type() == nsAttrValue::eEnum) {
     886               0 :       PreloadAttrValue attr = static_cast<PreloadAttrValue>(val->GetEnumValue());
     887               0 :       if (attr == nsHTMLMediaElement::PRELOAD_ATTR_EMPTY ||
     888                 :           attr == nsHTMLMediaElement::PRELOAD_ATTR_AUTO)
     889                 :       {
     890               0 :         nextAction = static_cast<PreloadAction>(preloadAuto);
     891               0 :       } else if (attr == nsHTMLMediaElement::PRELOAD_ATTR_METADATA) {
     892               0 :         nextAction = nsHTMLMediaElement::PRELOAD_METADATA;
     893               0 :       } else if (attr == nsHTMLMediaElement::PRELOAD_ATTR_NONE) {
     894               0 :         nextAction = nsHTMLMediaElement::PRELOAD_NONE;
     895                 :       }
     896                 :     } else {
     897                 :       // Use the suggested "missing value default" of "metadata", or the value
     898                 :       // specified by the media.preload.default, if present.
     899               0 :       nextAction = static_cast<PreloadAction>(preloadDefault);
     900                 :     }
     901                 :   }
     902                 : 
     903               0 :   if ((mBegun || mIsRunningSelectResource) && nextAction < mPreloadAction) {
     904                 :     // We've started a load or are already downloading, and the preload was
     905                 :     // changed to a state where we buffer less. We don't support this case,
     906                 :     // so don't change the preload behaviour.
     907               0 :     return;
     908                 :   }
     909                 : 
     910               0 :   mPreloadAction = nextAction;
     911               0 :   if (nextAction == nsHTMLMediaElement::PRELOAD_ENOUGH) {
     912               0 :     if (mLoadIsSuspended) {
     913                 :       // Our load was previouly suspended due to the media having preload
     914                 :       // value "none". The preload value has changed to preload:auto, so
     915                 :       // resume the load.
     916               0 :       ResumeLoad(PRELOAD_ENOUGH);
     917                 :     } else {
     918                 :       // Preload as much of the video as we can, i.e. don't suspend after
     919                 :       // the first frame.
     920               0 :       StopSuspendingAfterFirstFrame();
     921                 :     }
     922                 : 
     923               0 :   } else if (nextAction == nsHTMLMediaElement::PRELOAD_METADATA) {
     924                 :     // Ensure that the video can be suspended after first frame.
     925               0 :     mAllowSuspendAfterFirstFrame = true;
     926               0 :     if (mLoadIsSuspended) {
     927                 :       // Our load was previouly suspended due to the media having preload
     928                 :       // value "none". The preload value has changed to preload:metadata, so
     929                 :       // resume the load. We'll pause the load again after we've read the
     930                 :       // metadata.
     931               0 :       ResumeLoad(PRELOAD_METADATA);
     932                 :     }
     933                 :   }
     934                 : }
     935                 : 
     936               0 : nsresult nsHTMLMediaElement::LoadResource()
     937                 : {
     938               0 :   NS_ASSERTION(mDelayingLoadEvent,
     939                 :                "Should delay load event (if in document) during load");
     940                 : 
     941                 :   // If a previous call to mozSetup() was made, kill that media resource
     942                 :   // in order to use this new src instead.
     943               0 :   if (mAudioStream) {
     944               0 :     mAudioStream->Shutdown();
     945               0 :     mAudioStream = nsnull;
     946                 :   }
     947                 : 
     948               0 :   if (mChannel) {
     949               0 :     mChannel->Cancel(NS_BINDING_ABORTED);
     950               0 :     mChannel = nsnull;
     951                 :   }
     952                 : 
     953                 :   // Set the media element's CORS mode only when loading a resource
     954               0 :   mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
     955                 : 
     956               0 :   nsHTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
     957               0 :   if (other) {
     958                 :     // Clone it.
     959               0 :     nsresult rv = InitializeDecoderAsClone(other->mDecoder);
     960               0 :     if (NS_SUCCEEDED(rv))
     961               0 :       return rv;
     962                 :   }
     963                 : 
     964               0 :   PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
     965                 :   nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_MEDIA,
     966                 :                                           mLoadingSrc,
     967                 :                                           NodePrincipal(),
     968                 :                                           static_cast<nsGenericElement*>(this),
     969               0 :                                           EmptyCString(), // mime type
     970                 :                                           nsnull, // extra
     971                 :                                           &shouldLoad,
     972                 :                                           nsContentUtils::GetContentPolicy(),
     973               0 :                                           nsContentUtils::GetSecurityManager());
     974               0 :   NS_ENSURE_SUCCESS(rv, rv);
     975               0 :   if (NS_CP_REJECTED(shouldLoad)) {
     976               0 :     return NS_ERROR_FAILURE;
     977                 :   }
     978                 : 
     979               0 :   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
     980                 : 
     981                 :   // check for a Content Security Policy to pass down to the channel
     982                 :   // created to load the media content
     983               0 :   nsCOMPtr<nsIChannelPolicy> channelPolicy;
     984               0 :   nsCOMPtr<nsIContentSecurityPolicy> csp;
     985               0 :   rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
     986               0 :   NS_ENSURE_SUCCESS(rv,rv);
     987               0 :   if (csp) {
     988               0 :     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
     989               0 :     channelPolicy->SetContentSecurityPolicy(csp);
     990               0 :     channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
     991                 :   }
     992               0 :   nsCOMPtr<nsIChannel> channel;
     993               0 :   rv = NS_NewChannel(getter_AddRefs(channel),
     994                 :                      mLoadingSrc,
     995                 :                      nsnull,
     996                 :                      loadGroup,
     997                 :                      nsnull,
     998                 :                      nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
     999               0 :                      channelPolicy);
    1000               0 :   NS_ENSURE_SUCCESS(rv,rv);
    1001                 : 
    1002                 :   // The listener holds a strong reference to us.  This creates a
    1003                 :   // reference cycle, once we've set mChannel, which is manually broken
    1004                 :   // in the listener's OnStartRequest method after it is finished with
    1005                 :   // the element. The cycle will also be broken if we get a shutdown
    1006                 :   // notification before OnStartRequest fires.  Necko guarantees that
    1007                 :   // OnStartRequest will eventually fire if we don't shut down first.
    1008               0 :   nsRefPtr<MediaLoadListener> loadListener = new MediaLoadListener(this);
    1009                 : 
    1010               0 :   channel->SetNotificationCallbacks(loadListener);
    1011                 : 
    1012               0 :   nsCOMPtr<nsIStreamListener> listener;
    1013               0 :   if (ShouldCheckAllowOrigin()) {
    1014                 :     listener =
    1015                 :       new nsCORSListenerProxy(loadListener,
    1016               0 :                               NodePrincipal(),
    1017                 :                               channel,
    1018               0 :                               GetCORSMode() == CORS_USE_CREDENTIALS,
    1019               0 :                               &rv);
    1020                 :   } else {
    1021               0 :     rv = nsContentUtils::GetSecurityManager()->
    1022                 :            CheckLoadURIWithPrincipal(NodePrincipal(),
    1023                 :                                      mLoadingSrc,
    1024               0 :                                      nsIScriptSecurityManager::STANDARD);
    1025               0 :     listener = loadListener;
    1026                 :   }
    1027               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1028                 : 
    1029               0 :   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
    1030               0 :   if (hc) {
    1031                 :     // Use a byte range request from the start of the resource.
    1032                 :     // This enables us to detect if the stream supports byte range
    1033                 :     // requests, and therefore seeking, early.
    1034               0 :     hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
    1035               0 :                          NS_LITERAL_CSTRING("bytes=0-"),
    1036               0 :                          false);
    1037                 : 
    1038               0 :     SetRequestHeaders(hc);
    1039                 :   }
    1040                 : 
    1041               0 :   rv = channel->AsyncOpen(listener, nsnull);
    1042               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1043                 : 
    1044                 :   // Else the channel must be open and starting to download. If it encounters
    1045                 :   // a non-catastrophic failure, it will set a new task to continue loading
    1046                 :   // another candidate.  It's safe to set it as mChannel now.
    1047               0 :   mChannel = channel;
    1048                 : 
    1049                 :   // loadListener will be unregistered either on shutdown or when
    1050                 :   // OnStartRequest for the channel we just opened fires.
    1051               0 :   nsContentUtils::RegisterShutdownObserver(loadListener);
    1052               0 :   return NS_OK;
    1053                 : }
    1054                 : 
    1055               0 : nsresult nsHTMLMediaElement::LoadWithChannel(nsIChannel *aChannel,
    1056                 :                                              nsIStreamListener **aListener)
    1057                 : {
    1058               0 :   NS_ENSURE_ARG_POINTER(aChannel);
    1059               0 :   NS_ENSURE_ARG_POINTER(aListener);
    1060                 : 
    1061               0 :   *aListener = nsnull;
    1062                 : 
    1063               0 :   AbortExistingLoads();
    1064                 : 
    1065               0 :   nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(mLoadingSrc));
    1066               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1067                 : 
    1068               0 :   ChangeDelayLoadStatus(true);
    1069               0 :   rv = InitializeDecoderForChannel(aChannel, aListener);
    1070               0 :   if (NS_FAILED(rv)) {
    1071               0 :     ChangeDelayLoadStatus(false);
    1072               0 :     return rv;
    1073                 :   }
    1074                 : 
    1075               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
    1076                 : 
    1077               0 :   return NS_OK;
    1078                 : }
    1079                 : 
    1080               0 : NS_IMETHODIMP nsHTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther)
    1081                 : {
    1082               0 :   NS_ENSURE_ARG_POINTER(aOther);
    1083                 : 
    1084               0 :   AbortExistingLoads();
    1085                 : 
    1086               0 :   nsCOMPtr<nsIContent> content = do_QueryInterface(aOther);
    1087               0 :   nsHTMLMediaElement* other = static_cast<nsHTMLMediaElement*>(content.get());
    1088               0 :   if (!other || !other->mDecoder)
    1089               0 :     return NS_OK;
    1090                 : 
    1091               0 :   ChangeDelayLoadStatus(true);
    1092                 : 
    1093               0 :   mLoadingSrc = other->mLoadingSrc;
    1094               0 :   nsresult rv = InitializeDecoderAsClone(other->mDecoder);
    1095               0 :   if (NS_FAILED(rv)) {
    1096               0 :     ChangeDelayLoadStatus(false);
    1097               0 :     return rv;
    1098                 :   }
    1099                 : 
    1100               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
    1101                 : 
    1102               0 :   return NS_OK;
    1103                 : }
    1104                 : 
    1105                 : /* readonly attribute unsigned short readyState; */
    1106               0 : NS_IMETHODIMP nsHTMLMediaElement::GetReadyState(PRUint16 *aReadyState)
    1107                 : {
    1108               0 :   *aReadyState = mReadyState;
    1109                 : 
    1110               0 :   return NS_OK;
    1111                 : }
    1112                 : 
    1113                 : /* readonly attribute boolean seeking; */
    1114               0 : NS_IMETHODIMP nsHTMLMediaElement::GetSeeking(bool *aSeeking)
    1115                 : {
    1116               0 :   *aSeeking = mDecoder && mDecoder->IsSeeking();
    1117                 : 
    1118               0 :   return NS_OK;
    1119                 : }
    1120                 : 
    1121                 : /* attribute double currentTime; */
    1122               0 : NS_IMETHODIMP nsHTMLMediaElement::GetCurrentTime(double *aCurrentTime)
    1123                 : {
    1124               0 :   *aCurrentTime = mDecoder ? mDecoder->GetCurrentTime() : 0.0;
    1125               0 :   return NS_OK;
    1126                 : }
    1127                 : 
    1128               0 : NS_IMETHODIMP nsHTMLMediaElement::SetCurrentTime(double aCurrentTime)
    1129                 : {
    1130               0 :   StopSuspendingAfterFirstFrame();
    1131                 : 
    1132               0 :   if (!mDecoder) {
    1133               0 :     LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: no decoder", this, aCurrentTime));
    1134               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1135                 :   }
    1136                 : 
    1137               0 :   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
    1138               0 :     LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: no source", this, aCurrentTime));
    1139               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1140                 :   }
    1141                 : 
    1142                 :   // Detect for a NaN and invalid values.
    1143               0 :   if (aCurrentTime != aCurrentTime) {
    1144               0 :     LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: bad time", this, aCurrentTime));
    1145               0 :     return NS_ERROR_FAILURE;
    1146                 :   }
    1147                 : 
    1148                 :   // Clamp the time to [0, duration] as required by the spec
    1149               0 :   double clampedTime = NS_MAX(0.0, aCurrentTime);
    1150               0 :   double duration = mDecoder->GetDuration();
    1151               0 :   if (duration >= 0) {
    1152               0 :     clampedTime = NS_MIN(clampedTime, duration);
    1153                 :   }
    1154                 : 
    1155               0 :   mPlayingBeforeSeek = IsPotentiallyPlaying();
    1156                 :   // The media backend is responsible for dispatching the timeupdate
    1157                 :   // event if it changes the playback position as a result of the seek.
    1158               0 :   LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) starting seek", this, aCurrentTime));
    1159               0 :   nsresult rv = mDecoder->Seek(clampedTime);
    1160                 : 
    1161                 :   // We changed whether we're seeking so we need to AddRemoveSelfReference
    1162               0 :   AddRemoveSelfReference();
    1163                 : 
    1164               0 :   return rv;
    1165                 : }
    1166                 : 
    1167                 : /* readonly attribute double duration; */
    1168               0 : NS_IMETHODIMP nsHTMLMediaElement::GetDuration(double *aDuration)
    1169                 : {
    1170               0 :   *aDuration = mDecoder ? mDecoder->GetDuration() : std::numeric_limits<double>::quiet_NaN();
    1171               0 :   return NS_OK;
    1172                 : }
    1173                 : 
    1174                 : /* readonly attribute nsIDOMHTMLTimeRanges seekable; */
    1175               0 : NS_IMETHODIMP nsHTMLMediaElement::GetSeekable(nsIDOMTimeRanges** aSeekable)
    1176                 : {
    1177               0 :   nsRefPtr<nsTimeRanges> ranges = new nsTimeRanges();
    1178               0 :   if (mDecoder && mReadyState > nsIDOMHTMLMediaElement::HAVE_NOTHING) {
    1179               0 :     mDecoder->GetSeekable(ranges);
    1180                 :   }
    1181               0 :   ranges.forget(aSeekable);
    1182               0 :   return NS_OK;
    1183                 : }
    1184                 : 
    1185                 : 
    1186                 : /* readonly attribute boolean paused; */
    1187               0 : NS_IMETHODIMP nsHTMLMediaElement::GetPaused(bool *aPaused)
    1188                 : {
    1189               0 :   *aPaused = mPaused;
    1190                 : 
    1191               0 :   return NS_OK;
    1192                 : }
    1193                 : 
    1194                 : /* void pause (); */
    1195               0 : NS_IMETHODIMP nsHTMLMediaElement::Pause()
    1196                 : {
    1197               0 :   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
    1198               0 :     LOG(PR_LOG_DEBUG, ("Loading due to Pause()"));
    1199               0 :     nsresult rv = Load();
    1200               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1201               0 :   } else if (mDecoder) {
    1202               0 :     mDecoder->Pause();
    1203                 :   }
    1204                 : 
    1205               0 :   bool oldPaused = mPaused;
    1206               0 :   mPaused = true;
    1207               0 :   mAutoplaying = false;
    1208                 :   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
    1209               0 :   AddRemoveSelfReference();
    1210                 : 
    1211               0 :   if (!oldPaused) {
    1212               0 :     FireTimeUpdate(false);
    1213               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
    1214                 :   }
    1215                 : 
    1216               0 :   return NS_OK;
    1217                 : }
    1218                 : 
    1219                 : /* attribute double volume; */
    1220               0 : NS_IMETHODIMP nsHTMLMediaElement::GetVolume(double *aVolume)
    1221                 : {
    1222               0 :   *aVolume = mVolume;
    1223                 : 
    1224               0 :   return NS_OK;
    1225                 : }
    1226                 : 
    1227               0 : NS_IMETHODIMP nsHTMLMediaElement::SetVolume(double aVolume)
    1228                 : {
    1229               0 :   if (aVolume < 0.0 || aVolume > 1.0)
    1230               0 :     return NS_ERROR_DOM_INDEX_SIZE_ERR;
    1231                 : 
    1232               0 :   if (aVolume == mVolume)
    1233               0 :     return NS_OK;
    1234                 : 
    1235               0 :   mVolume = aVolume;
    1236                 : 
    1237               0 :   if (mDecoder && !mMuted) {
    1238               0 :     mDecoder->SetVolume(mVolume);
    1239               0 :   } else if (mAudioStream && !mMuted) {
    1240               0 :     mAudioStream->SetVolume(mVolume);
    1241                 :   }
    1242                 : 
    1243               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
    1244                 : 
    1245               0 :   return NS_OK;
    1246                 : }
    1247                 : 
    1248                 : NS_IMETHODIMP
    1249               0 : nsHTMLMediaElement::GetMozChannels(PRUint32 *aMozChannels)
    1250                 : {
    1251               0 :   if (!mDecoder && !mAudioStream) {
    1252               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1253                 :   }
    1254                 : 
    1255               0 :   *aMozChannels = mChannels;
    1256               0 :   return NS_OK;
    1257                 : }
    1258                 : 
    1259                 : NS_IMETHODIMP
    1260               0 : nsHTMLMediaElement::GetMozSampleRate(PRUint32 *aMozSampleRate)
    1261                 : {
    1262               0 :   if (!mDecoder && !mAudioStream) {
    1263               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1264                 :   }
    1265                 : 
    1266               0 :   *aMozSampleRate = mRate;
    1267               0 :   return NS_OK;
    1268                 : }
    1269                 : 
    1270                 : NS_IMETHODIMP
    1271               0 : nsHTMLMediaElement::GetMozFrameBufferLength(PRUint32 *aMozFrameBufferLength)
    1272                 : {
    1273                 :   // The framebuffer (via MozAudioAvailable events) is only available
    1274                 :   // when reading vs. writing audio directly.
    1275               0 :   if (!mDecoder) {
    1276               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1277                 :   }
    1278                 : 
    1279               0 :   *aMozFrameBufferLength = mDecoder->GetFrameBufferLength();
    1280               0 :   return NS_OK;
    1281                 : }
    1282                 : 
    1283                 : NS_IMETHODIMP
    1284               0 : nsHTMLMediaElement::SetMozFrameBufferLength(PRUint32 aMozFrameBufferLength)
    1285                 : {
    1286               0 :   if (!mDecoder)
    1287               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1288                 : 
    1289               0 :   return mDecoder->RequestFrameBufferLength(aMozFrameBufferLength);
    1290                 : }
    1291                 : 
    1292                 : /* attribute boolean muted; */
    1293               0 : NS_IMETHODIMP nsHTMLMediaElement::GetMuted(bool *aMuted)
    1294                 : {
    1295               0 :   *aMuted = mMuted;
    1296                 : 
    1297               0 :   return NS_OK;
    1298                 : }
    1299                 : 
    1300               0 : NS_IMETHODIMP nsHTMLMediaElement::SetMuted(bool aMuted)
    1301                 : {
    1302               0 :   if (aMuted == mMuted)
    1303               0 :     return NS_OK;
    1304                 : 
    1305               0 :   mMuted = aMuted;
    1306                 : 
    1307               0 :   if (mDecoder) {
    1308               0 :     mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
    1309               0 :   } else if (mAudioStream) {
    1310               0 :     mAudioStream->SetVolume(mMuted ? 0.0 : mVolume);
    1311                 :   }
    1312                 : 
    1313               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
    1314                 : 
    1315               0 :   return NS_OK;
    1316                 : }
    1317                 : 
    1318               0 : class MediaElementSetForURI : public nsURIHashKey {
    1319                 : public:
    1320               0 :   MediaElementSetForURI(const nsIURI* aKey) : nsURIHashKey(aKey) {}
    1321                 :   MediaElementSetForURI(const MediaElementSetForURI& toCopy)
    1322                 :     : nsURIHashKey(toCopy), mElements(toCopy.mElements) {}
    1323                 :   nsTArray<nsHTMLMediaElement*> mElements;
    1324                 : };
    1325                 : 
    1326                 : typedef nsTHashtable<MediaElementSetForURI> MediaElementURITable;
    1327                 : // Elements in this table must have non-null mDecoder and mLoadingSrc, and those
    1328                 : // can't change while the element is in the table. The table is keyed by
    1329                 : // the element's mLoadingSrc. Each entry has a list of all elements with the
    1330                 : // same mLoadingSrc.
    1331                 : static MediaElementURITable* gElementTable;
    1332                 : 
    1333                 : #ifdef DEBUG
    1334                 : // Returns the number of times aElement appears in the media element table
    1335                 : // for aURI. If this returns other than 0 or 1, there's a bug somewhere!
    1336                 : static unsigned
    1337               0 : MediaElementTableCount(nsHTMLMediaElement* aElement, nsIURI* aURI)
    1338                 : {
    1339               0 :   if (!gElementTable || !aElement || !aURI) {
    1340               0 :     return 0;
    1341                 :   }
    1342               0 :   MediaElementSetForURI* entry = gElementTable->GetEntry(aURI);
    1343               0 :   if (!entry) {
    1344               0 :     return 0;
    1345                 :   }
    1346               0 :   PRUint32 count = 0;
    1347               0 :   for (PRUint32 i = 0; i < entry->mElements.Length(); ++i) {
    1348               0 :     nsHTMLMediaElement* elem = entry->mElements[i];
    1349               0 :     if (elem == aElement) {
    1350               0 :       count++;
    1351                 :     }
    1352                 :   }
    1353               0 :   return count;
    1354                 : }
    1355                 : #endif
    1356                 : 
    1357                 : void
    1358               0 : nsHTMLMediaElement::AddMediaElementToURITable()
    1359                 : {
    1360               0 :   NS_ASSERTION(mDecoder && mDecoder->GetResource(), "Call this only with decoder Load called");
    1361               0 :   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
    1362                 :     "Should not have entry for element in element table before addition");
    1363               0 :   if (!gElementTable) {
    1364               0 :     gElementTable = new MediaElementURITable();
    1365               0 :     gElementTable->Init();
    1366                 :   }
    1367               0 :   MediaElementSetForURI* entry = gElementTable->PutEntry(mLoadingSrc);
    1368               0 :   entry->mElements.AppendElement(this);
    1369               0 :   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 1,
    1370                 :     "Should have a single entry for element in element table after addition");
    1371               0 : }
    1372                 : 
    1373                 : void
    1374               0 : nsHTMLMediaElement::RemoveMediaElementFromURITable()
    1375                 : {
    1376               0 :   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 1,
    1377                 :     "Before remove, should have a single entry for element in element table");
    1378               0 :   NS_ASSERTION(mDecoder, "Don't call this without decoder!");
    1379               0 :   NS_ASSERTION(mLoadingSrc, "Can't have decoder without source!");
    1380               0 :   if (!gElementTable)
    1381               0 :     return;
    1382               0 :   MediaElementSetForURI* entry = gElementTable->GetEntry(mLoadingSrc);
    1383               0 :   if (!entry)
    1384               0 :     return;
    1385               0 :   entry->mElements.RemoveElement(this);
    1386               0 :   if (entry->mElements.IsEmpty()) {
    1387               0 :     gElementTable->RemoveEntry(mLoadingSrc);
    1388               0 :     if (gElementTable->Count() == 0) {
    1389               0 :       delete gElementTable;
    1390               0 :       gElementTable = nsnull;
    1391                 :     }
    1392                 :   }
    1393               0 :   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
    1394                 :     "After remove, should no longer have an entry in element table");
    1395                 : }
    1396                 : 
    1397                 : nsHTMLMediaElement*
    1398               0 : nsHTMLMediaElement::LookupMediaElementURITable(nsIURI* aURI)
    1399                 : {
    1400               0 :   if (!gElementTable)
    1401               0 :     return nsnull;
    1402               0 :   MediaElementSetForURI* entry = gElementTable->GetEntry(aURI);
    1403               0 :   if (!entry)
    1404               0 :     return nsnull;
    1405               0 :   for (PRUint32 i = 0; i < entry->mElements.Length(); ++i) {
    1406               0 :     nsHTMLMediaElement* elem = entry->mElements[i];
    1407                 :     bool equal;
    1408                 :     // Look for elements that have the same principal and CORS mode.
    1409                 :     // Ditto for anything else that could cause us to send different headers.
    1410               0 :     if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) && equal &&
    1411                 :         elem->mCORSMode == mCORSMode) {
    1412               0 :       NS_ASSERTION(elem->mDecoder && elem->mDecoder->GetResource(), "Decoder gone");
    1413               0 :       MediaResource* resource = elem->mDecoder->GetResource();
    1414               0 :       if (resource->CanClone()) {
    1415               0 :         return elem;
    1416                 :       }
    1417                 :     }
    1418                 :   }
    1419               0 :   return nsnull;
    1420                 : }
    1421                 : 
    1422               0 : nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
    1423                 :   : nsGenericHTMLElement(aNodeInfo),
    1424                 :     mCurrentLoadID(0),
    1425                 :     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
    1426                 :     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
    1427                 :     mLoadWaitStatus(NOT_WAITING),
    1428                 :     mVolume(1.0),
    1429                 :     mChannels(0),
    1430                 :     mRate(0),
    1431                 :     mPreloadAction(PRELOAD_UNDEFINED),
    1432                 :     mMediaSize(-1,-1),
    1433                 :     mLastCurrentTime(0.0),
    1434                 :     mFragmentStart(-1.0),
    1435                 :     mFragmentEnd(-1.0),
    1436                 :     mAllowAudioData(false),
    1437                 :     mBegun(false),
    1438                 :     mLoadedFirstFrame(false),
    1439                 :     mAutoplaying(true),
    1440                 :     mAutoplayEnabled(true),
    1441                 :     mPaused(true),
    1442                 :     mMuted(false),
    1443                 :     mPlayingBeforeSeek(false),
    1444                 :     mPausedForInactiveDocument(false),
    1445                 :     mWaitingFired(false),
    1446                 :     mIsRunningLoadMethod(false),
    1447                 :     mIsLoadingFromSourceChildren(false),
    1448                 :     mDelayingLoadEvent(false),
    1449                 :     mIsRunningSelectResource(false),
    1450                 :     mHaveQueuedSelectResource(false),
    1451                 :     mSuspendedAfterFirstFrame(false),
    1452                 :     mAllowSuspendAfterFirstFrame(true),
    1453                 :     mHasPlayedOrSeeked(false),
    1454                 :     mHasSelfReference(false),
    1455                 :     mShuttingDown(false),
    1456                 :     mLoadIsSuspended(false),
    1457                 :     mMediaSecurityVerified(false),
    1458               0 :     mCORSMode(CORS_NONE)
    1459                 : {
    1460                 : #ifdef PR_LOGGING
    1461               0 :   if (!gMediaElementLog) {
    1462               0 :     gMediaElementLog = PR_NewLogModule("nsMediaElement");
    1463                 :   }
    1464               0 :   if (!gMediaElementEventsLog) {
    1465               0 :     gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
    1466                 :   }
    1467                 : #endif
    1468                 : 
    1469               0 :   RegisterFreezableElement();
    1470               0 :   NotifyOwnerDocumentActivityChanged();
    1471               0 : }
    1472                 : 
    1473               0 : nsHTMLMediaElement::~nsHTMLMediaElement()
    1474                 : {
    1475               0 :   NS_ASSERTION(!mHasSelfReference,
    1476                 :                "How can we be destroyed if we're still holding a self reference?");
    1477                 : 
    1478               0 :   if (mVideoFrameContainer) {
    1479               0 :     mVideoFrameContainer->ForgetElement();
    1480                 :   }
    1481               0 :   UnregisterFreezableElement();
    1482               0 :   if (mDecoder) {
    1483               0 :     RemoveMediaElementFromURITable();
    1484               0 :     mDecoder->Shutdown();
    1485                 :   }
    1486                 : 
    1487               0 :   NS_ASSERTION(MediaElementTableCount(this, mLoadingSrc) == 0,
    1488                 :     "Destroyed media element should no longer be in element table");
    1489                 : 
    1490               0 :   if (mChannel) {
    1491               0 :     mChannel->Cancel(NS_BINDING_ABORTED);
    1492                 :   }
    1493               0 :   if (mAudioStream) {
    1494               0 :     mAudioStream->Shutdown();
    1495                 :   }
    1496               0 : }
    1497                 : 
    1498               0 : void nsHTMLMediaElement::StopSuspendingAfterFirstFrame()
    1499                 : {
    1500               0 :   mAllowSuspendAfterFirstFrame = false;
    1501               0 :   if (!mSuspendedAfterFirstFrame)
    1502               0 :     return;
    1503               0 :   mSuspendedAfterFirstFrame = false;
    1504               0 :   if (mDecoder) {
    1505               0 :     mDecoder->Resume(true);
    1506                 :   }
    1507                 : }
    1508                 : 
    1509               0 : void nsHTMLMediaElement::SetPlayedOrSeeked(bool aValue)
    1510                 : {
    1511               0 :   if (aValue == mHasPlayedOrSeeked) {
    1512               0 :     return;
    1513                 :   }
    1514                 : 
    1515               0 :   mHasPlayedOrSeeked = aValue;
    1516                 : 
    1517                 :   // Force a reflow so that the poster frame hides or shows immediately.
    1518               0 :   nsIFrame* frame = GetPrimaryFrame();
    1519               0 :   if (!frame) {
    1520               0 :     return;
    1521                 :   }
    1522               0 :   frame->PresContext()->PresShell()->FrameNeedsReflow(frame,
    1523                 :                                                       nsIPresShell::eTreeChange,
    1524               0 :                                                       NS_FRAME_IS_DIRTY);
    1525                 : }
    1526                 : 
    1527               0 : NS_IMETHODIMP nsHTMLMediaElement::Play()
    1528                 : {
    1529               0 :   StopSuspendingAfterFirstFrame();
    1530               0 :   SetPlayedOrSeeked(true);
    1531                 : 
    1532               0 :   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
    1533               0 :     nsresult rv = Load();
    1534               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1535                 :   }
    1536               0 :   if (mLoadIsSuspended) {
    1537               0 :     ResumeLoad(PRELOAD_ENOUGH);
    1538                 :   }
    1539                 :   // Even if we just did Load() or ResumeLoad(), we could already have a decoder
    1540                 :   // here if we managed to clone an existing decoder.
    1541               0 :   if (mDecoder) {
    1542               0 :     if (mDecoder->IsEnded()) {
    1543               0 :       SetCurrentTime(0);
    1544                 :     }
    1545               0 :     if (!mPausedForInactiveDocument) {
    1546               0 :       nsresult rv = mDecoder->Play();
    1547               0 :       NS_ENSURE_SUCCESS(rv, rv);
    1548                 :     }
    1549                 :   }
    1550                 : 
    1551                 :   // TODO: If the playback has ended, then the user agent must set
    1552                 :   // seek to the effective start.
    1553                 :   // TODO: The playback rate must be set to the default playback rate.
    1554               0 :   if (mPaused) {
    1555               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
    1556               0 :     switch (mReadyState) {
    1557                 :     case nsIDOMHTMLMediaElement::HAVE_NOTHING:
    1558               0 :       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    1559               0 :       break;
    1560                 :     case nsIDOMHTMLMediaElement::HAVE_METADATA:
    1561                 :     case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
    1562               0 :       FireTimeUpdate(false);
    1563               0 :       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    1564               0 :       break;
    1565                 :     case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
    1566                 :     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
    1567               0 :       DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
    1568               0 :       break;
    1569                 :     }
    1570                 :   }
    1571                 : 
    1572               0 :   mPaused = false;
    1573               0 :   mAutoplaying = false;
    1574                 :   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
    1575                 :   // and our preload status.
    1576               0 :   AddRemoveSelfReference();
    1577               0 :   UpdatePreloadAction();
    1578                 : 
    1579               0 :   return NS_OK;
    1580                 : }
    1581                 : 
    1582               0 : NS_IMPL_STRING_ATTR(nsHTMLMediaElement, Crossorigin, crossorigin)
    1583                 : 
    1584               0 : bool nsHTMLMediaElement::ParseAttribute(PRInt32 aNamespaceID,
    1585                 :                                           nsIAtom* aAttribute,
    1586                 :                                           const nsAString& aValue,
    1587                 :                                           nsAttrValue& aResult)
    1588                 : {
    1589                 :   // Mappings from 'preload' attribute strings to an enumeration.
    1590                 :   static const nsAttrValue::EnumTable kPreloadTable[] = {
    1591                 :     { "",         nsHTMLMediaElement::PRELOAD_ATTR_EMPTY },
    1592                 :     { "none",     nsHTMLMediaElement::PRELOAD_ATTR_NONE },
    1593                 :     { "metadata", nsHTMLMediaElement::PRELOAD_ATTR_METADATA },
    1594                 :     { "auto",     nsHTMLMediaElement::PRELOAD_ATTR_AUTO },
    1595                 :     { 0 }
    1596                 :   };
    1597                 : 
    1598               0 :   if (aNamespaceID == kNameSpaceID_None) {
    1599               0 :     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
    1600               0 :       return true;
    1601                 :     }
    1602               0 :     if (aAttribute == nsGkAtoms::crossorigin) {
    1603               0 :       ParseCORSValue(aValue, aResult);
    1604               0 :       return true;
    1605                 :     }
    1606               0 :     if (aAttribute == nsGkAtoms::preload) {
    1607               0 :       return aResult.ParseEnumValue(aValue, kPreloadTable, false);
    1608                 :     }
    1609                 :   }
    1610                 : 
    1611                 :   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
    1612               0 :                                               aResult);
    1613                 : }
    1614                 : 
    1615               0 : void nsHTMLMediaElement::DoneCreatingElement()
    1616                 : {
    1617               0 :    if (HasAttr(kNameSpaceID_None, nsGkAtoms::muted))
    1618               0 :      mMuted = true; 
    1619               0 : }
    1620                 : 
    1621               0 : nsresult nsHTMLMediaElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
    1622                 :                                      nsIAtom* aPrefix, const nsAString& aValue,
    1623                 :                                      bool aNotify)
    1624                 : {
    1625                 :   nsresult rv =
    1626                 :     nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
    1627               0 :                                   aNotify);
    1628               0 :   if (NS_FAILED(rv))
    1629               0 :     return rv;
    1630               0 :   if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) {
    1631               0 :     Load();
    1632                 :   }
    1633               0 :   if (aNotify && aNameSpaceID == kNameSpaceID_None) {
    1634               0 :     if (aName == nsGkAtoms::autoplay) {
    1635               0 :       StopSuspendingAfterFirstFrame();
    1636               0 :       if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
    1637               0 :         NotifyAutoplayDataReady();
    1638                 :       }
    1639                 :       // This attribute can affect AddRemoveSelfReference
    1640               0 :       AddRemoveSelfReference();
    1641               0 :       UpdatePreloadAction();
    1642               0 :     } else if (aName == nsGkAtoms::preload) {
    1643               0 :       UpdatePreloadAction();
    1644                 :     }
    1645                 :   }
    1646                 : 
    1647               0 :   return rv;
    1648                 : }
    1649                 : 
    1650               0 : nsresult nsHTMLMediaElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttr,
    1651                 :                                        bool aNotify)
    1652                 : {
    1653               0 :   nsresult rv = nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttr, aNotify);
    1654               0 :   if (NS_FAILED(rv))
    1655               0 :     return rv;
    1656               0 :   if (aNotify && aNameSpaceID == kNameSpaceID_None) {
    1657               0 :     if (aAttr == nsGkAtoms::autoplay) {
    1658                 :       // This attribute can affect AddRemoveSelfReference
    1659               0 :       AddRemoveSelfReference();
    1660               0 :       UpdatePreloadAction();
    1661               0 :     } else if (aAttr == nsGkAtoms::preload) {
    1662               0 :       UpdatePreloadAction();
    1663                 :     }
    1664                 :   }
    1665                 : 
    1666               0 :   return rv;
    1667                 : }
    1668                 : 
    1669               0 : nsresult nsHTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
    1670                 :                                         nsIContent* aBindingParent,
    1671                 :                                         bool aCompileEventHandlers)
    1672                 : {
    1673                 :   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument,
    1674                 :                                                  aParent,
    1675                 :                                                  aBindingParent,
    1676               0 :                                                  aCompileEventHandlers);
    1677               0 :   if (aDocument) {
    1678                 :     mAutoplayEnabled =
    1679               0 :       IsAutoplayEnabled() && (!aDocument || !aDocument->IsStaticDocument()) &&
    1680               0 :       !IsEditable();
    1681                 :     // The preload action depends on the value of the autoplay attribute.
    1682                 :     // It's value may have changed, so update it.
    1683               0 :     UpdatePreloadAction();
    1684                 : 
    1685               0 :     if (aDocument->HasAudioAvailableListeners()) {
    1686                 :       // The document already has listeners for the "MozAudioAvailable"
    1687                 :       // event, so the decoder must be notified so it initiates
    1688                 :       // "MozAudioAvailable" event dispatch.
    1689               0 :       NotifyAudioAvailableListener();
    1690                 :     }
    1691                 :   }
    1692                 : 
    1693               0 :   return rv;
    1694                 : }
    1695                 : 
    1696               0 : void nsHTMLMediaElement::UnbindFromTree(bool aDeep,
    1697                 :                                         bool aNullParent)
    1698                 : {
    1699               0 :   if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY)
    1700               0 :     Pause();
    1701               0 :   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
    1702               0 : }
    1703                 : 
    1704                 : #ifdef MOZ_RAW
    1705                 : static const char gRawTypes[][16] = {
    1706                 :   "video/x-raw",
    1707                 :   "video/x-raw-yuv"
    1708                 : };
    1709                 : 
    1710                 : static const char* gRawCodecs[] = {
    1711                 :   nsnull
    1712                 : };
    1713                 : 
    1714                 : static bool IsRawEnabled()
    1715                 : {
    1716                 :   return Preferences::GetBool("media.raw.enabled");
    1717                 : }
    1718                 : 
    1719                 : static bool IsRawType(const nsACString& aType)
    1720                 : {
    1721                 :   if (!IsRawEnabled()) {
    1722                 :     return false;
    1723                 :   }
    1724                 : 
    1725                 :   for (PRUint32 i = 0; i < ArrayLength(gRawTypes); ++i) {
    1726                 :     if (aType.EqualsASCII(gRawTypes[i])) {
    1727                 :       return true;
    1728                 :     }
    1729                 :   }
    1730                 : 
    1731                 :   return false;
    1732                 : }
    1733                 : #endif
    1734                 : #ifdef MOZ_OGG
    1735                 : // See http://www.rfc-editor.org/rfc/rfc5334.txt for the definitions
    1736                 : // of Ogg media types and codec types
    1737                 : const char nsHTMLMediaElement::gOggTypes[3][16] = {
    1738                 :   "video/ogg",
    1739                 :   "audio/ogg",
    1740                 :   "application/ogg"
    1741                 : };
    1742                 : 
    1743                 : char const *const nsHTMLMediaElement::gOggCodecs[3] = {
    1744                 :   "vorbis",
    1745                 :   "theora",
    1746                 :   nsnull
    1747                 : };
    1748                 : 
    1749                 : bool
    1750               0 : nsHTMLMediaElement::IsOggEnabled()
    1751                 : {
    1752               0 :   return Preferences::GetBool("media.ogg.enabled");
    1753                 : }
    1754                 : 
    1755                 : bool
    1756               0 : nsHTMLMediaElement::IsOggType(const nsACString& aType)
    1757                 : {
    1758               0 :   if (!IsOggEnabled()) {
    1759               0 :     return false;
    1760                 :   }
    1761                 : 
    1762               0 :   for (PRUint32 i = 0; i < ArrayLength(gOggTypes); ++i) {
    1763               0 :     if (aType.EqualsASCII(gOggTypes[i])) {
    1764               0 :       return true;
    1765                 :     }
    1766                 :   }
    1767                 : 
    1768               0 :   return false;
    1769                 : }
    1770                 : #endif
    1771                 : 
    1772                 : #ifdef MOZ_WAVE
    1773                 : // See http://www.rfc-editor.org/rfc/rfc2361.txt for the definitions
    1774                 : // of WAVE media types and codec types. However, the audio/vnd.wave
    1775                 : // MIME type described there is not used.
    1776                 : const char nsHTMLMediaElement::gWaveTypes[4][16] = {
    1777                 :   "audio/x-wav",
    1778                 :   "audio/wav",
    1779                 :   "audio/wave",
    1780                 :   "audio/x-pn-wav"
    1781                 : };
    1782                 : 
    1783                 : char const *const nsHTMLMediaElement::gWaveCodecs[2] = {
    1784                 :   "1", // Microsoft PCM Format
    1785                 :   nsnull
    1786                 : };
    1787                 : 
    1788                 : bool
    1789               0 : nsHTMLMediaElement::IsWaveEnabled()
    1790                 : {
    1791               0 :   return Preferences::GetBool("media.wave.enabled");
    1792                 : }
    1793                 : 
    1794                 : bool
    1795               0 : nsHTMLMediaElement::IsWaveType(const nsACString& aType)
    1796                 : {
    1797               0 :   if (!IsWaveEnabled()) {
    1798               0 :     return false;
    1799                 :   }
    1800                 : 
    1801               0 :   for (PRUint32 i = 0; i < ArrayLength(gWaveTypes); ++i) {
    1802               0 :     if (aType.EqualsASCII(gWaveTypes[i])) {
    1803               0 :       return true;
    1804                 :     }
    1805                 :   }
    1806                 : 
    1807               0 :   return false;
    1808                 : }
    1809                 : #endif
    1810                 : 
    1811                 : #ifdef MOZ_WEBM
    1812                 : const char nsHTMLMediaElement::gWebMTypes[2][17] = {
    1813                 :   "video/webm",
    1814                 :   "audio/webm"
    1815                 : };
    1816                 : 
    1817                 : char const *const nsHTMLMediaElement::gWebMCodecs[4] = {
    1818                 :   "vp8",
    1819                 :   "vp8.0",
    1820                 :   "vorbis",
    1821                 :   nsnull
    1822                 : };
    1823                 : 
    1824                 : bool
    1825               0 : nsHTMLMediaElement::IsWebMEnabled()
    1826                 : {
    1827               0 :   return Preferences::GetBool("media.webm.enabled");
    1828                 : }
    1829                 : 
    1830                 : bool
    1831               0 : nsHTMLMediaElement::IsWebMType(const nsACString& aType)
    1832                 : {
    1833               0 :   if (!IsWebMEnabled()) {
    1834               0 :     return false;
    1835                 :   }
    1836                 : 
    1837               0 :   for (PRUint32 i = 0; i < ArrayLength(gWebMTypes); ++i) {
    1838               0 :     if (aType.EqualsASCII(gWebMTypes[i])) {
    1839               0 :       return true;
    1840                 :     }
    1841                 :   }
    1842                 : 
    1843               0 :   return false;
    1844                 : }
    1845                 : #endif
    1846                 : 
    1847                 : /* static */
    1848                 : nsHTMLMediaElement::CanPlayStatus 
    1849               0 : nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType,
    1850                 :                                        char const *const ** aCodecList)
    1851                 : {
    1852                 : #ifdef MOZ_RAW
    1853                 :   if (IsRawType(nsDependentCString(aMIMEType))) {
    1854                 :     *aCodecList = gRawCodecs;
    1855                 :     return CANPLAY_MAYBE;
    1856                 :   }
    1857                 : #endif
    1858                 : #ifdef MOZ_OGG
    1859               0 :   if (IsOggType(nsDependentCString(aMIMEType))) {
    1860               0 :     *aCodecList = gOggCodecs;
    1861               0 :     return CANPLAY_MAYBE;
    1862                 :   }
    1863                 : #endif
    1864                 : #ifdef MOZ_WAVE
    1865               0 :   if (IsWaveType(nsDependentCString(aMIMEType))) {
    1866               0 :     *aCodecList = gWaveCodecs;
    1867               0 :     return CANPLAY_MAYBE;
    1868                 :   }
    1869                 : #endif
    1870                 : #ifdef MOZ_WEBM
    1871               0 :   if (IsWebMType(nsDependentCString(aMIMEType))) {
    1872               0 :     *aCodecList = gWebMCodecs;
    1873               0 :     return CANPLAY_YES;
    1874                 :   }
    1875                 : #endif
    1876               0 :   return CANPLAY_NO;
    1877                 : }
    1878                 : 
    1879                 : /* static */
    1880               0 : bool nsHTMLMediaElement::ShouldHandleMediaType(const char* aMIMEType)
    1881                 : {
    1882                 : #ifdef MOZ_RAW
    1883                 :   if (IsRawType(nsDependentCString(aMIMEType)))
    1884                 :     return true;
    1885                 : #endif
    1886                 : #ifdef MOZ_OGG
    1887               0 :   if (IsOggType(nsDependentCString(aMIMEType)))
    1888               0 :     return true;
    1889                 : #endif
    1890                 : #ifdef MOZ_WEBM
    1891               0 :   if (IsWebMType(nsDependentCString(aMIMEType)))
    1892               0 :     return true;
    1893                 : #endif
    1894                 :   // We should not return true for Wave types, since there are some
    1895                 :   // Wave codecs actually in use in the wild that we don't support, and
    1896                 :   // we should allow those to be handled by plugins or helper apps.
    1897                 :   // Furthermore people can play Wave files on most platforms by other
    1898                 :   // means.
    1899               0 :   return false;
    1900                 : }
    1901                 : 
    1902                 : static bool
    1903               0 : CodecListContains(char const *const * aCodecs, const nsAString& aCodec)
    1904                 : {
    1905               0 :   for (PRInt32 i = 0; aCodecs[i]; ++i) {
    1906               0 :     if (aCodec.EqualsASCII(aCodecs[i]))
    1907               0 :       return true;
    1908                 :   }
    1909               0 :   return false;
    1910                 : }
    1911                 : 
    1912                 : /* static */
    1913                 : nsHTMLMediaElement::CanPlayStatus
    1914               0 : nsHTMLMediaElement::GetCanPlay(const nsAString& aType)
    1915                 : {
    1916               0 :   nsContentTypeParser parser(aType);
    1917               0 :   nsAutoString mimeType;
    1918               0 :   nsresult rv = parser.GetType(mimeType);
    1919               0 :   if (NS_FAILED(rv))
    1920               0 :     return CANPLAY_NO;
    1921                 : 
    1922               0 :   NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
    1923                 :   char const *const * supportedCodecs;
    1924                 :   CanPlayStatus status = CanHandleMediaType(mimeTypeUTF8.get(),
    1925               0 :                                             &supportedCodecs);
    1926               0 :   if (status == CANPLAY_NO)
    1927               0 :     return CANPLAY_NO;
    1928                 : 
    1929               0 :   nsAutoString codecs;
    1930               0 :   rv = parser.GetParameter("codecs", codecs);
    1931               0 :   if (NS_FAILED(rv)) {
    1932                 :     // Parameter not found or whatever
    1933               0 :     return status;
    1934                 :   }
    1935                 : 
    1936               0 :   CanPlayStatus result = CANPLAY_YES;
    1937                 :   // See http://www.rfc-editor.org/rfc/rfc4281.txt for the description
    1938                 :   // of the 'codecs' parameter
    1939               0 :   nsCharSeparatedTokenizer tokenizer(codecs, ',');
    1940               0 :   bool expectMoreTokens = false;
    1941               0 :   while (tokenizer.hasMoreTokens()) {
    1942               0 :     const nsSubstring& token = tokenizer.nextToken();
    1943                 : 
    1944               0 :     if (!CodecListContains(supportedCodecs, token)) {
    1945                 :       // Totally unsupported codec
    1946               0 :       return CANPLAY_NO;
    1947                 :     }
    1948               0 :     expectMoreTokens = tokenizer.lastTokenEndedWithSeparator();
    1949                 :   }
    1950               0 :   if (expectMoreTokens) {
    1951                 :     // Last codec name was empty
    1952               0 :     return CANPLAY_NO;
    1953                 :   }
    1954               0 :   return result;
    1955                 : }
    1956                 : 
    1957                 : NS_IMETHODIMP
    1958               0 : nsHTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
    1959                 : {
    1960               0 :   switch (GetCanPlay(aType)) {
    1961                 :   case CANPLAY_NO:
    1962               0 :     aResult.Truncate();
    1963               0 :     break;
    1964                 :   case CANPLAY_YES:
    1965               0 :     aResult.AssignLiteral("probably");
    1966               0 :     break;
    1967                 :   default:
    1968                 :   case CANPLAY_MAYBE:
    1969               0 :     aResult.AssignLiteral("maybe");
    1970               0 :     break;
    1971                 :   }
    1972               0 :   return NS_OK;
    1973                 : }
    1974                 : 
    1975                 : already_AddRefed<nsMediaDecoder>
    1976               0 : nsHTMLMediaElement::CreateDecoder(const nsACString& aType)
    1977                 : {
    1978                 : #ifdef MOZ_RAW
    1979                 :   if (IsRawType(aType)) {
    1980                 :     nsRefPtr<nsRawDecoder> decoder = new nsRawDecoder();
    1981                 :     if (decoder->Init(this)) {
    1982                 :       return decoder.forget();
    1983                 :     }
    1984                 :   }
    1985                 : #endif
    1986                 : #ifdef MOZ_OGG
    1987               0 :   if (IsOggType(aType)) {
    1988               0 :     nsRefPtr<nsOggDecoder> decoder = new nsOggDecoder();
    1989               0 :     if (decoder->Init(this)) {
    1990               0 :       return decoder.forget();
    1991                 :     }
    1992                 :   }
    1993                 : #endif
    1994                 : #ifdef MOZ_WAVE
    1995               0 :   if (IsWaveType(aType)) {
    1996               0 :     nsRefPtr<nsWaveDecoder> decoder = new nsWaveDecoder();
    1997               0 :     if (decoder->Init(this)) {
    1998               0 :       return decoder.forget();
    1999                 :     }
    2000                 :   }
    2001                 : #endif
    2002                 : #ifdef MOZ_WEBM
    2003               0 :   if (IsWebMType(aType)) {
    2004               0 :     nsRefPtr<nsWebMDecoder> decoder = new nsWebMDecoder();
    2005               0 :     if (decoder->Init(this)) {
    2006               0 :       return decoder.forget();
    2007                 :     }
    2008                 :   }
    2009                 : #endif
    2010               0 :   return nsnull;
    2011                 : }
    2012                 : 
    2013               0 : nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal)
    2014                 : {
    2015               0 :   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
    2016               0 :   NS_ASSERTION(mDecoder == nsnull, "Shouldn't have a decoder");
    2017                 : 
    2018               0 :   MediaResource* originalResource = aOriginal->GetResource();
    2019               0 :   if (!originalResource)
    2020               0 :     return NS_ERROR_FAILURE;
    2021               0 :   nsRefPtr<nsMediaDecoder> decoder = aOriginal->Clone();
    2022               0 :   if (!decoder)
    2023               0 :     return NS_ERROR_FAILURE;
    2024                 : 
    2025               0 :   LOG(PR_LOG_DEBUG, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
    2026                 : 
    2027               0 :   if (!decoder->Init(this)) {
    2028               0 :     LOG(PR_LOG_DEBUG, ("%p Failed to init cloned decoder %p", this, decoder.get()));
    2029               0 :     return NS_ERROR_FAILURE;
    2030                 :   }
    2031                 : 
    2032               0 :   double duration = aOriginal->GetDuration();
    2033               0 :   if (duration >= 0) {
    2034               0 :     decoder->SetDuration(duration);
    2035               0 :     decoder->SetSeekable(aOriginal->IsSeekable());
    2036                 :   }
    2037                 : 
    2038               0 :   MediaResource* resource = originalResource->CloneData(decoder);
    2039               0 :   if (!resource) {
    2040               0 :     LOG(PR_LOG_DEBUG, ("%p Failed to cloned stream for decoder %p", this, decoder.get()));
    2041               0 :     return NS_ERROR_FAILURE;
    2042                 :   }
    2043                 : 
    2044               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
    2045                 : 
    2046               0 :   nsresult rv = decoder->Load(resource, nsnull, aOriginal);
    2047               0 :   if (NS_FAILED(rv)) {
    2048               0 :     LOG(PR_LOG_DEBUG, ("%p Failed to load decoder/stream for decoder %p", this, decoder.get()));
    2049               0 :     return rv;
    2050                 :   }
    2051                 : 
    2052               0 :   return FinishDecoderSetup(decoder);
    2053                 : }
    2054                 : 
    2055               0 : nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel,
    2056                 :                                                          nsIStreamListener **aListener)
    2057                 : {
    2058               0 :   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
    2059               0 :   NS_ASSERTION(mDecoder == nsnull, "Shouldn't have a decoder");
    2060                 : 
    2061               0 :   nsCAutoString mimeType;
    2062               0 :   aChannel->GetContentType(mimeType);
    2063                 : 
    2064               0 :   nsRefPtr<nsMediaDecoder> decoder = CreateDecoder(mimeType);
    2065               0 :   if (!decoder) {
    2066               0 :     nsAutoString src;
    2067               0 :     GetCurrentSrc(src);
    2068               0 :     NS_ConvertUTF8toUTF16 mimeUTF16(mimeType);
    2069               0 :     const PRUnichar* params[] = { mimeUTF16.get(), src.get() };
    2070               0 :     ReportLoadError("MediaLoadUnsupportedMimeType", params, ArrayLength(params));
    2071               0 :     return NS_ERROR_FAILURE;
    2072                 :   }
    2073                 : 
    2074               0 :   LOG(PR_LOG_DEBUG, ("%p Created decoder %p for type %s", this, decoder.get(), mimeType.get()));
    2075                 : 
    2076               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
    2077                 : 
    2078               0 :   MediaResource* resource = MediaResource::Create(decoder, aChannel);
    2079               0 :   if (!resource)
    2080               0 :     return NS_ERROR_OUT_OF_MEMORY;
    2081                 : 
    2082               0 :   nsresult rv = decoder->Load(resource, aListener, nsnull);
    2083               0 :   if (NS_FAILED(rv)) {
    2084               0 :     return rv;
    2085                 :   }
    2086                 : 
    2087                 :   // Decoder successfully created, the decoder now owns the MediaResource
    2088                 :   // which owns the channel.
    2089               0 :   mChannel = nsnull;
    2090                 : 
    2091               0 :   return FinishDecoderSetup(decoder);
    2092                 : }
    2093                 : 
    2094               0 : nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
    2095                 : {
    2096               0 :   NS_ASSERTION(mLoadingSrc, "mLoadingSrc set up");
    2097                 : 
    2098               0 :   mDecoder = aDecoder;
    2099               0 :   AddMediaElementToURITable();
    2100                 : 
    2101                 :   // Force a same-origin check before allowing events for this media resource.
    2102               0 :   mMediaSecurityVerified = false;
    2103                 : 
    2104                 :   // The new resource has not been suspended by us.
    2105               0 :   mPausedForInactiveDocument = false;
    2106                 :   // But we may want to suspend it now.
    2107                 :   // This will also do an AddRemoveSelfReference.
    2108               0 :   NotifyOwnerDocumentActivityChanged();
    2109                 : 
    2110               0 :   nsresult rv = NS_OK;
    2111                 : 
    2112               0 :   mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
    2113                 : 
    2114               0 :   if (!mPaused) {
    2115               0 :     SetPlayedOrSeeked(true);
    2116               0 :     if (!mPausedForInactiveDocument) {
    2117               0 :       rv = mDecoder->Play();
    2118                 :     }
    2119                 :   }
    2120                 : 
    2121               0 :   if (OwnerDoc()->HasAudioAvailableListeners()) {
    2122               0 :     NotifyAudioAvailableListener();
    2123                 :   }
    2124                 : 
    2125               0 :   if (NS_FAILED(rv)) {
    2126               0 :     RemoveMediaElementFromURITable();
    2127               0 :     mDecoder->Shutdown();
    2128               0 :     mDecoder = nsnull;
    2129                 :   }
    2130                 : 
    2131               0 :   NS_ASSERTION(NS_SUCCEEDED(rv) == (MediaElementTableCount(this, mLoadingSrc) == 1),
    2132                 :     "Media element should have single table entry if decode initialized");
    2133                 : 
    2134               0 :   mBegun = true;
    2135               0 :   return rv;
    2136                 : }
    2137                 : 
    2138               0 : nsresult nsHTMLMediaElement::NewURIFromString(const nsAutoString& aURISpec, nsIURI** aURI)
    2139                 : {
    2140               0 :   NS_ENSURE_ARG_POINTER(aURI);
    2141                 : 
    2142               0 :   *aURI = nsnull;
    2143                 : 
    2144               0 :   nsCOMPtr<nsIDocument> doc = OwnerDoc();
    2145                 : 
    2146               0 :   nsCOMPtr<nsIURI> baseURI = GetBaseURI();
    2147               0 :   nsCOMPtr<nsIURI> uri;
    2148               0 :   nsresult rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
    2149                 :                                                           aURISpec,
    2150                 :                                                           doc,
    2151               0 :                                                           baseURI);
    2152               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2153                 : 
    2154                 :   bool equal;
    2155               0 :   if (aURISpec.IsEmpty() &&
    2156               0 :       doc->GetDocumentURI() &&
    2157               0 :       NS_SUCCEEDED(doc->GetDocumentURI()->Equals(uri, &equal)) &&
    2158                 :       equal) {
    2159                 :     // It's not possible for a media resource to be embedded in the current
    2160                 :     // document we extracted aURISpec from, so there's no point returning
    2161                 :     // the current document URI just to let the caller attempt and fail to
    2162                 :     // decode it.
    2163               0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    2164                 :   }
    2165                 : 
    2166               0 :   uri.forget(aURI);
    2167               0 :   return NS_OK;
    2168                 : }
    2169                 : 
    2170               0 : void nsHTMLMediaElement::ProcessMediaFragmentURI()
    2171                 : {
    2172               0 :   nsCAutoString ref;
    2173               0 :   GetCurrentSpec(ref);
    2174               0 :   nsMediaFragmentURIParser parser(ref);
    2175               0 :   parser.Parse();
    2176               0 :   double start = parser.GetStartTime();
    2177               0 :   if (mDecoder) {
    2178               0 :     double end = parser.GetEndTime();
    2179               0 :     if (end < 0.0 || end > start) {
    2180               0 :       mFragmentEnd = end;
    2181                 :     }
    2182                 :     else {
    2183               0 :       start = -1.0;
    2184               0 :       end = -1.0;
    2185                 :     }
    2186                 :   }
    2187               0 :   if (start > 0.0) {
    2188               0 :     SetCurrentTime(start);
    2189               0 :     mFragmentStart = start;
    2190                 :   }
    2191               0 : }
    2192                 : 
    2193               0 : void nsHTMLMediaElement::MetadataLoaded(PRUint32 aChannels, PRUint32 aRate)
    2194                 : {
    2195               0 :   mChannels = aChannels;
    2196               0 :   mRate = aRate;
    2197               0 :   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
    2198               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
    2199               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
    2200               0 :   if (mDecoder && mDecoder->IsSeekable()) {
    2201               0 :     ProcessMediaFragmentURI();
    2202               0 :     mDecoder->SetEndTime(mFragmentEnd);
    2203                 :   }
    2204               0 : }
    2205                 : 
    2206               0 : void nsHTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
    2207                 : {
    2208                 :   ChangeReadyState(aResourceFullyLoaded ?
    2209                 :     nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA :
    2210               0 :     nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
    2211               0 :   ChangeDelayLoadStatus(false);
    2212                 : 
    2213               0 :   NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
    2214                 : 
    2215               0 :   if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
    2216               0 :       !aResourceFullyLoaded &&
    2217               0 :       !HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
    2218                 :       mPreloadAction == nsHTMLMediaElement::PRELOAD_METADATA) {
    2219               0 :     mSuspendedAfterFirstFrame = true;
    2220               0 :     mDecoder->Suspend();
    2221                 :   }
    2222               0 : }
    2223                 : 
    2224               0 : void nsHTMLMediaElement::ResourceLoaded()
    2225                 : {
    2226               0 :   mBegun = false;
    2227               0 :   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
    2228               0 :   AddRemoveSelfReference();
    2229               0 :   if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA) {
    2230               0 :     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
    2231                 :   }
    2232                 :   // Ensure a progress event is dispatched at the end of download.
    2233               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
    2234                 :   // The download has stopped.
    2235               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
    2236               0 : }
    2237                 : 
    2238               0 : void nsHTMLMediaElement::NetworkError()
    2239                 : {
    2240               0 :   Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
    2241               0 : }
    2242                 : 
    2243               0 : void nsHTMLMediaElement::DecodeError()
    2244                 : {
    2245               0 :   nsAutoString src;
    2246               0 :   GetCurrentSrc(src);
    2247               0 :   const PRUnichar* params[] = { src.get() };
    2248               0 :   ReportLoadError("MediaLoadDecodeError", params, ArrayLength(params));
    2249                 : 
    2250               0 :   if (mDecoder) {
    2251               0 :     RemoveMediaElementFromURITable();
    2252               0 :     mDecoder->Shutdown();
    2253               0 :     mDecoder = nsnull;
    2254                 :   }
    2255               0 :   mLoadingSrc = nsnull;
    2256               0 :   if (mIsLoadingFromSourceChildren) {
    2257               0 :     mError = nsnull;
    2258               0 :     if (mSourceLoadCandidate) {
    2259               0 :       DispatchAsyncSourceError(mSourceLoadCandidate);
    2260               0 :       QueueLoadFromSourceTask();
    2261                 :     } else {
    2262               0 :       NS_WARNING("Should know the source we were loading from!");
    2263                 :     }
    2264                 :   } else {
    2265               0 :     Error(nsIDOMMediaError::MEDIA_ERR_DECODE);
    2266                 :   }
    2267               0 : }
    2268                 : 
    2269               0 : void nsHTMLMediaElement::LoadAborted()
    2270                 : {
    2271               0 :   Error(nsIDOMMediaError::MEDIA_ERR_ABORTED);
    2272               0 : }
    2273                 : 
    2274               0 : void nsHTMLMediaElement::Error(PRUint16 aErrorCode)
    2275                 : {
    2276               0 :   NS_ASSERTION(aErrorCode == nsIDOMMediaError::MEDIA_ERR_DECODE ||
    2277                 :                aErrorCode == nsIDOMMediaError::MEDIA_ERR_NETWORK ||
    2278                 :                aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
    2279                 :                "Only use nsIDOMMediaError codes!");
    2280               0 :   mError = new nsMediaError(aErrorCode);
    2281               0 :   mBegun = false;
    2282               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("error"));
    2283               0 :   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
    2284               0 :     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
    2285               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
    2286                 :   } else {
    2287               0 :     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
    2288                 :   }
    2289               0 :   AddRemoveSelfReference();
    2290               0 :   ChangeDelayLoadStatus(false);
    2291               0 : }
    2292                 : 
    2293               0 : void nsHTMLMediaElement::PlaybackEnded()
    2294                 : {
    2295               0 :   NS_ASSERTION(mDecoder->IsEnded(), "Decoder fired ended, but not in ended state");
    2296                 :   // We changed the state of IsPlaybackEnded which can affect AddRemoveSelfReference
    2297               0 :   AddRemoveSelfReference();
    2298                 : 
    2299               0 :   if (mDecoder && mDecoder->IsInfinite()) {
    2300               0 :     LOG(PR_LOG_DEBUG, ("%p, got duration by reaching the end of the resource", this));
    2301               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
    2302                 :   }
    2303                 : 
    2304               0 :   if (HasAttr(kNameSpaceID_None, nsGkAtoms::loop)) {
    2305               0 :     SetCurrentTime(0);
    2306               0 :     return;
    2307                 :   }
    2308                 : 
    2309               0 :   FireTimeUpdate(false);
    2310               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
    2311                 : }
    2312                 : 
    2313               0 : void nsHTMLMediaElement::SeekStarted()
    2314                 : {
    2315               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
    2316               0 :   FireTimeUpdate(false);
    2317               0 : }
    2318                 : 
    2319               0 : void nsHTMLMediaElement::SeekCompleted()
    2320                 : {
    2321               0 :   mPlayingBeforeSeek = false;
    2322               0 :   SetPlayedOrSeeked(true);
    2323               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
    2324                 :   // We changed whether we're seeking so we need to AddRemoveSelfReference
    2325               0 :   AddRemoveSelfReference();
    2326               0 : }
    2327                 : 
    2328               0 : void nsHTMLMediaElement::DownloadSuspended()
    2329                 : {
    2330               0 :   DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
    2331               0 :   if (mBegun) {
    2332               0 :     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
    2333               0 :     AddRemoveSelfReference();
    2334               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
    2335                 :   }
    2336               0 : }
    2337                 : 
    2338               0 : void nsHTMLMediaElement::DownloadResumed()
    2339                 : {
    2340               0 :   if (mBegun) {
    2341               0 :     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
    2342               0 :     AddRemoveSelfReference();
    2343                 :   }
    2344               0 : }
    2345                 : 
    2346               0 : void nsHTMLMediaElement::DownloadStalled()
    2347                 : {
    2348               0 :   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
    2349               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("stalled"));
    2350                 :   }
    2351               0 : }
    2352                 : 
    2353               0 : bool nsHTMLMediaElement::ShouldCheckAllowOrigin()
    2354                 : {
    2355               0 :   return mCORSMode != CORS_NONE;
    2356                 : }
    2357                 : 
    2358               0 : void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
    2359                 : {
    2360               0 :   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
    2361                 :     // aNextFrame might have a next frame because the decoder can advance
    2362                 :     // on its own thread before ResourceLoaded or MetadataLoaded gets
    2363                 :     // a chance to run.
    2364                 :     // The arrival of more data can't change us out of this readyState.
    2365               0 :     return;
    2366                 :   }
    2367                 : 
    2368               0 :   if (aNextFrame != NEXT_FRAME_AVAILABLE) {
    2369               0 :     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
    2370               0 :     if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
    2371               0 :       FireTimeUpdate(false);
    2372               0 :       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    2373               0 :       mWaitingFired = true;
    2374                 :     }
    2375               0 :     return;
    2376                 :   }
    2377                 : 
    2378                 :   // Now see if we should set HAVE_ENOUGH_DATA.
    2379                 :   // If it's something we don't know the size of, then we can't
    2380                 :   // make a real estimate, so we go straight to HAVE_ENOUGH_DATA once
    2381                 :   // we've downloaded enough data that our download rate is considered
    2382                 :   // reliable. We have to move to HAVE_ENOUGH_DATA at some point or
    2383                 :   // autoplay elements for live streams will never play. Otherwise we
    2384                 :   // move to HAVE_ENOUGH_DATA if we can play through the entire media
    2385                 :   // without stopping to buffer.
    2386               0 :   nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
    2387               0 :   if (stats.mTotalBytes < 0 ? stats.mDownloadRateReliable :
    2388                 :                               stats.mTotalBytes == stats.mDownloadPosition ||
    2389               0 :       mDecoder->CanPlayThrough())
    2390                 :   {
    2391               0 :     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
    2392               0 :     return;
    2393                 :   }
    2394               0 :   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
    2395                 : }
    2396                 : 
    2397                 : #ifdef PR_LOGGING
    2398                 : static const char* gReadyStateToString[] = {
    2399                 :   "HAVE_NOTHING",
    2400                 :   "HAVE_METADATA",
    2401                 :   "HAVE_CURRENT_DATA",
    2402                 :   "HAVE_FUTURE_DATA",
    2403                 :   "HAVE_ENOUGH_DATA"
    2404                 : };
    2405                 : #endif
    2406                 : 
    2407               0 : void nsHTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
    2408                 : {
    2409               0 :   nsMediaReadyState oldState = mReadyState;
    2410               0 :   mReadyState = aState;
    2411                 : 
    2412               0 :   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY ||
    2413                 :       oldState == mReadyState) {
    2414               0 :     return;
    2415                 :   }
    2416                 : 
    2417               0 :   LOG(PR_LOG_DEBUG, ("%p Ready state changed to %s", this, gReadyStateToString[aState]));
    2418                 : 
    2419                 :   // Handle raising of "waiting" event during seek (see 4.8.10.9)
    2420               0 :   if (mPlayingBeforeSeek &&
    2421                 :       oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
    2422               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
    2423                 :   }
    2424                 : 
    2425               0 :   if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
    2426                 :       mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
    2427               0 :       !mLoadedFirstFrame)
    2428                 :   {
    2429               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
    2430               0 :     mLoadedFirstFrame = true;
    2431                 :   }
    2432                 : 
    2433               0 :   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
    2434               0 :     mWaitingFired = false;
    2435                 :   }
    2436                 : 
    2437               0 :   if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
    2438                 :       mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
    2439               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
    2440                 :   }
    2441                 : 
    2442               0 :   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
    2443               0 :     NotifyAutoplayDataReady();
    2444                 :   }
    2445                 : 
    2446               0 :   if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
    2447                 :       mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
    2448               0 :       IsPotentiallyPlaying()) {
    2449               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
    2450                 :   }
    2451                 : 
    2452               0 :   if (oldState < nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA &&
    2453                 :       mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
    2454               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("canplaythrough"));
    2455                 :   }
    2456                 : }
    2457                 : 
    2458               0 : bool nsHTMLMediaElement::CanActivateAutoplay()
    2459                 : {
    2460                 :   return mAutoplaying &&
    2461                 :          mPaused &&
    2462               0 :          HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
    2463                 :          mAutoplayEnabled &&
    2464               0 :          !IsEditable();
    2465                 : }
    2466                 : 
    2467               0 : void nsHTMLMediaElement::NotifyAutoplayDataReady()
    2468                 : {
    2469               0 :   if (CanActivateAutoplay()) {
    2470               0 :     mPaused = false;
    2471                 :     // We changed mPaused which can affect AddRemoveSelfReference
    2472               0 :     AddRemoveSelfReference();
    2473                 : 
    2474               0 :     if (mDecoder) {
    2475               0 :       SetPlayedOrSeeked(true);
    2476               0 :       mDecoder->Play();
    2477                 :     }
    2478               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
    2479                 :   }
    2480               0 : }
    2481                 : 
    2482               0 : VideoFrameContainer* nsHTMLMediaElement::GetVideoFrameContainer()
    2483                 : {
    2484               0 :   if (mVideoFrameContainer)
    2485               0 :     return mVideoFrameContainer;
    2486                 : 
    2487                 :   // If we have a print surface, this is just a static image so
    2488                 :   // no image container is required
    2489               0 :   if (mPrintSurface)
    2490               0 :     return nsnull;
    2491                 : 
    2492                 :   // Only video frames need an image container.
    2493               0 :   nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
    2494               0 :   if (!video)
    2495               0 :     return nsnull;
    2496                 : 
    2497                 :   mVideoFrameContainer =
    2498               0 :     new VideoFrameContainer(this, LayerManager::CreateImageContainer());
    2499               0 :   return mVideoFrameContainer;
    2500                 : }
    2501                 : 
    2502               0 : nsresult nsHTMLMediaElement::DispatchAudioAvailableEvent(float* aFrameBuffer,
    2503                 :                                                          PRUint32 aFrameBufferLength,
    2504                 :                                                          float aTime)
    2505                 : {
    2506                 :   // Auto manage the memory for the frame buffer. If we fail and return
    2507                 :   // an error, this ensures we free the memory in the frame buffer. Otherwise
    2508                 :   // we hand off ownership of the frame buffer to the audioavailable event,
    2509                 :   // which frees the memory when it's destroyed.
    2510               0 :   nsAutoArrayPtr<float> frameBuffer(aFrameBuffer);
    2511                 : 
    2512               0 :   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc());
    2513               0 :   nsCOMPtr<nsIDOMEventTarget> target(do_QueryObject(this));
    2514               0 :   NS_ENSURE_TRUE(domDoc && target, NS_ERROR_INVALID_ARG);
    2515                 : 
    2516               0 :   nsCOMPtr<nsIDOMEvent> event;
    2517               0 :   nsresult rv = domDoc->CreateEvent(NS_LITERAL_STRING("MozAudioAvailableEvent"),
    2518               0 :                                     getter_AddRefs(event));
    2519               0 :   nsCOMPtr<nsIDOMNotifyAudioAvailableEvent> audioavailableEvent(do_QueryInterface(event));
    2520               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2521                 : 
    2522               0 :   rv = audioavailableEvent->InitAudioAvailableEvent(NS_LITERAL_STRING("MozAudioAvailable"),
    2523                 :                                                     true, true, frameBuffer.forget(), aFrameBufferLength,
    2524               0 :                                                     aTime, mAllowAudioData);
    2525               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2526                 : 
    2527                 :   bool dummy;
    2528               0 :   return target->DispatchEvent(event, &dummy);
    2529                 : }
    2530                 : 
    2531               0 : nsresult nsHTMLMediaElement::DispatchEvent(const nsAString& aName)
    2532                 : {
    2533               0 :   LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching event %s", this,
    2534                 :                           NS_ConvertUTF16toUTF8(aName).get()));
    2535                 : 
    2536                 :   // Save events that occur while in the bfcache. These will be dispatched
    2537                 :   // if the page comes out of the bfcache.
    2538               0 :   if (mPausedForInactiveDocument) {
    2539               0 :     mPendingEvents.AppendElement(aName);
    2540               0 :     return NS_OK;
    2541                 :   }
    2542                 : 
    2543                 :   return nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
    2544                 :                                               static_cast<nsIContent*>(this),
    2545                 :                                               aName,
    2546                 :                                               false,
    2547               0 :                                               true);
    2548                 : }
    2549                 : 
    2550               0 : nsresult nsHTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
    2551                 : {
    2552               0 :   LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing event %s", this,
    2553                 :             NS_ConvertUTF16toUTF8(aName).get()));
    2554                 : 
    2555               0 :   nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this);
    2556               0 :   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    2557               0 :   return NS_OK;
    2558                 : }
    2559                 : 
    2560               0 : nsresult nsHTMLMediaElement::DispatchPendingMediaEvents()
    2561                 : {
    2562               0 :   NS_ASSERTION(!mPausedForInactiveDocument,
    2563                 :                "Must not be in bfcache when dispatching pending media events");
    2564                 : 
    2565               0 :   PRUint32 count = mPendingEvents.Length();
    2566               0 :   for (PRUint32 i = 0; i < count; ++i) {
    2567               0 :     DispatchAsyncEvent(mPendingEvents[i]);
    2568                 :   }
    2569               0 :   mPendingEvents.Clear();
    2570                 : 
    2571               0 :   return NS_OK;
    2572                 : }
    2573                 : 
    2574               0 : bool nsHTMLMediaElement::IsPotentiallyPlaying() const
    2575                 : {
    2576                 :   // TODO:
    2577                 :   //   playback has not stopped due to errors,
    2578                 :   //   and the element has not paused for user interaction
    2579                 :   return
    2580               0 :     !mPaused &&
    2581                 :     (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA ||
    2582                 :     mReadyState == nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) &&
    2583               0 :     !IsPlaybackEnded();
    2584                 : }
    2585                 : 
    2586               0 : bool nsHTMLMediaElement::IsPlaybackEnded() const
    2587                 : {
    2588                 :   // TODO:
    2589                 :   //   the current playback position is equal to the effective end of the media resource.
    2590                 :   //   See bug 449157.
    2591                 :   return mNetworkState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
    2592               0 :     mDecoder ? mDecoder->IsEnded() : false;
    2593                 : }
    2594                 : 
    2595               0 : already_AddRefed<nsIPrincipal> nsHTMLMediaElement::GetCurrentPrincipal()
    2596                 : {
    2597               0 :   if (!mDecoder)
    2598               0 :     return nsnull;
    2599                 : 
    2600               0 :   return mDecoder->GetCurrentPrincipal();
    2601                 : }
    2602                 : 
    2603               0 : void nsHTMLMediaElement::UpdateMediaSize(nsIntSize size)
    2604                 : {
    2605               0 :   mMediaSize = size;
    2606               0 : }
    2607                 : 
    2608               0 : void nsHTMLMediaElement::NotifyOwnerDocumentActivityChanged()
    2609                 : {
    2610               0 :   nsIDocument* ownerDoc = OwnerDoc();
    2611                 :   bool pauseForInactiveDocument =
    2612               0 :     !ownerDoc->IsActive() || !ownerDoc->IsVisible();
    2613                 : 
    2614               0 :   if (pauseForInactiveDocument != mPausedForInactiveDocument) {
    2615               0 :     mPausedForInactiveDocument = pauseForInactiveDocument;
    2616               0 :     if (mDecoder) {
    2617               0 :       if (pauseForInactiveDocument) {
    2618               0 :         mDecoder->Pause();
    2619               0 :         mDecoder->Suspend();
    2620                 :       } else {
    2621               0 :         mDecoder->Resume(false);
    2622               0 :         DispatchPendingMediaEvents();
    2623               0 :         if (!mPaused && !mDecoder->IsEnded()) {
    2624               0 :           mDecoder->Play();
    2625                 :         }
    2626                 :       }
    2627                 :     }
    2628                 :   }
    2629                 : 
    2630               0 :   AddRemoveSelfReference();
    2631               0 : }
    2632                 : 
    2633               0 : void nsHTMLMediaElement::AddRemoveSelfReference()
    2634                 : {
    2635                 :   // XXX we could release earlier here in many situations if we examined
    2636                 :   // which event listeners are attached. Right now we assume there is a
    2637                 :   // potential listener for every event. We would also have to keep the
    2638                 :   // element alive if it was playing and producing audio output --- right now
    2639                 :   // that's covered by the !mPaused check.
    2640               0 :   nsIDocument* ownerDoc = OwnerDoc();
    2641                 : 
    2642                 :   // See the comment at the top of this file for the explanation of this
    2643                 :   // boolean expression.
    2644               0 :   bool needSelfReference = !mShuttingDown &&
    2645               0 :     ownerDoc->IsActive() &&
    2646                 :     (mDelayingLoadEvent ||
    2647               0 :      (!mPaused && mDecoder && !mDecoder->IsEnded()) ||
    2648               0 :      (mDecoder && mDecoder->IsSeeking()) ||
    2649               0 :      CanActivateAutoplay() ||
    2650               0 :      mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING);
    2651                 : 
    2652               0 :   if (needSelfReference != mHasSelfReference) {
    2653               0 :     mHasSelfReference = needSelfReference;
    2654               0 :     if (needSelfReference) {
    2655                 :       // The observer service will hold a strong reference to us. This
    2656                 :       // will do to keep us alive. We need to know about shutdown so that
    2657                 :       // we can release our self-reference.
    2658               0 :       nsContentUtils::RegisterShutdownObserver(this);
    2659                 :     } else {
    2660                 :       // Dispatch Release asynchronously so that we don't destroy this object
    2661                 :       // inside a call stack of method calls on this object
    2662                 :       nsCOMPtr<nsIRunnable> event =
    2663               0 :         NS_NewRunnableMethod(this, &nsHTMLMediaElement::DoRemoveSelfReference);
    2664               0 :       NS_DispatchToMainThread(event);
    2665                 :     }
    2666                 :   }
    2667               0 : }
    2668                 : 
    2669               0 : void nsHTMLMediaElement::DoRemoveSelfReference()
    2670                 : {
    2671                 :   // We don't need the shutdown observer anymore. Unregistering releases
    2672                 :   // its reference to us, which we were using as our self-reference.
    2673               0 :   nsContentUtils::UnregisterShutdownObserver(this);
    2674               0 : }
    2675                 : 
    2676               0 : nsresult nsHTMLMediaElement::Observe(nsISupports* aSubject,
    2677                 :                                      const char* aTopic, const PRUnichar* aData)
    2678                 : {
    2679               0 :   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
    2680                 :   
    2681               0 :   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
    2682               0 :     mShuttingDown = true;
    2683               0 :     AddRemoveSelfReference();
    2684                 :   }
    2685               0 :   return NS_OK;
    2686                 : }
    2687                 : 
    2688                 : bool
    2689               0 : nsHTMLMediaElement::IsNodeOfType(PRUint32 aFlags) const
    2690                 : {
    2691               0 :   return !(aFlags & ~(eCONTENT | eMEDIA));
    2692                 : }
    2693                 : 
    2694               0 : void nsHTMLMediaElement::DispatchAsyncSourceError(nsIContent* aSourceElement)
    2695                 : {
    2696               0 :   LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing simple source error event", this));
    2697                 : 
    2698               0 :   nsCOMPtr<nsIRunnable> event = new nsSourceErrorEventRunner(this, aSourceElement);
    2699               0 :   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
    2700               0 : }
    2701                 : 
    2702               0 : void nsHTMLMediaElement::NotifyAddedSource()
    2703                 : {
    2704                 :   // If a source element is inserted as a child of a media element
    2705                 :   // that has no src attribute and whose networkState has the value
    2706                 :   // NETWORK_EMPTY, the user agent must invoke the media element's
    2707                 :   // resource selection algorithm.
    2708               0 :   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
    2709                 :       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY)
    2710                 :   {
    2711               0 :     QueueSelectResourceTask();
    2712                 :   }
    2713                 : 
    2714                 :   // A load was paused in the resource selection algorithm, waiting for
    2715                 :   // a new source child to be added, resume the resource selction algorithm.
    2716               0 :   if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
    2717               0 :     QueueLoadFromSourceTask();
    2718                 :   }
    2719               0 : }
    2720                 : 
    2721               0 : nsIContent* nsHTMLMediaElement::GetNextSource()
    2722                 : {
    2723               0 :   nsCOMPtr<nsIDOMNode> thisDomNode = do_QueryObject(this);
    2724                 : 
    2725               0 :   mSourceLoadCandidate = nsnull;
    2726                 : 
    2727               0 :   nsresult rv = NS_OK;
    2728               0 :   if (!mSourcePointer) {
    2729                 :     // First time this has been run, create a selection to cover children.
    2730               0 :     mSourcePointer = new nsRange();
    2731                 : 
    2732               0 :     rv = mSourcePointer->SelectNodeContents(thisDomNode);
    2733               0 :     if (NS_FAILED(rv)) return nsnull;
    2734                 : 
    2735               0 :     rv = mSourcePointer->Collapse(true);
    2736               0 :     if (NS_FAILED(rv)) return nsnull;
    2737                 :   }
    2738                 : 
    2739               0 :   while (true) {
    2740                 : #ifdef DEBUG
    2741               0 :     nsCOMPtr<nsIDOMNode> startContainer;
    2742               0 :     rv = mSourcePointer->GetStartContainer(getter_AddRefs(startContainer));
    2743               0 :     if (NS_FAILED(rv)) return nsnull;
    2744               0 :     NS_ASSERTION(startContainer == thisDomNode,
    2745                 :                 "Should only iterate over direct children");
    2746                 : #endif
    2747                 : 
    2748               0 :     PRInt32 startOffset = 0;
    2749               0 :     rv = mSourcePointer->GetStartOffset(&startOffset);
    2750               0 :     NS_ENSURE_SUCCESS(rv, nsnull);
    2751                 : 
    2752               0 :     if (PRUint32(startOffset) == GetChildCount())
    2753               0 :       return nsnull; // No more children.
    2754                 : 
    2755                 :     // Advance the range to the next child.
    2756               0 :     rv = mSourcePointer->SetStart(thisDomNode, startOffset + 1);
    2757               0 :     NS_ENSURE_SUCCESS(rv, nsnull);
    2758                 : 
    2759               0 :     nsIContent* child = GetChildAt(startOffset);
    2760                 : 
    2761                 :     // If child is a <source> element, it is the next candidate.
    2762               0 :     if (child && child->IsHTML(nsGkAtoms::source)) {
    2763               0 :       mSourceLoadCandidate = child;
    2764               0 :       return child;
    2765                 :     }
    2766                 :   }
    2767                 :   NS_NOTREACHED("Execution should not reach here!");
    2768                 :   return nsnull;
    2769                 : }
    2770                 : 
    2771               0 : void nsHTMLMediaElement::ChangeDelayLoadStatus(bool aDelay)
    2772                 : {
    2773               0 :   if (mDelayingLoadEvent == aDelay)
    2774               0 :     return;
    2775                 : 
    2776               0 :   mDelayingLoadEvent = aDelay;
    2777                 : 
    2778               0 :   if (aDelay) {
    2779               0 :     mLoadBlockedDoc = OwnerDoc();
    2780               0 :     mLoadBlockedDoc->BlockOnload();
    2781               0 :     LOG(PR_LOG_DEBUG, ("%p ChangeDelayLoadStatus(%d) doc=0x%p", this, aDelay, mLoadBlockedDoc.get()));
    2782                 :   } else {
    2783               0 :     if (mDecoder) {
    2784               0 :       mDecoder->MoveLoadsToBackground();
    2785                 :     }
    2786               0 :     LOG(PR_LOG_DEBUG, ("%p ChangeDelayLoadStatus(%d) doc=0x%p", this, aDelay, mLoadBlockedDoc.get()));
    2787                 :     // mLoadBlockedDoc might be null due to GC unlinking
    2788               0 :     if (mLoadBlockedDoc) {
    2789               0 :       mLoadBlockedDoc->UnblockOnload(false);
    2790               0 :       mLoadBlockedDoc = nsnull;
    2791                 :     }
    2792                 :   }
    2793                 : 
    2794                 :   // We changed mDelayingLoadEvent which can affect AddRemoveSelfReference
    2795               0 :   AddRemoveSelfReference();
    2796                 : }
    2797                 : 
    2798               0 : already_AddRefed<nsILoadGroup> nsHTMLMediaElement::GetDocumentLoadGroup()
    2799                 : {
    2800               0 :   if (!OwnerDoc()->IsActive()) {
    2801               0 :     NS_WARNING("Load group requested for media element in inactive document.");
    2802                 :   }
    2803               0 :   return OwnerDoc()->GetDocumentLoadGroup();
    2804                 : }
    2805                 : 
    2806                 : nsresult
    2807               0 : nsHTMLMediaElement::CopyInnerTo(nsGenericElement* aDest) const
    2808                 : {
    2809               0 :   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
    2810               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2811               0 :   if (aDest->OwnerDoc()->IsStaticDocument()) {
    2812               0 :     nsHTMLMediaElement* dest = static_cast<nsHTMLMediaElement*>(aDest);
    2813               0 :     if (mPrintSurface) {
    2814               0 :       dest->mPrintSurface = mPrintSurface;
    2815               0 :       dest->mMediaSize = mMediaSize;
    2816                 :     } else {
    2817               0 :       nsIFrame* frame = GetPrimaryFrame();
    2818                 :       Element* element;
    2819               0 :       if (frame && frame->GetType() == nsGkAtoms::HTMLVideoFrame &&
    2820               0 :           static_cast<nsVideoFrame*>(frame)->ShouldDisplayPoster()) {
    2821               0 :         nsIContent* content = static_cast<nsVideoFrame*>(frame)->GetPosterImage();
    2822               0 :         element = content ? content->AsElement() : NULL;
    2823                 :       } else {
    2824               0 :         element = const_cast<nsHTMLMediaElement*>(this);
    2825                 :       }
    2826                 : 
    2827                 :       nsLayoutUtils::SurfaceFromElementResult res =
    2828                 :         nsLayoutUtils::SurfaceFromElement(element,
    2829               0 :                                           nsLayoutUtils::SFE_WANT_NEW_SURFACE);
    2830               0 :       dest->mPrintSurface = res.mSurface;
    2831               0 :       dest->mMediaSize = nsIntSize(res.mSize.width, res.mSize.height);
    2832                 :     }
    2833                 :   }
    2834               0 :   return rv;
    2835                 : }
    2836                 : 
    2837               0 : nsresult nsHTMLMediaElement::GetBuffered(nsIDOMTimeRanges** aBuffered)
    2838                 : {
    2839               0 :   nsRefPtr<nsTimeRanges> ranges = new nsTimeRanges();
    2840               0 :   if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && mDecoder) {
    2841                 :     // If GetBuffered fails we ignore the error result and just return the
    2842                 :     // time ranges we found up till the error.
    2843               0 :     mDecoder->GetBuffered(ranges);
    2844                 :   }
    2845               0 :   ranges.forget(aBuffered);
    2846               0 :   return NS_OK;
    2847                 : }
    2848                 : 
    2849               0 : void nsHTMLMediaElement::SetRequestHeaders(nsIHttpChannel* aChannel)
    2850                 : {
    2851                 :   // Send Accept header for video and audio types only (Bug 489071)
    2852               0 :   SetAcceptHeader(aChannel);
    2853                 : 
    2854                 :   // Apache doesn't send Content-Length when gzip transfer encoding is used,
    2855                 :   // which prevents us from estimating the video length (if explicit Content-Duration
    2856                 :   // and a length spec in the container are not present either) and from seeking.
    2857                 :   // So, disable the standard "Accept-Encoding: gzip,deflate" that we usually send.
    2858                 :   // See bug 614760.
    2859               0 :   aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept-Encoding"),
    2860               0 :                              EmptyCString(), false);
    2861                 : 
    2862                 :   // Set the Referer header
    2863               0 :   aChannel->SetReferrer(OwnerDoc()->GetDocumentURI());
    2864               0 : }
    2865                 : 
    2866               0 : void nsHTMLMediaElement::FireTimeUpdate(bool aPeriodic)
    2867                 : {
    2868               0 :   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
    2869                 : 
    2870               0 :   TimeStamp now = TimeStamp::Now();
    2871               0 :   double time = 0;
    2872               0 :   GetCurrentTime(&time);
    2873                 : 
    2874                 :   // Fire a timupdate event if this is not a periodic update (i.e. it's a
    2875                 :   // timeupdate event mandated by the spec), or if it's a periodic update
    2876                 :   // and TIMEUPDATE_MS has passed since the last timeupdate event fired and
    2877                 :   // the time has changed.
    2878               0 :   if (!aPeriodic ||
    2879                 :       (mLastCurrentTime != time &&
    2880               0 :        (mTimeUpdateTime.IsNull() ||
    2881               0 :         now - mTimeUpdateTime >= TimeDuration::FromMilliseconds(TIMEUPDATE_MS)))) {
    2882               0 :     DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
    2883               0 :     mTimeUpdateTime = now;
    2884               0 :     mLastCurrentTime = time;
    2885                 :   }
    2886               0 :   if (mFragmentEnd >= 0.0 && time >= mFragmentEnd) {
    2887               0 :     Pause();
    2888               0 :     mFragmentEnd = -1.0;
    2889               0 :     mFragmentStart = -1.0;
    2890               0 :     mDecoder->SetEndTime(mFragmentEnd);
    2891                 :   }
    2892               0 : }
    2893                 : 
    2894               0 : void nsHTMLMediaElement::GetCurrentSpec(nsCString& aString)
    2895                 : {
    2896               0 :   if (mLoadingSrc) {
    2897               0 :     mLoadingSrc->GetSpec(aString);
    2898                 :   } else {
    2899               0 :     aString.Truncate();
    2900                 :   }
    2901               0 : }
    2902                 : 
    2903                 : /* attribute double initialTime; */
    2904               0 : NS_IMETHODIMP nsHTMLMediaElement::GetInitialTime(double *aTime)
    2905                 : {
    2906                 :   // If there is no start fragment then the initalTime is zero.
    2907                 :   // Clamp to duration if it is greater than duration.
    2908               0 :   double duration = 0.0;
    2909               0 :   nsresult rv = GetDuration(&duration);
    2910               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2911                 : 
    2912               0 :   *aTime = mFragmentStart < 0.0 ? 0.0 : mFragmentStart;
    2913               0 :   if (*aTime > duration) {
    2914               0 :     *aTime = duration;
    2915                 :   }
    2916               0 :   return NS_OK;
    2917                 : }
    2918                 : 
    2919                 : /* attribute double mozFragmentEnd; */
    2920               0 : NS_IMETHODIMP nsHTMLMediaElement::GetMozFragmentEnd(double *aTime)
    2921                 : {
    2922               0 :   double duration = 0.0;
    2923               0 :   nsresult rv = GetDuration(&duration);
    2924               0 :   NS_ENSURE_SUCCESS(rv, rv);
    2925                 : 
    2926                 :   // If there is no end fragment, or the fragment end is greater than the
    2927                 :   // duration, return the duration.
    2928               0 :   *aTime = (mFragmentEnd < 0.0 || mFragmentEnd > duration) ? duration : mFragmentEnd;
    2929               0 :   return NS_OK;
    2930                 : }
    2931                 : 
    2932               0 : void nsHTMLMediaElement::NotifyAudioAvailableListener()
    2933                 : {
    2934               0 :   if (mDecoder) {
    2935               0 :     mDecoder->NotifyAudioAvailableListener();
    2936                 :   }
    2937            4392 : }

Generated by: LCOV version 1.7