2008-07-09 08:22:20 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: ML 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is the Mozilla Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2007
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Chris Double <chris.double@double.co.nz>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "nsIDOMHTMLMediaElement.h"
|
|
|
|
#include "nsIDOMHTMLSourceElement.h"
|
|
|
|
#include "nsHTMLMediaElement.h"
|
|
|
|
#include "nsGenericHTMLElement.h"
|
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsSize.h"
|
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsDOMError.h"
|
|
|
|
#include "nsNodeInfoManager.h"
|
|
|
|
#include "plbase64.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "prmem.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsXPCOMStrings.h"
|
|
|
|
#include "prlock.h"
|
|
|
|
#include "nsThreadUtils.h"
|
2008-12-17 02:11:07 +00:00
|
|
|
#include "nsContentUtils.h"
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
#include "nsIScriptSecurityManager.h"
|
|
|
|
#include "nsIXPConnect.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
|
|
|
|
#include "nsIRenderingContext.h"
|
|
|
|
#include "nsITimer.h"
|
|
|
|
|
|
|
|
#include "nsEventDispatcher.h"
|
|
|
|
#include "nsIDOMDocumentEvent.h"
|
|
|
|
#include "nsIDOMProgressEvent.h"
|
|
|
|
#include "nsHTMLMediaError.h"
|
2008-10-30 05:20:08 +00:00
|
|
|
#include "nsICategoryManager.h"
|
2008-12-17 02:11:07 +00:00
|
|
|
#include "nsCommaSeparatedTokenizer.h"
|
2008-07-09 08:22:20 +00:00
|
|
|
|
2009-01-24 11:00:17 +00:00
|
|
|
#include "nsIContentPolicy.h"
|
|
|
|
#include "nsContentPolicyUtils.h"
|
|
|
|
#include "nsContentErrors.h"
|
|
|
|
#include "nsCrossSiteListenerProxy.h"
|
|
|
|
|
2008-07-30 06:50:14 +00:00
|
|
|
#ifdef MOZ_OGG
|
|
|
|
#include "nsOggDecoder.h"
|
|
|
|
#endif
|
2008-11-06 20:53:20 +00:00
|
|
|
#ifdef MOZ_WAVE
|
|
|
|
#include "nsWaveDecoder.h"
|
|
|
|
#endif
|
2008-07-30 06:50:14 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
|
|
|
|
class nsMediaEvent : public nsRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
nsMediaEvent(nsHTMLMediaElement* aElement) :
|
|
|
|
mElement(aElement),
|
|
|
|
mCurrentLoad(mElement->GetCurrentMediaLoad()) {}
|
|
|
|
~nsMediaEvent() {}
|
|
|
|
|
|
|
|
NS_IMETHOD Run() = 0;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
PRBool IsCancelled() {
|
|
|
|
return mElement->GetCurrentMediaLoad() != mCurrentLoad;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsHTMLMediaElement> mElement;
|
|
|
|
nsRefPtr<nsMediaLoad> mCurrentLoad;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class nsAsyncEventRunner : public nsMediaEvent
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
nsString mName;
|
|
|
|
PRPackedBool mProgress;
|
|
|
|
|
|
|
|
public:
|
|
|
|
nsAsyncEventRunner(const nsAString& aName, nsHTMLMediaElement* aElement, PRBool aProgress) :
|
2009-02-20 02:49:00 +00:00
|
|
|
nsMediaEvent(aElement), mName(aName), mProgress(aProgress)
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD Run() {
|
2009-02-20 02:49:00 +00:00
|
|
|
// Silently cancel if our load has been cancelled.
|
|
|
|
if (IsCancelled())
|
|
|
|
return NS_OK;
|
2008-07-09 08:22:20 +00:00
|
|
|
return mProgress ?
|
|
|
|
mElement->DispatchProgressEvent(mName) :
|
|
|
|
mElement->DispatchSimpleEvent(mName);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
class nsMediaLoadEvent : public nsMediaEvent {
|
2009-02-16 01:05:28 +00:00
|
|
|
public:
|
2009-02-20 02:49:00 +00:00
|
|
|
nsMediaLoadEvent(nsHTMLMediaElement *aElement)
|
|
|
|
: nsMediaEvent(aElement) {}
|
2009-02-16 01:05:28 +00:00
|
|
|
NS_IMETHOD Run() {
|
2009-02-20 02:49:00 +00:00
|
|
|
if (!IsCancelled())
|
|
|
|
mElement->Load();
|
2009-02-16 01:05:28 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2009-02-20 02:49:00 +00:00
|
|
|
};
|
2009-02-16 01:05:28 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
class nsHTMLMediaElement::LoadNextCandidateEvent : public nsMediaEvent {
|
|
|
|
public:
|
|
|
|
LoadNextCandidateEvent(nsHTMLMediaElement *aElement)
|
|
|
|
: nsMediaEvent(aElement) {}
|
|
|
|
NS_IMETHOD Run() {
|
|
|
|
if (!IsCancelled())
|
|
|
|
mElement->LoadNextCandidate();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2009-02-16 01:05:28 +00:00
|
|
|
};
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
void nsHTMLMediaElement::QueueLoadTask()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> event = new nsMediaLoadEvent(this);
|
|
|
|
NS_DispatchToMainThread(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::QueueLoadNextCandidateTask()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> event = new LoadNextCandidateEvent(this);
|
|
|
|
NS_DispatchToMainThread(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener
|
2008-12-16 03:32:03 +00:00
|
|
|
{
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_DECL_NSIREQUESTOBSERVER
|
|
|
|
NS_DECL_NSISTREAMLISTENER
|
|
|
|
|
|
|
|
public:
|
2009-02-20 02:49:00 +00:00
|
|
|
MediaLoadListener(nsHTMLMediaElement* aElement)
|
2008-12-16 03:32:03 +00:00
|
|
|
: mElement(aElement)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(mElement, "Must pass an element to call back");
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsRefPtr<nsHTMLMediaElement> mElement;
|
|
|
|
nsCOMPtr<nsIStreamListener> mNextListener;
|
|
|
|
};
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
NS_IMPL_ISUPPORTS2(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver, nsIStreamListener)
|
2008-12-16 03:32:03 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
2008-12-16 03:32:03 +00:00
|
|
|
{
|
2009-02-15 16:26:32 +00:00
|
|
|
nsresult rv = NS_OK;
|
2008-12-16 03:32:03 +00:00
|
|
|
|
|
|
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
|
|
|
|
if (channel &&
|
|
|
|
mElement &&
|
2009-02-15 16:26:32 +00:00
|
|
|
NS_SUCCEEDED(rv = mElement->InitializeDecoderForChannel(channel, getter_AddRefs(mNextListener))) &&
|
2008-12-16 03:32:03 +00:00
|
|
|
mNextListener) {
|
|
|
|
rv = mNextListener->OnStartRequest(aRequest, aContext);
|
|
|
|
} else {
|
2009-02-15 16:26:32 +00:00
|
|
|
// If InitializeDecoderForChannel() returned an error, fire a network
|
|
|
|
// error.
|
|
|
|
if (NS_FAILED(rv) && !mNextListener && mElement) {
|
2009-02-20 02:49:00 +00:00
|
|
|
// Load failed, attempt to load the next candidate resource. If there
|
|
|
|
// are none, this will trigger a MEDIA_ERR_NONE_SUPPORTED error.
|
|
|
|
mElement->QueueLoadNextCandidateTask();
|
2009-02-15 16:26:32 +00:00
|
|
|
}
|
|
|
|
// If InitializeDecoderForChannel did not return a listener (but may
|
|
|
|
// have otherwise succeeded), we abort the connection since we aren't
|
|
|
|
// interested in keeping the channel alive ourselves.
|
2008-12-16 03:32:03 +00:00
|
|
|
rv = NS_BINDING_ABORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The element is only needed until we've had a chance to call
|
|
|
|
// InitializeDecoderForChannel.
|
|
|
|
mElement = nsnull;
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
|
2008-12-16 03:32:03 +00:00
|
|
|
nsresult aStatus)
|
|
|
|
{
|
|
|
|
if (mNextListener) {
|
|
|
|
return mNextListener->OnStopRequest(aRequest, aContext, aStatus);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
|
2008-12-16 03:32:03 +00:00
|
|
|
nsIInputStream* aStream, PRUint32 aOffset,
|
|
|
|
PRUint32 aCount)
|
|
|
|
{
|
|
|
|
NS_ABORT_IF_FALSE(mNextListener, "Must have a listener");
|
|
|
|
return mNextListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount);
|
|
|
|
}
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
void
|
|
|
|
nsMediaLoad::AddCandidate(nsIURI *aURI)
|
|
|
|
{
|
|
|
|
mCandidates.AppendObject(aURI);
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsIURI>
|
|
|
|
nsMediaLoad::GetNextCandidate()
|
|
|
|
{
|
|
|
|
if (mPosition == mCandidates.Count())
|
|
|
|
return nsnull;
|
|
|
|
nsCOMPtr<nsIURI> uri = mCandidates.ObjectAt(mPosition);
|
|
|
|
mPosition++;
|
|
|
|
return uri.forget();
|
|
|
|
}
|
|
|
|
|
2009-02-16 01:05:28 +00:00
|
|
|
NS_IMPL_ISUPPORTS0(nsMediaLoad)
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
// nsIDOMHTMLMediaElement
|
|
|
|
NS_IMPL_URI_ATTR(nsHTMLMediaElement, Src, src)
|
|
|
|
NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Controls, controls)
|
2008-07-30 04:55:27 +00:00
|
|
|
NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Autoplay, autoplay)
|
2008-07-09 08:22:20 +00:00
|
|
|
|
2009-02-20 04:05:07 +00:00
|
|
|
/* readonly attribute nsIDOMHTMLMediaElement mozAutoplayEnabled; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetMozAutoplayEnabled(PRBool *aAutoplayEnabled)
|
|
|
|
{
|
|
|
|
*aAutoplayEnabled = mAutoplayEnabled;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
/* readonly attribute nsIDOMHTMLMediaError error; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetError(nsIDOMHTMLMediaError * *aError)
|
|
|
|
{
|
|
|
|
NS_IF_ADDREF(*aError = mError);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-07-30 04:55:27 +00:00
|
|
|
/* readonly attribute boolean ended; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetEnded(PRBool *aEnded)
|
|
|
|
{
|
2008-12-14 18:02:54 +00:00
|
|
|
*aEnded = mDecoder ? mDecoder->IsEnded() : PR_FALSE;
|
2008-07-30 04:55:27 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
/* readonly attribute DOMString currentSrc; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetCurrentSrc(nsAString & aCurrentSrc)
|
|
|
|
{
|
|
|
|
nsCAutoString src;
|
|
|
|
|
|
|
|
if (mDecoder) {
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
mDecoder->GetCurrentURI(getter_AddRefs(uri));
|
|
|
|
if (uri) {
|
|
|
|
uri->GetSpec(src);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
aCurrentSrc = NS_ConvertUTF8toUTF16(src);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* readonly attribute unsigned short networkState; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetNetworkState(PRUint16 *aNetworkState)
|
|
|
|
{
|
2008-07-09 09:50:08 +00:00
|
|
|
*aNetworkState = mNetworkState;
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
PRBool nsHTMLMediaElement::AbortExistingLoads()
|
2008-10-30 05:20:08 +00:00
|
|
|
{
|
2009-02-20 02:49:00 +00:00
|
|
|
// Set a new load object. This will cause events which were enqueued
|
|
|
|
// with a differnet load object to silently be cancelled.
|
|
|
|
mCurrentLoad = new nsMediaLoad();
|
|
|
|
nsRefPtr<nsMediaLoad> currentLoad = mCurrentLoad;
|
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
if (mDecoder) {
|
|
|
|
mDecoder->Shutdown();
|
|
|
|
mDecoder = nsnull;
|
2008-10-30 05:20:08 +00:00
|
|
|
}
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
if (mBegun) {
|
|
|
|
mBegun = PR_FALSE;
|
2009-01-24 11:00:17 +00:00
|
|
|
mError = new nsHTMLMediaError(nsIDOMHTMLMediaError::MEDIA_ERR_ABORTED);
|
2008-07-09 08:22:20 +00:00
|
|
|
DispatchProgressEvent(NS_LITERAL_STRING("abort"));
|
2009-02-20 02:49:00 +00:00
|
|
|
if (mCurrentLoad != currentLoad) {
|
|
|
|
// A new load was triggered in handler, bail out of this load.
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mError = nsnull;
|
|
|
|
mLoadedFirstFrame = PR_FALSE;
|
|
|
|
mAutoplaying = PR_TRUE;
|
|
|
|
|
2008-11-05 22:53:29 +00:00
|
|
|
// TODO: The playback rate must be set to the default playback rate.
|
2008-07-09 08:22:20 +00:00
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
|
|
|
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
2008-07-09 08:22:20 +00:00
|
|
|
mPaused = PR_TRUE;
|
|
|
|
// TODO: The current playback position must be set to 0.
|
|
|
|
// TODO: The currentLoop DOM attribute must be set to 0.
|
|
|
|
DispatchSimpleEvent(NS_LITERAL_STRING("emptied"));
|
2009-02-20 02:49:00 +00:00
|
|
|
if (mCurrentLoad != currentLoad) {
|
|
|
|
// A new load was triggered in handler, bail out of this load.
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
2008-12-16 03:32:03 +00:00
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2009-02-15 16:26:32 +00:00
|
|
|
void nsHTMLMediaElement::NoSupportedMediaError()
|
|
|
|
{
|
2009-02-20 02:49:00 +00:00
|
|
|
NS_ASSERTION(mCurrentLoad && !mCurrentLoad->HasMoreCandidates(),
|
|
|
|
"Should have exhausted all candidates");
|
2009-02-15 16:26:32 +00:00
|
|
|
mError = new nsHTMLMediaError(nsIDOMHTMLMediaError::MEDIA_ERR_NONE_SUPPORTED);
|
|
|
|
mBegun = PR_FALSE;
|
|
|
|
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
|
|
|
|
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
|
|
|
|
}
|
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
/* void load (); */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::Load()
|
|
|
|
{
|
|
|
|
if (AbortExistingLoads())
|
|
|
|
return NS_OK;
|
|
|
|
|
2009-02-15 16:26:32 +00:00
|
|
|
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
|
|
|
mBegun = PR_TRUE;
|
|
|
|
DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
GenerateCandidates();
|
2008-12-16 03:32:03 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
QueueLoadNextCandidateTask();
|
2008-12-16 03:32:03 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
2009-01-27 02:32:33 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
void nsHTMLMediaElement::LoadNextCandidate()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mCurrentLoad, "Need a load object");
|
|
|
|
|
|
|
|
while (mCurrentLoad->HasMoreCandidates()) {
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIURI> uri = mCurrentLoad->GetNextCandidate();
|
|
|
|
|
|
|
|
if (mChannel) {
|
|
|
|
mChannel->Cancel(NS_BINDING_ABORTED);
|
|
|
|
mChannel = nsnull;
|
2009-02-15 16:26:32 +00:00
|
|
|
}
|
2009-02-20 02:49:00 +00:00
|
|
|
|
|
|
|
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
|
|
|
|
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_MEDIA,
|
|
|
|
uri,
|
|
|
|
NodePrincipal(),
|
|
|
|
this,
|
|
|
|
EmptyCString(), // mime type
|
|
|
|
nsnull, // extra
|
|
|
|
&shouldLoad,
|
|
|
|
nsContentUtils::GetContentPolicy(),
|
|
|
|
nsContentUtils::GetSecurityManager());
|
|
|
|
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) continue;
|
|
|
|
|
|
|
|
rv = NS_NewChannel(getter_AddRefs(mChannel),
|
|
|
|
uri,
|
|
|
|
nsnull,
|
|
|
|
nsnull,
|
|
|
|
nsnull,
|
|
|
|
nsIRequest::LOAD_NORMAL);
|
|
|
|
if (NS_FAILED(rv)) continue;
|
|
|
|
|
|
|
|
// The listener holds a strong reference to us. This creates a reference
|
|
|
|
// cycle which is manually broken in the listener's OnStartRequest method
|
|
|
|
// after it is finished with the element.
|
|
|
|
nsCOMPtr<nsIStreamListener> loadListener = new MediaLoadListener(this);
|
|
|
|
if (!loadListener) continue;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIStreamListener> listener;
|
|
|
|
if (ShouldCheckAllowOrigin()) {
|
|
|
|
listener = new nsCrossSiteListenerProxy(loadListener,
|
|
|
|
NodePrincipal(),
|
|
|
|
mChannel,
|
|
|
|
PR_FALSE,
|
|
|
|
&rv);
|
|
|
|
if (!listener || NS_FAILED(rv)) continue;
|
|
|
|
} else {
|
|
|
|
rv = nsContentUtils::GetSecurityManager()->
|
|
|
|
CheckLoadURIWithPrincipal(NodePrincipal(),
|
|
|
|
uri,
|
|
|
|
nsIScriptSecurityManager::STANDARD);
|
|
|
|
if (NS_FAILED(rv)) continue;
|
|
|
|
listener = loadListener;
|
2009-02-15 16:26:32 +00:00
|
|
|
}
|
2008-12-16 03:32:03 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
|
|
|
|
if (hc) {
|
|
|
|
// Use a byte range request from the start of the resource.
|
|
|
|
// This enables us to detect if the stream supports byte range
|
|
|
|
// requests, and therefore seeking, early.
|
|
|
|
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
|
|
|
|
NS_LITERAL_CSTRING("bytes=0-"),
|
|
|
|
PR_FALSE);
|
|
|
|
}
|
2008-12-16 03:32:03 +00:00
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
rv = mChannel->AsyncOpen(listener, nsnull);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
// OnStartRequest is guaranteed to be called if the open succeeds. If
|
|
|
|
// the open failed, the listener's OnStartRequest will never be called,
|
|
|
|
// so we need to break the element->channel->listener->element reference
|
|
|
|
// cycle here. The channel holds the only reference to the listener,
|
|
|
|
// and is useless now anyway, so drop our reference to it to allow it to
|
|
|
|
// be destroyed.
|
|
|
|
mChannel = nsnull;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Else the channel must be open and starting to download. If it encounters
|
|
|
|
// a non-catestrophic failure, it will set a new task to continue loading
|
|
|
|
// the candidates.
|
|
|
|
return;
|
2008-12-16 03:32:03 +00:00
|
|
|
}
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
// Must have exhausted all candidates.
|
|
|
|
NoSupportedMediaError();
|
2008-12-16 03:32:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLMediaElement::LoadWithChannel(nsIChannel *aChannel,
|
|
|
|
nsIStreamListener **aListener)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aChannel);
|
|
|
|
NS_ENSURE_ARG_POINTER(aListener);
|
|
|
|
|
|
|
|
*aListener = nsnull;
|
|
|
|
|
|
|
|
if (AbortExistingLoads())
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult rv = InitializeDecoderForChannel(aChannel, aListener);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
mBegun = PR_TRUE;
|
|
|
|
|
|
|
|
DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* readonly attribute unsigned short readyState; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetReadyState(PRUint16 *aReadyState)
|
|
|
|
{
|
2008-07-09 09:50:08 +00:00
|
|
|
*aReadyState = mReadyState;
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* readonly attribute boolean seeking; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetSeeking(PRBool *aSeeking)
|
|
|
|
{
|
2008-10-19 07:39:21 +00:00
|
|
|
*aSeeking = mDecoder && mDecoder->IsSeeking();
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* attribute float currentTime; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetCurrentTime(float *aCurrentTime)
|
|
|
|
{
|
|
|
|
*aCurrentTime = mDecoder ? mDecoder->GetCurrentTime() : 0.0;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::SetCurrentTime(float aCurrentTime)
|
|
|
|
{
|
2008-10-19 07:39:21 +00:00
|
|
|
if (!mDecoder)
|
|
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
|
|
|
|
|
|
// Detect for a NaN and invalid values.
|
|
|
|
if (!(aCurrentTime >= 0.0))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING)
|
2008-10-19 07:39:21 +00:00
|
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
mPlayingBeforeSeek = IsPotentiallyPlaying();
|
2008-10-23 08:02:18 +00:00
|
|
|
// The media backend is responsible for dispatching the timeupdate
|
|
|
|
// event if it changes the playback position as a result of the seek.
|
2008-10-19 07:39:21 +00:00
|
|
|
nsresult rv = mDecoder->Seek(aCurrentTime);
|
|
|
|
return rv;
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* readonly attribute float duration; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetDuration(float *aDuration)
|
|
|
|
{
|
2008-07-09 09:50:08 +00:00
|
|
|
*aDuration = mDecoder ? mDecoder->GetDuration() : 0.0;
|
2008-07-09 08:22:20 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-10-28 21:54:17 +00:00
|
|
|
/* readonly attribute boolean paused; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetPaused(PRBool *aPaused)
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
2008-07-09 09:50:08 +00:00
|
|
|
*aPaused = mPaused;
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* void pause (); */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::Pause()
|
|
|
|
{
|
2008-12-15 03:38:16 +00:00
|
|
|
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
2009-01-08 08:44:38 +00:00
|
|
|
nsresult rv = Load();
|
2008-07-09 08:22:20 +00:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2009-01-08 08:44:38 +00:00
|
|
|
} else if (mDecoder) {
|
|
|
|
mDecoder->Pause();
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PRBool oldPaused = mPaused;
|
|
|
|
mPaused = PR_TRUE;
|
|
|
|
mAutoplaying = PR_FALSE;
|
|
|
|
|
|
|
|
if (!oldPaused) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("pause"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* attribute float volume; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetVolume(float *aVolume)
|
|
|
|
{
|
|
|
|
if (mMuted)
|
|
|
|
*aVolume = mMutedVolume;
|
|
|
|
else
|
|
|
|
*aVolume = mDecoder ? mDecoder->GetVolume() : 0.0;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::SetVolume(float aVolume)
|
|
|
|
{
|
2008-10-01 05:44:16 +00:00
|
|
|
if (aVolume < 0.0f || aVolume > 1.0f)
|
|
|
|
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
if (mMuted)
|
|
|
|
mMutedVolume = aVolume;
|
|
|
|
else {
|
|
|
|
if (mDecoder)
|
|
|
|
mDecoder->SetVolume(aVolume);
|
|
|
|
|
2009-02-07 21:43:43 +00:00
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("volumechange"));
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* attribute boolean muted; */
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::GetMuted(PRBool *aMuted)
|
|
|
|
{
|
2008-07-09 09:50:08 +00:00
|
|
|
*aMuted = mMuted;
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::SetMuted(PRBool aMuted)
|
|
|
|
{
|
|
|
|
PRBool oldMuted = mMuted;
|
|
|
|
|
|
|
|
if (mDecoder) {
|
|
|
|
if (mMuted && !aMuted) {
|
|
|
|
mDecoder->SetVolume(mMutedVolume);
|
|
|
|
}
|
|
|
|
else if (!mMuted && aMuted) {
|
|
|
|
mMutedVolume = mDecoder->GetVolume();
|
|
|
|
mDecoder->SetVolume(0.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mMuted = aMuted;
|
|
|
|
|
|
|
|
if (oldMuted != mMuted)
|
2009-02-07 21:43:43 +00:00
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("volumechange"));
|
2008-07-09 08:22:20 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsHTMLMediaElement::nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRBool aFromParser)
|
|
|
|
: nsGenericHTMLElement(aNodeInfo),
|
2008-12-15 03:38:16 +00:00
|
|
|
mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
|
|
|
|
mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
|
2008-09-29 23:05:06 +00:00
|
|
|
mMutedVolume(0.0),
|
2008-10-19 07:39:21 +00:00
|
|
|
mMediaSize(-1,-1),
|
2008-07-09 08:22:20 +00:00
|
|
|
mBegun(PR_FALSE),
|
|
|
|
mLoadedFirstFrame(PR_FALSE),
|
|
|
|
mAutoplaying(PR_TRUE),
|
2009-02-20 04:05:07 +00:00
|
|
|
mAutoplayEnabled(PR_TRUE),
|
2008-07-09 08:22:20 +00:00
|
|
|
mPaused(PR_TRUE),
|
|
|
|
mMuted(PR_FALSE),
|
2008-10-19 07:39:21 +00:00
|
|
|
mIsDoneAddingChildren(!aFromParser),
|
2009-02-11 01:43:45 +00:00
|
|
|
mPlayingBeforeSeek(PR_FALSE),
|
2009-02-16 01:05:28 +00:00
|
|
|
mWaitingFired(PR_FALSE),
|
|
|
|
mIsBindingToTree(PR_FALSE)
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
nsHTMLMediaElement::~nsHTMLMediaElement()
|
|
|
|
{
|
2008-10-19 07:39:21 +00:00
|
|
|
if (mDecoder) {
|
|
|
|
mDecoder->Shutdown();
|
|
|
|
mDecoder = nsnull;
|
|
|
|
}
|
2008-12-16 03:32:03 +00:00
|
|
|
if (mChannel) {
|
|
|
|
mChannel->Cancel(NS_BINDING_ABORTED);
|
|
|
|
mChannel = nsnull;
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
NS_IMETHODIMP nsHTMLMediaElement::Play()
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
2008-12-15 03:38:16 +00:00
|
|
|
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
2009-02-11 01:23:19 +00:00
|
|
|
nsresult rv = Load();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else if (mDecoder) {
|
|
|
|
if (mDecoder->IsEnded()) {
|
|
|
|
SetCurrentTime(0);
|
|
|
|
}
|
|
|
|
nsresult rv = mDecoder->Play();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2008-10-21 09:19:33 +00:00
|
|
|
}
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
// TODO: If the playback has ended, then the user agent must set
|
|
|
|
// currentLoop to zero and seek to the effective start.
|
2008-11-05 22:53:29 +00:00
|
|
|
// TODO: The playback rate must be set to the default playback rate.
|
2009-02-11 01:23:19 +00:00
|
|
|
if (mPaused) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("play"));
|
|
|
|
switch (mReadyState) {
|
|
|
|
case nsIDOMHTMLMediaElement::HAVE_METADATA:
|
|
|
|
case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
|
|
|
|
break;
|
|
|
|
case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
|
|
|
|
case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
mPaused = PR_FALSE;
|
|
|
|
mAutoplaying = PR_FALSE;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
PRBool nsHTMLMediaElement::ParseAttribute(PRInt32 aNamespaceID,
|
|
|
|
nsIAtom* aAttribute,
|
|
|
|
const nsAString& aValue,
|
|
|
|
nsAttrValue& aResult)
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
|
|
|
if (aNamespaceID == kNameSpaceID_None) {
|
|
|
|
if (aAttribute == nsGkAtoms::src) {
|
|
|
|
static const char* kWhitespace = " \n\r\t\b";
|
|
|
|
aResult.SetTo(nsContentUtils::TrimCharsInSet(kWhitespace, aValue));
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
2008-09-29 23:05:06 +00:00
|
|
|
else if (aAttribute == nsGkAtoms::loopstart
|
2008-07-30 04:55:27 +00:00
|
|
|
|| aAttribute == nsGkAtoms::loopend
|
|
|
|
|| aAttribute == nsGkAtoms::start
|
|
|
|
|| aAttribute == nsGkAtoms::end) {
|
2008-07-09 08:22:20 +00:00
|
|
|
return aResult.ParseFloatValue(aValue);
|
|
|
|
}
|
|
|
|
else if (ParseImageAttribute(aAttribute, aValue, aResult)) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
|
|
|
|
aResult);
|
|
|
|
}
|
2008-10-19 07:39:21 +00:00
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
nsresult nsHTMLMediaElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
|
|
|
nsIAtom* aPrefix, const nsAString& aValue,
|
|
|
|
PRBool aNotify)
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
|
|
|
nsresult rv =
|
|
|
|
nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
|
|
|
|
aNotify);
|
|
|
|
if (aNotify && aNameSpaceID == kNameSpaceID_None) {
|
2009-02-15 16:26:32 +00:00
|
|
|
if (aName == nsGkAtoms::src &&
|
|
|
|
IsInDoc() &&
|
|
|
|
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
2008-07-09 08:22:20 +00:00
|
|
|
Load();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2009-02-20 04:05:07 +00:00
|
|
|
static PRBool IsAutoplayEnabled()
|
|
|
|
{
|
|
|
|
return nsContentUtils::GetBoolPref("media.autoplay.enabled");
|
|
|
|
}
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
nsresult nsHTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
|
|
|
nsIContent* aBindingParent,
|
|
|
|
PRBool aCompileEventHandlers)
|
|
|
|
{
|
2009-02-16 01:05:28 +00:00
|
|
|
mIsBindingToTree = PR_TRUE;
|
2009-02-20 04:05:07 +00:00
|
|
|
mAutoplayEnabled = IsAutoplayEnabled();
|
2008-07-09 08:22:20 +00:00
|
|
|
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument,
|
|
|
|
aParent,
|
|
|
|
aBindingParent,
|
|
|
|
aCompileEventHandlers);
|
2009-02-16 01:05:28 +00:00
|
|
|
if (NS_SUCCEEDED(rv) &&
|
|
|
|
mIsDoneAddingChildren &&
|
2008-12-15 03:38:16 +00:00
|
|
|
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
2009-02-16 01:05:28 +00:00
|
|
|
QueueLoadTask();
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
2009-02-16 01:05:28 +00:00
|
|
|
mIsBindingToTree = PR_FALSE;
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::UnbindFromTree(PRBool aDeep,
|
|
|
|
PRBool aNullParent)
|
|
|
|
{
|
2008-12-15 03:38:16 +00:00
|
|
|
if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY)
|
2008-08-04 01:51:01 +00:00
|
|
|
Pause();
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
|
|
|
}
|
|
|
|
|
2008-10-30 05:20:08 +00:00
|
|
|
#ifdef MOZ_OGG
|
2008-12-17 02:11:07 +00:00
|
|
|
// See http://www.rfc-editor.org/rfc/rfc5334.txt for the definitions
|
|
|
|
// of Ogg media types and codec types
|
2008-10-30 05:20:08 +00:00
|
|
|
static const char gOggTypes[][16] = {
|
|
|
|
"video/ogg",
|
|
|
|
"audio/ogg",
|
|
|
|
"application/ogg"
|
|
|
|
};
|
|
|
|
|
2008-12-17 02:11:07 +00:00
|
|
|
static const char* gOggCodecs[] = {
|
|
|
|
"vorbis",
|
|
|
|
"theora",
|
|
|
|
nsnull
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char* gOggMaybeCodecs[] = {
|
|
|
|
nsnull
|
|
|
|
};
|
|
|
|
|
2008-12-12 08:17:57 +00:00
|
|
|
static PRBool IsOggEnabled()
|
|
|
|
{
|
|
|
|
return nsContentUtils::GetBoolPref("media.ogg.enabled");
|
|
|
|
}
|
|
|
|
|
2008-10-30 05:20:08 +00:00
|
|
|
static PRBool IsOggType(const nsACString& aType)
|
|
|
|
{
|
2008-12-12 08:17:57 +00:00
|
|
|
if (!IsOggEnabled())
|
|
|
|
return PR_FALSE;
|
2008-10-30 05:20:08 +00:00
|
|
|
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gOggTypes); ++i) {
|
|
|
|
if (aType.EqualsASCII(gOggTypes[i]))
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-11-06 20:53:20 +00:00
|
|
|
#ifdef MOZ_WAVE
|
2008-12-17 02:11:07 +00:00
|
|
|
// See http://www.rfc-editor.org/rfc/rfc2361.txt for the definitions
|
|
|
|
// of WAVE media types and codec types. However, the audio/vnd.wave
|
|
|
|
// MIME type described there is not used.
|
2008-11-06 20:53:20 +00:00
|
|
|
static const char gWaveTypes[][16] = {
|
|
|
|
"audio/x-wav",
|
|
|
|
"audio/wav",
|
|
|
|
"audio/wave",
|
|
|
|
"audio/x-pn-wav"
|
|
|
|
};
|
|
|
|
|
2008-12-17 02:11:07 +00:00
|
|
|
static const char* gWaveCodecs[] = {
|
|
|
|
"1", // Microsoft PCM Format
|
|
|
|
nsnull
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char* gWaveMaybeCodecs[] = {
|
|
|
|
"0", // Microsoft Unknown Wave Format
|
|
|
|
nsnull
|
|
|
|
};
|
|
|
|
|
2008-12-12 08:17:57 +00:00
|
|
|
static PRBool IsWaveEnabled()
|
|
|
|
{
|
|
|
|
return nsContentUtils::GetBoolPref("media.wave.enabled");
|
|
|
|
}
|
|
|
|
|
2008-11-06 20:53:20 +00:00
|
|
|
static PRBool IsWaveType(const nsACString& aType)
|
|
|
|
{
|
2008-12-12 08:17:57 +00:00
|
|
|
if (!IsWaveEnabled())
|
|
|
|
return PR_FALSE;
|
2008-11-06 20:53:20 +00:00
|
|
|
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gWaveTypes); ++i) {
|
|
|
|
if (aType.EqualsASCII(gWaveTypes[i]))
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-30 05:20:08 +00:00
|
|
|
/* static */
|
2008-12-17 02:11:07 +00:00
|
|
|
PRBool nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType,
|
|
|
|
const char*** aCodecList,
|
|
|
|
const char*** aMaybeCodecList)
|
2008-10-30 05:20:08 +00:00
|
|
|
{
|
|
|
|
#ifdef MOZ_OGG
|
2008-12-17 02:11:07 +00:00
|
|
|
if (IsOggType(nsDependentCString(aMIMEType))) {
|
|
|
|
*aCodecList = gOggCodecs;
|
|
|
|
*aMaybeCodecList = gOggMaybeCodecs;
|
2008-10-30 05:20:08 +00:00
|
|
|
return PR_TRUE;
|
2008-12-17 02:11:07 +00:00
|
|
|
}
|
2008-11-06 20:53:20 +00:00
|
|
|
#endif
|
|
|
|
#ifdef MOZ_WAVE
|
2008-12-17 02:11:07 +00:00
|
|
|
if (IsWaveType(nsDependentCString(aMIMEType))) {
|
|
|
|
*aCodecList = gWaveCodecs;
|
|
|
|
*aMaybeCodecList = gWaveMaybeCodecs;
|
2008-11-06 20:53:20 +00:00
|
|
|
return PR_TRUE;
|
2008-12-17 02:11:07 +00:00
|
|
|
}
|
2008-10-30 05:20:08 +00:00
|
|
|
#endif
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2008-12-17 02:11:07 +00:00
|
|
|
static PRBool
|
|
|
|
CodecListContains(const char** aCodecs, const nsAString& aCodec)
|
|
|
|
{
|
|
|
|
for (PRInt32 i = 0; aCodecs[i]; ++i) {
|
|
|
|
if (aCodec.EqualsASCII(aCodecs[i]))
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum CanPlayStatus {
|
|
|
|
CANPLAY_NO,
|
|
|
|
CANPLAY_MAYBE,
|
|
|
|
CANPLAY_YES
|
|
|
|
};
|
|
|
|
|
|
|
|
static CanPlayStatus GetCanPlay(const nsAString& aType)
|
|
|
|
{
|
|
|
|
nsContentTypeParser parser(aType);
|
|
|
|
nsAutoString mimeType;
|
|
|
|
nsresult rv = parser.GetType(mimeType);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return CANPLAY_NO;
|
|
|
|
|
|
|
|
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
|
|
|
|
const char** supportedCodecs;
|
|
|
|
const char** maybeSupportedCodecs;
|
|
|
|
if (!nsHTMLMediaElement::CanHandleMediaType(mimeTypeUTF8.get(),
|
|
|
|
&supportedCodecs, &maybeSupportedCodecs))
|
|
|
|
return CANPLAY_NO;
|
|
|
|
|
|
|
|
nsAutoString codecs;
|
|
|
|
rv = parser.GetParameter("codecs", codecs);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
// Parameter not found or whatever
|
|
|
|
return CANPLAY_MAYBE;
|
|
|
|
|
|
|
|
CanPlayStatus result = CANPLAY_YES;
|
|
|
|
// See http://www.rfc-editor.org/rfc/rfc4281.txt for the description
|
|
|
|
// of the 'codecs' parameter
|
|
|
|
nsCommaSeparatedTokenizer tokenizer(codecs);
|
|
|
|
PRBool expectMoreTokens = PR_FALSE;
|
|
|
|
while (tokenizer.hasMoreTokens()) {
|
|
|
|
const nsSubstring& token = tokenizer.nextToken();
|
|
|
|
|
|
|
|
if (CodecListContains(maybeSupportedCodecs, token)) {
|
|
|
|
result = CANPLAY_MAYBE;
|
|
|
|
} else if (!CodecListContains(supportedCodecs, token)) {
|
|
|
|
// Totally unsupported codec
|
|
|
|
return CANPLAY_NO;
|
|
|
|
}
|
|
|
|
expectMoreTokens = tokenizer.lastTokenEndedWithComma();
|
|
|
|
}
|
|
|
|
if (expectMoreTokens) {
|
|
|
|
// Last codec name was empty
|
|
|
|
return CANPLAY_NO;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsHTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
|
|
|
|
{
|
|
|
|
switch (GetCanPlay(aType)) {
|
|
|
|
case CANPLAY_NO: aResult.AssignLiteral("no"); break;
|
|
|
|
case CANPLAY_YES: aResult.AssignLiteral("probably"); break;
|
|
|
|
default:
|
|
|
|
case CANPLAY_MAYBE: aResult.AssignLiteral("maybe"); break;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-10-30 05:20:08 +00:00
|
|
|
/* static */
|
|
|
|
void nsHTMLMediaElement::InitMediaTypes()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
#ifdef MOZ_OGG
|
2008-12-12 08:17:57 +00:00
|
|
|
if (IsOggEnabled()) {
|
|
|
|
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gOggTypes); i++) {
|
|
|
|
catMan->AddCategoryEntry("Gecko-Content-Viewers", gOggTypes[i],
|
|
|
|
"@mozilla.org/content/document-loader-factory;1",
|
|
|
|
PR_FALSE, PR_TRUE, nsnull);
|
|
|
|
}
|
2008-10-30 05:20:08 +00:00
|
|
|
}
|
2008-11-06 20:53:20 +00:00
|
|
|
#endif
|
|
|
|
#ifdef MOZ_WAVE
|
2008-12-12 08:17:57 +00:00
|
|
|
if (IsWaveEnabled()) {
|
|
|
|
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gWaveTypes); i++) {
|
|
|
|
catMan->AddCategoryEntry("Gecko-Content-Viewers", gWaveTypes[i],
|
|
|
|
"@mozilla.org/content/document-loader-factory;1",
|
|
|
|
PR_FALSE, PR_TRUE, nsnull);
|
|
|
|
}
|
2008-11-06 20:53:20 +00:00
|
|
|
}
|
2008-10-30 05:20:08 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
void nsHTMLMediaElement::ShutdownMediaTypes()
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsICategoryManager> catMan(do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
#ifdef MOZ_OGG
|
|
|
|
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gOggTypes); i++) {
|
|
|
|
catMan->DeleteCategoryEntry("Gecko-Content-Viewers", gOggTypes[i], PR_FALSE);
|
|
|
|
}
|
2008-11-06 20:53:20 +00:00
|
|
|
#endif
|
|
|
|
#ifdef MOZ_WAVE
|
|
|
|
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(gWaveTypes); i++) {
|
|
|
|
catMan->DeleteCategoryEntry("Gecko-Content-Viewers", gWaveTypes[i], PR_FALSE);
|
|
|
|
}
|
2008-10-30 05:20:08 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool nsHTMLMediaElement::CreateDecoder(const nsACString& aType)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_OGG
|
|
|
|
if (IsOggType(aType)) {
|
|
|
|
mDecoder = new nsOggDecoder();
|
2009-01-16 07:57:37 +00:00
|
|
|
if (mDecoder && !mDecoder->Init(this)) {
|
2008-10-30 05:20:08 +00:00
|
|
|
mDecoder = nsnull;
|
|
|
|
}
|
|
|
|
}
|
2008-11-06 20:53:20 +00:00
|
|
|
#endif
|
|
|
|
#ifdef MOZ_WAVE
|
|
|
|
if (IsWaveType(aType)) {
|
|
|
|
mDecoder = new nsWaveDecoder();
|
2009-01-16 07:57:37 +00:00
|
|
|
if (mDecoder && !mDecoder->Init(this)) {
|
2008-11-06 20:53:20 +00:00
|
|
|
mDecoder = nsnull;
|
|
|
|
}
|
|
|
|
}
|
2008-10-30 05:20:08 +00:00
|
|
|
#endif
|
|
|
|
return mDecoder != nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel,
|
|
|
|
nsIStreamListener **aListener)
|
|
|
|
{
|
|
|
|
nsCAutoString mimeType;
|
|
|
|
aChannel->GetContentType(mimeType);
|
|
|
|
|
|
|
|
if (!CreateDecoder(mimeType))
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
|
2008-12-16 03:32:03 +00:00
|
|
|
|
2009-02-11 01:23:19 +00:00
|
|
|
nsresult rv = mDecoder->Load(nsnull, aChannel, aListener);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
if (!mPaused) {
|
|
|
|
rv = mDecoder->Play();
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
2008-10-30 05:20:08 +00:00
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
nsresult nsHTMLMediaElement::NewURIFromString(const nsAutoString& aURISpec, nsIURI** aURI)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
|
|
|
|
|
|
*aURI = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> doc = GetOwnerDoc();
|
|
|
|
if (!doc) {
|
|
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> baseURI = GetBaseURI();
|
|
|
|
nsresult rv = nsContentUtils::NewURIWithDocumentCharset(aURI,
|
|
|
|
aURISpec,
|
|
|
|
doc,
|
|
|
|
baseURI);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRBool equal;
|
|
|
|
if (aURISpec.IsEmpty() &&
|
|
|
|
doc->GetDocumentURI() &&
|
|
|
|
NS_SUCCEEDED(doc->GetDocumentURI()->Equals(*aURI, &equal)) &&
|
|
|
|
equal) {
|
|
|
|
// It's not possible for a media resource to be embedded in the current
|
|
|
|
// document we extracted aURISpec from, so there's no point returning
|
|
|
|
// the current document URI just to let the caller attempt and fail to
|
|
|
|
// decode it.
|
|
|
|
NS_RELEASE(*aURI);
|
|
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-02-20 02:49:00 +00:00
|
|
|
// Implements 'generate the list of potential media resources' as per:
|
|
|
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#generate-the-list-of-potential-media-resources
|
|
|
|
void nsHTMLMediaElement::GenerateCandidates()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mCurrentLoad, "Need a nsMediaLoad Object");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
nsresult res;
|
|
|
|
nsAutoString src;
|
|
|
|
|
|
|
|
// If we have a src attribute on the media element, just use that only.
|
|
|
|
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
|
|
|
|
res = NewURIFromString(src, getter_AddRefs(uri));
|
|
|
|
if (NS_SUCCEEDED(res)) {
|
|
|
|
mCurrentLoad->AddCandidate(uri);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 count = GetChildCount();
|
|
|
|
for (PRUint32 i = 0; i < count; ++i) {
|
|
|
|
nsIContent* child = GetChildAt(i);
|
|
|
|
NS_ASSERTION(child, "GetChildCount lied!");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> source = do_QueryInterface(child);
|
|
|
|
|
|
|
|
// Only check source element children.
|
|
|
|
if (!source ||
|
|
|
|
source->Tag() != nsGkAtoms::source ||
|
|
|
|
!source->IsNodeOfType(nsINode::eHTML))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nsAutoString type;
|
|
|
|
|
|
|
|
// Must have src attribute.
|
|
|
|
if (!source->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If we have a type attribyte, it must be valid.
|
|
|
|
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
|
|
|
|
GetCanPlay(type) == CANPLAY_NO)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
res = NewURIFromString(src, getter_AddRefs(uri));
|
|
|
|
if (NS_SUCCEEDED(res)) {
|
|
|
|
mCurrentLoad->AddCandidate(uri);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
nsresult nsHTMLMediaElement::PickMediaElement(nsIURI** aURI)
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
2008-12-16 03:32:03 +00:00
|
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
// Implements:
|
2008-12-15 03:38:16 +00:00
|
|
|
// http://www.whatwg.org/specs/web-apps/current-work/#pick-a-media-resource
|
2008-07-09 08:22:20 +00:00
|
|
|
nsAutoString src;
|
2008-11-04 08:50:24 +00:00
|
|
|
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
|
2008-12-16 03:32:03 +00:00
|
|
|
return NewURIFromString(src, aURI);
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Checking of 'source' elements as per:
|
2008-12-15 03:38:16 +00:00
|
|
|
// http://www.whatwg.org/specs/web-apps/current-work/#pick-a-media-resource
|
2008-07-09 08:22:20 +00:00
|
|
|
PRUint32 count = GetChildCount();
|
|
|
|
for (PRUint32 i = 0; i < count; ++i) {
|
|
|
|
nsIContent* child = GetChildAt(i);
|
|
|
|
NS_ASSERTION(child, "GetChildCount lied!");
|
2008-12-16 03:32:03 +00:00
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
nsCOMPtr<nsIContent> source = do_QueryInterface(child);
|
2008-12-16 03:32:03 +00:00
|
|
|
if (source &&
|
|
|
|
source->Tag() == nsGkAtoms::source &&
|
|
|
|
source->IsNodeOfType(nsINode::eHTML)) {
|
2008-11-04 08:50:24 +00:00
|
|
|
nsAutoString type;
|
|
|
|
nsAutoString src;
|
2008-12-16 03:32:03 +00:00
|
|
|
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
|
|
|
|
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
|
2008-12-17 02:11:07 +00:00
|
|
|
if (GetCanPlay(type) != CANPLAY_NO)
|
2008-12-16 03:32:03 +00:00
|
|
|
return NewURIFromString(src, aURI);
|
|
|
|
} else if (i + 1 == count) {
|
|
|
|
// The last source element is permitted to omit the type
|
|
|
|
// attribute, in which case we need to open the URI and examine
|
|
|
|
// the channel's MIME type.
|
|
|
|
return NewURIFromString(src, aURI);
|
|
|
|
}
|
|
|
|
}
|
2008-10-30 05:20:08 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
|
|
|
|
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::MetadataLoaded()
|
|
|
|
{
|
2008-12-15 03:38:16 +00:00
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
|
2008-07-09 08:22:20 +00:00
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("durationchange"));
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadedmetadata"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::FirstFrameLoaded()
|
|
|
|
{
|
2008-12-15 03:38:16 +00:00
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::ResourceLoaded()
|
|
|
|
{
|
|
|
|
mBegun = PR_FALSE;
|
2008-12-15 03:38:16 +00:00
|
|
|
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADED;
|
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
2009-02-07 08:42:32 +00:00
|
|
|
DispatchAsyncProgressEvent(NS_LITERAL_STRING("load"));
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::NetworkError()
|
|
|
|
{
|
2009-01-24 11:00:17 +00:00
|
|
|
mError = new nsHTMLMediaError(nsIDOMHTMLMediaError::MEDIA_ERR_NETWORK);
|
2008-07-09 08:22:20 +00:00
|
|
|
mBegun = PR_FALSE;
|
2009-02-07 08:42:32 +00:00
|
|
|
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
|
2008-12-15 03:38:16 +00:00
|
|
|
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
2009-02-07 08:42:32 +00:00
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
2008-10-19 07:39:21 +00:00
|
|
|
void nsHTMLMediaElement::PlaybackEnded()
|
2008-07-09 08:22:20 +00:00
|
|
|
{
|
2008-12-14 18:02:54 +00:00
|
|
|
NS_ASSERTION(mDecoder->IsEnded(), "Decoder fired ended, but not in ended state");
|
2008-07-09 08:22:20 +00:00
|
|
|
mBegun = PR_FALSE;
|
2008-10-19 07:39:21 +00:00
|
|
|
mPaused = PR_TRUE;
|
2009-02-07 08:42:32 +00:00
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("ended"));
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
2008-10-19 07:39:21 +00:00
|
|
|
void nsHTMLMediaElement::SeekStarted()
|
|
|
|
{
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("seeking"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::SeekCompleted()
|
|
|
|
{
|
|
|
|
mPlayingBeforeSeek = PR_FALSE;
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("seeked"));
|
|
|
|
}
|
|
|
|
|
2009-01-24 11:00:17 +00:00
|
|
|
PRBool nsHTMLMediaElement::ShouldCheckAllowOrigin()
|
|
|
|
{
|
|
|
|
return nsContentUtils::GetBoolPref("media.enforce_same_site_origin",
|
|
|
|
PR_TRUE);
|
|
|
|
}
|
|
|
|
|
2009-02-05 08:02:21 +00:00
|
|
|
// Number of bytes to add to the download size when we're computing
|
|
|
|
// when the download will finish --- a safety margin in case bandwidth
|
|
|
|
// or other conditions are worse than expected
|
|
|
|
static const PRInt32 gDownloadSizeSafetyMargin = 1000000;
|
|
|
|
|
2009-02-11 01:43:45 +00:00
|
|
|
void nsHTMLMediaElement::UpdateReadyStateForData(NextFrameStatus aNextFrame)
|
2009-02-05 08:02:21 +00:00
|
|
|
{
|
|
|
|
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
|
2009-02-11 01:43:45 +00:00
|
|
|
NS_ASSERTION(aNextFrame != NEXT_FRAME_AVAILABLE,
|
|
|
|
"How can we have a frame but no metadata?");
|
2009-02-05 08:02:21 +00:00
|
|
|
// The arrival of more data can't change us out of this state.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-02-11 01:43:45 +00:00
|
|
|
if (aNextFrame != NEXT_FRAME_AVAILABLE && !mDecoder->IsEnded()) {
|
2009-02-05 08:02:21 +00:00
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
|
2009-02-11 01:43:45 +00:00
|
|
|
if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
|
|
|
|
mWaitingFired = PR_TRUE;
|
|
|
|
}
|
2009-02-05 08:02:21 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now see if we should set HAVE_ENOUGH_DATA
|
|
|
|
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
|
|
|
if (stats.mTotalBytes < 0 || stats.mTotalBytes == stats.mDownloadPosition) {
|
|
|
|
// If it's something we don't know the size of, then we can't
|
|
|
|
// make an estimate, so let's just go straight to HAVE_ENOUGH_DATA,
|
|
|
|
// since otherwise autoplay elements will never play.
|
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stats.mDownloadRateReliable && stats.mPlaybackRateReliable) {
|
|
|
|
PRInt64 bytesToDownload = stats.mTotalBytes - stats.mDownloadPosition;
|
|
|
|
PRInt64 bytesToPlayback = stats.mTotalBytes - stats.mPlaybackPosition;
|
|
|
|
double timeToDownload =
|
|
|
|
(bytesToDownload + gDownloadSizeSafetyMargin)/stats.mDownloadRate;
|
|
|
|
double timeToPlay = bytesToPlayback/stats.mPlaybackRate;
|
|
|
|
if (timeToDownload <= timeToPlay) {
|
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA);
|
|
|
|
}
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
void nsHTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
|
|
|
|
{
|
2009-02-05 08:02:21 +00:00
|
|
|
nsMediaReadyState oldState = mReadyState;
|
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
// Handle raising of "waiting" event during seek (see 4.8.10.9)
|
2009-02-05 08:02:21 +00:00
|
|
|
if (mPlayingBeforeSeek && oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
2008-10-19 07:39:21 +00:00
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
|
2009-02-05 08:02:21 +00:00
|
|
|
}
|
|
|
|
|
2008-07-09 08:22:20 +00:00
|
|
|
mReadyState = aState;
|
2008-12-15 03:38:16 +00:00
|
|
|
if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
2009-02-05 08:02:21 +00:00
|
|
|
switch (mReadyState) {
|
2008-12-15 03:38:16 +00:00
|
|
|
case nsIDOMHTMLMediaElement::HAVE_NOTHING:
|
2009-02-05 08:02:21 +00:00
|
|
|
if (oldState != mReadyState) {
|
|
|
|
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_NOTHING"));
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-05 08:02:21 +00:00
|
|
|
case nsIDOMHTMLMediaElement::HAVE_METADATA:
|
|
|
|
if (oldState != mReadyState) {
|
|
|
|
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_METADATA"));
|
|
|
|
}
|
2009-02-05 08:02:21 +00:00
|
|
|
break;
|
2009-02-05 10:51:24 +00:00
|
|
|
|
2009-02-05 08:02:21 +00:00
|
|
|
case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
|
|
|
|
if (oldState != mReadyState) {
|
|
|
|
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_CURRENT_DATA"));
|
2009-02-11 01:43:45 +00:00
|
|
|
mWaitingFired = PR_FALSE;
|
2009-02-05 08:02:21 +00:00
|
|
|
}
|
|
|
|
if (oldState <= nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
|
|
|
!mLoadedFirstFrame) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadeddata"));
|
|
|
|
mLoadedFirstFrame = PR_TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
|
2009-02-05 08:02:21 +00:00
|
|
|
if (oldState != mReadyState) {
|
|
|
|
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_FUTURE_DATA"));
|
|
|
|
}
|
|
|
|
if (oldState <= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplay"));
|
|
|
|
if (IsPotentiallyPlaying()) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
|
|
|
|
}
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
break;
|
2009-02-05 08:02:21 +00:00
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
|
2009-02-05 08:02:21 +00:00
|
|
|
if (oldState != mReadyState) {
|
|
|
|
LOG(PR_LOG_DEBUG, ("Ready state changed to HAVE_ENOUGH_DATA"));
|
|
|
|
}
|
|
|
|
if (oldState <= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplay"));
|
|
|
|
}
|
2008-12-16 03:32:03 +00:00
|
|
|
if (mAutoplaying &&
|
|
|
|
mPaused &&
|
2009-02-20 04:05:07 +00:00
|
|
|
HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
|
|
|
|
mAutoplayEnabled) {
|
2008-07-09 08:22:20 +00:00
|
|
|
mPaused = PR_FALSE;
|
|
|
|
if (mDecoder) {
|
|
|
|
mDecoder->Play();
|
|
|
|
}
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("play"));
|
|
|
|
}
|
2009-02-05 08:02:21 +00:00
|
|
|
if (oldState <= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
|
|
|
|
IsPotentiallyPlaying()) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
|
|
|
|
}
|
2009-02-11 01:23:19 +00:00
|
|
|
if (oldState <= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
|
|
|
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplaythrough"));
|
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
break;
|
|
|
|
}
|
2008-12-16 03:32:03 +00:00
|
|
|
}
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::Paint(gfxContext* aContext, const gfxRect& aRect)
|
|
|
|
{
|
|
|
|
if (mDecoder)
|
|
|
|
mDecoder->Paint(aContext, aRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLMediaElement::DispatchSimpleEvent(const nsAString& aName)
|
|
|
|
{
|
|
|
|
return nsContentUtils::DispatchTrustedEvent(GetOwnerDoc(),
|
|
|
|
static_cast<nsIContent*>(this),
|
|
|
|
aName,
|
|
|
|
PR_TRUE,
|
|
|
|
PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLMediaElement::DispatchAsyncSimpleEvent(const nsAString& aName)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this, PR_FALSE);
|
2008-10-19 07:39:21 +00:00
|
|
|
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
2008-07-09 08:22:20 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLMediaElement::DispatchAsyncProgressEvent(const nsAString& aName)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this, PR_TRUE);
|
2008-10-19 07:39:21 +00:00
|
|
|
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
2008-07-09 08:22:20 +00:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(GetOwnerDoc()));
|
|
|
|
nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(static_cast<nsIContent*>(this)));
|
|
|
|
NS_ENSURE_TRUE(docEvent && target, NS_ERROR_INVALID_ARG);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMEvent> event;
|
|
|
|
nsresult rv = docEvent->CreateEvent(NS_LITERAL_STRING("ProgressEvent"), getter_AddRefs(event));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event));
|
|
|
|
NS_ENSURE_TRUE(progressEvent, NS_ERROR_FAILURE);
|
2009-01-15 20:26:51 +00:00
|
|
|
|
2009-02-15 16:26:32 +00:00
|
|
|
PRInt64 totalBytes = 0;
|
|
|
|
PRUint64 downloadPosition = 0;
|
|
|
|
if (mDecoder) {
|
|
|
|
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
|
|
|
totalBytes = stats.mTotalBytes;
|
|
|
|
downloadPosition = stats.mDownloadPosition;
|
|
|
|
}
|
2009-01-15 20:26:51 +00:00
|
|
|
rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE,
|
2009-02-15 16:26:32 +00:00
|
|
|
totalBytes >= 0, downloadPosition, totalBytes);
|
2008-07-09 08:22:20 +00:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRBool dummy;
|
|
|
|
return target->DispatchEvent(event, &dummy);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult nsHTMLMediaElement::DoneAddingChildren(PRBool aHaveNotified)
|
|
|
|
{
|
|
|
|
if (!mIsDoneAddingChildren) {
|
|
|
|
mIsDoneAddingChildren = PR_TRUE;
|
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
|
2009-02-16 01:05:28 +00:00
|
|
|
QueueLoadTask();
|
2008-07-09 08:22:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool nsHTMLMediaElement::IsDoneAddingChildren()
|
|
|
|
{
|
|
|
|
return mIsDoneAddingChildren;
|
|
|
|
}
|
2008-09-06 23:47:28 +00:00
|
|
|
|
2008-12-15 03:38:16 +00:00
|
|
|
PRBool nsHTMLMediaElement::IsPotentiallyPlaying() const
|
2008-10-19 07:39:21 +00:00
|
|
|
{
|
|
|
|
// TODO:
|
|
|
|
// playback has not stopped due to errors,
|
|
|
|
// and the element has not paused for user interaction
|
|
|
|
return
|
|
|
|
!mPaused &&
|
2008-12-15 03:38:16 +00:00
|
|
|
(mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA ||
|
|
|
|
mReadyState == nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) &&
|
2008-10-19 07:39:21 +00:00
|
|
|
!IsPlaybackEnded();
|
|
|
|
}
|
2008-12-14 18:02:54 +00:00
|
|
|
|
2008-10-19 07:39:21 +00:00
|
|
|
PRBool nsHTMLMediaElement::IsPlaybackEnded() const
|
|
|
|
{
|
|
|
|
// TODO:
|
|
|
|
// the current playback position is equal to the effective end of the media resource,
|
|
|
|
// and the currentLoop attribute is equal to playCount-1.
|
|
|
|
// See bug 449157.
|
2008-12-15 03:38:16 +00:00
|
|
|
return mNetworkState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
|
2008-12-14 18:02:54 +00:00
|
|
|
mDecoder ? mDecoder->IsEnded() : PR_FALSE;
|
2008-10-19 07:39:21 +00:00
|
|
|
}
|
|
|
|
|
2008-12-16 03:32:03 +00:00
|
|
|
nsIPrincipal* nsHTMLMediaElement::GetCurrentPrincipal()
|
2008-09-06 23:47:28 +00:00
|
|
|
{
|
|
|
|
if (!mDecoder)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
return mDecoder->GetCurrentPrincipal();
|
|
|
|
}
|
2008-09-26 12:56:21 +00:00
|
|
|
|
2008-10-19 07:39:21 +00:00
|
|
|
void nsHTMLMediaElement::UpdateMediaSize(nsIntSize size)
|
|
|
|
{
|
|
|
|
mMediaSize = size;
|
|
|
|
}
|
|
|
|
|
2008-09-26 12:56:21 +00:00
|
|
|
void nsHTMLMediaElement::DestroyContent()
|
|
|
|
{
|
|
|
|
if (mDecoder) {
|
2008-10-19 07:39:21 +00:00
|
|
|
mDecoder->Shutdown();
|
2008-09-26 12:56:21 +00:00
|
|
|
mDecoder = nsnull;
|
|
|
|
}
|
2008-12-16 03:32:03 +00:00
|
|
|
if (mChannel) {
|
|
|
|
mChannel->Cancel(NS_BINDING_ABORTED);
|
|
|
|
mChannel = nsnull;
|
|
|
|
}
|
2008-09-26 12:56:21 +00:00
|
|
|
nsGenericHTMLElement::DestroyContent();
|
|
|
|
}
|
2008-10-28 18:48:39 +00:00
|
|
|
|
|
|
|
void nsHTMLMediaElement::Freeze()
|
|
|
|
{
|
|
|
|
mPausedBeforeFreeze = mPaused;
|
|
|
|
if (!mPaused) {
|
|
|
|
Pause();
|
|
|
|
}
|
2009-01-21 23:54:40 +00:00
|
|
|
if (mDecoder) {
|
|
|
|
mDecoder->Suspend();
|
|
|
|
}
|
2008-10-28 18:48:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::Thaw()
|
|
|
|
{
|
|
|
|
if (!mPausedBeforeFreeze) {
|
|
|
|
Play();
|
|
|
|
}
|
2009-01-21 23:54:40 +00:00
|
|
|
|
|
|
|
if (mDecoder) {
|
|
|
|
mDecoder->Resume();
|
|
|
|
}
|
2008-10-28 18:48:39 +00:00
|
|
|
}
|
2009-02-16 01:05:28 +00:00
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsHTMLMediaElement::IsNodeOfType(PRUint32 aFlags) const
|
|
|
|
{
|
|
|
|
return !(aFlags & ~(eCONTENT | eELEMENT | eHTML | eMEDIA));
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsHTMLMediaElement::NotifyAddedSource()
|
|
|
|
{
|
|
|
|
// Binding a source element to a media element could trigger a new load.
|
|
|
|
// See HTML spec, '4.8.9 The source element' for conditions:
|
|
|
|
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#the-source-element
|
|
|
|
// Note: we must not start a load if we're in nsHTMLMediaElement::BindToTree(),
|
|
|
|
// that will trigger a load when it completes.
|
|
|
|
PRBool shouldLoad = IsInDoc() &&
|
|
|
|
!mIsBindingToTree &&
|
|
|
|
mIsDoneAddingChildren &&
|
|
|
|
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY;
|
|
|
|
if (shouldLoad) {
|
|
|
|
QueueLoadTask();
|
|
|
|
}
|
|
|
|
}
|