Bug 841493 - Move HTMLMediaElement to WebIDL, r=Ms2ger

This commit is contained in:
Andrea Marchesini 2013-03-19 13:25:19 +01:00
parent eb3d295f7f
commit f1041ff584
9 changed files with 786 additions and 186 deletions

View File

@ -27,6 +27,8 @@
#include "DecoderTraits.h"
#include "MediaMetadataManager.h"
#include "AudioChannelAgent.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
// Define to output information on decoding and painting framerate
/* #define DEBUG_FRAME_RATE 1 */
@ -42,6 +44,8 @@ class MediaDecoder;
namespace mozilla {
namespace dom {
class MediaError;
class HTMLMediaElement : public nsGenericHTMLElement,
public nsIObserver,
public MediaDecoderOwner,
@ -318,12 +322,194 @@ public:
*/
virtual void FireTimeUpdate(bool aPeriodic) MOZ_FINAL MOZ_OVERRIDE;
MediaStream* GetSrcMediaStream()
MediaStream* GetSrcMediaStream() const
{
NS_ASSERTION(mSrcStream, "Don't call this when not playing a stream");
return mSrcStream->GetStream();
}
// WebIDL
MediaError* GetError() const
{
return mError;
}
// XPCOM GetSrc() is OK
void SetSrc(const nsAString& aSrc, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::src, aSrc, aRv);
}
// XPCOM GetCurrentSrc() is OK
// XPCOM GetCrossorigin() is OK
void SetCrossorigin(const nsAString& aValue, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::crossorigin, aValue, aRv);
}
uint16_t NetworkState() const
{
return mNetworkState;
}
// XPCOM GetPreload() is OK
void SetPreload(const nsAString& aValue, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::preload, aValue, aRv);
}
already_AddRefed<TimeRanges> Buffered() const;
// XPCOM Load() is OK
// XPCOM CanPlayType() is OK
uint16_t ReadyState() const
{
return mReadyState;
}
bool Seeking() const;
double CurrentTime() const;
void SetCurrentTime(double aCurrentTime, ErrorResult& aRv);
double Duration() const;
bool Paused() const
{
return mPaused;
}
double DefaultPlaybackRate() const
{
return mDefaultPlaybackRate;
}
void SetDefaultPlaybackRate(double aDefaultPlaybackRate, ErrorResult& aRv);
double PlaybackRate() const
{
return mPlaybackRate;
}
void SetPlaybackRate(double aPlaybackRate, ErrorResult& aRv);
already_AddRefed<TimeRanges> Played();
already_AddRefed<TimeRanges> Seekable() const;
bool Ended();
bool Autoplay() const
{
return GetBoolAttr(nsGkAtoms::autoplay);
}
void SetAutoplay(bool aValue, ErrorResult& aRv)
{
SetHTMLBoolAttr(nsGkAtoms::autoplay, aValue, aRv);
}
bool Loop() const
{
return GetBoolAttr(nsGkAtoms::loop);
}
void SetLoop(bool aValue, ErrorResult& aRv)
{
SetHTMLBoolAttr(nsGkAtoms::loop, aValue, aRv);
}
void Play(ErrorResult& aRv);
void Pause(ErrorResult& aRv);
bool Controls() const
{
return GetBoolAttr(nsGkAtoms::controls);
}
void SetControls(bool aValue, ErrorResult& aRv)
{
SetHTMLBoolAttr(nsGkAtoms::controls, aValue, aRv);
}
double Volume() const
{
return mVolume;
}
void SetVolume(double aVolume, ErrorResult& aRv);
bool Muted() const
{
return mMuted;
}
// XPCOM SetMuted() is OK
bool DefaultMuted() const
{
return GetBoolAttr(nsGkAtoms::muted);
}
void SetDefaultMuted(bool aMuted, ErrorResult& aRv)
{
SetHTMLBoolAttr(nsGkAtoms::muted, aMuted, aRv);
}
already_AddRefed<DOMMediaStream> GetMozSrcObject() const;
void SetMozSrcObject(DOMMediaStream& aValue);
double InitialTime();
bool MozPreservesPitch() const
{
return mPreservesPitch;
}
// XPCOM MozPreservesPitch() is OK
bool MozAutoplayEnabled() const
{
return mAutoplayEnabled;
}
already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
already_AddRefed<DOMMediaStream> MozCaptureStreamUntilEnded(ErrorResult& aRv);
bool MozAudioCaptured() const
{
return mAudioCaptured;
}
uint32_t GetMozChannels(ErrorResult& aRv) const;
uint32_t GetMozSampleRate(ErrorResult& aRv) const;
uint32_t GetMozFrameBufferLength(ErrorResult& aRv) const;
void SetMozFrameBufferLength(uint32_t aValue, ErrorResult& aRv);
JSObject* MozGetMetadata(JSContext* aCx, ErrorResult& aRv);
void MozLoadFrom(HTMLMediaElement& aOther, ErrorResult& aRv);
double MozFragmentEnd();
// XPCOM GetMozAudioChannelType() is OK
void SetMozAudioChannelType(const nsAString& aValue, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::mozaudiochannel, aValue, aRv);
}
protected:
class MediaLoadListener;
class StreamListener;
@ -335,7 +521,7 @@ protected:
public:
WakeLockBoolWrapper(bool val = false) : mValue(val), mOuter(NULL), mWakeLock(NULL) {}
void SetOuter(HTMLMediaElement* outer) { mOuter = outer; }
operator bool() { return mValue; }
operator bool() const { return mValue; }
WakeLockBoolWrapper& operator=(bool val);
bool operator !() const { return !mValue; }
private:
@ -654,7 +840,7 @@ protected:
nsCOMPtr<nsIChannel> mChannel;
// Error attribute
nsCOMPtr<nsIDOMMediaError> mError;
nsRefPtr<MediaError> mError;
// The current media load ID. This is incremented every time we start a
// new load. Async events note the ID when they're first sent, and only fire

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/HTMLMediaElementBinding.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Util.h"
@ -442,21 +443,35 @@ NS_IMPL_BOOL_ATTR(HTMLMediaElement, DefaultMuted, muted)
NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMediaElement, Preload, preload, NULL)
NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(HTMLMediaElement, MozAudioChannelType, mozaudiochannel, "normal")
NS_IMETHODIMP
HTMLMediaElement::GetMozSrcObject(nsIDOMMediaStream** aStream)
already_AddRefed<DOMMediaStream>
HTMLMediaElement::GetMozSrcObject() const
{
NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
"MediaStream should have been set up properly");
nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
return stream.forget();
}
NS_IMETHODIMP
HTMLMediaElement::GetMozSrcObject(nsIDOMMediaStream** aStream)
{
nsRefPtr<DOMMediaStream> stream = GetMozSrcObject();
stream.forget(aStream);
return NS_OK;
}
void
HTMLMediaElement::SetMozSrcObject(DOMMediaStream& aValue)
{
mSrcAttrStream = &aValue;
Load();
}
NS_IMETHODIMP
HTMLMediaElement::SetMozSrcObject(nsIDOMMediaStream* aStream)
{
mSrcAttrStream = static_cast<DOMMediaStream*>(aStream);
Load();
DOMMediaStream* stream = static_cast<DOMMediaStream*>(aStream);
SetMozSrcObject(*stream);
return NS_OK;
}
@ -477,13 +492,23 @@ NS_IMETHODIMP HTMLMediaElement::GetError(nsIDOMMediaError * *aError)
}
/* readonly attribute boolean ended; */
NS_IMETHODIMP HTMLMediaElement::GetEnded(bool *aEnded)
bool
HTMLMediaElement::Ended()
{
if (mSrcStream) {
*aEnded = GetSrcMediaStream()->IsFinished();
} else if (mDecoder) {
*aEnded = mDecoder->IsEnded();
return GetSrcMediaStream()->IsFinished();
}
if (mDecoder) {
return mDecoder->IsEnded();
}
return false;
}
NS_IMETHODIMP HTMLMediaElement::GetEnded(bool* aEnded)
{
*aEnded = Ended();
return NS_OK;
}
@ -499,8 +524,7 @@ NS_IMETHODIMP HTMLMediaElement::GetCurrentSrc(nsAString & aCurrentSrc)
/* readonly attribute unsigned short networkState; */
NS_IMETHODIMP HTMLMediaElement::GetNetworkState(uint16_t* aNetworkState)
{
*aNetworkState = mNetworkState;
*aNetworkState = NetworkState();
return NS_OK;
}
@ -682,6 +706,7 @@ NS_IMETHODIMP HTMLMediaElement::Load()
{
if (mIsRunningLoadMethod)
return NS_OK;
SetPlayedOrSeeked(false);
mIsRunningLoadMethod = true;
AbortExistingLoads();
@ -689,6 +714,7 @@ NS_IMETHODIMP HTMLMediaElement::Load()
QueueSelectResourceTask();
ResetState();
mIsRunningLoadMethod = false;
return NS_OK;
}
@ -1173,79 +1199,110 @@ nsresult HTMLMediaElement::LoadWithChannel(nsIChannel *aChannel,
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther)
void
HTMLMediaElement::MozLoadFrom(HTMLMediaElement& aOther, ErrorResult& aRv)
{
NS_ENSURE_ARG_POINTER(aOther);
// Make sure we don't reenter during synchronous abort events.
if (mIsRunningLoadMethod)
return NS_OK;
if (mIsRunningLoadMethod) {
return;
}
mIsRunningLoadMethod = true;
AbortExistingLoads();
mIsRunningLoadMethod = false;
nsCOMPtr<nsIContent> content = do_QueryInterface(aOther);
HTMLMediaElement* other = static_cast<HTMLMediaElement*>(content.get());
if (!other || !other->mDecoder)
return NS_OK;
if (!aOther.mDecoder) {
return;
}
ChangeDelayLoadStatus(true);
mLoadingSrc = other->mLoadingSrc;
nsresult rv = InitializeDecoderAsClone(other->mDecoder);
if (NS_FAILED(rv)) {
mLoadingSrc = aOther.mLoadingSrc;
aRv = InitializeDecoderAsClone(aOther.mDecoder);
if (aRv.Failed()) {
ChangeDelayLoadStatus(false);
return rv;
return;
}
SetPlaybackRate(mDefaultPlaybackRate);
DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
}
return NS_OK;
NS_IMETHODIMP HTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther)
{
NS_ENSURE_ARG_POINTER(aOther);
nsCOMPtr<nsIContent> content = do_QueryInterface(aOther);
HTMLMediaElement* other = static_cast<HTMLMediaElement*>(content.get());
if (!other) {
return NS_ERROR_FAILURE;
}
ErrorResult rv;
MozLoadFrom(*other, rv);
return rv.ErrorCode();
}
/* readonly attribute unsigned short readyState; */
NS_IMETHODIMP HTMLMediaElement::GetReadyState(uint16_t* aReadyState)
{
*aReadyState = mReadyState;
*aReadyState = ReadyState();
return NS_OK;
}
/* readonly attribute boolean seeking; */
bool
HTMLMediaElement::Seeking() const
{
return mDecoder && mDecoder->IsSeeking();
}
NS_IMETHODIMP HTMLMediaElement::GetSeeking(bool* aSeeking)
{
*aSeeking = mDecoder && mDecoder->IsSeeking();
*aSeeking = Seeking();
return NS_OK;
}
/* attribute double currentTime; */
NS_IMETHODIMP HTMLMediaElement::GetCurrentTime(double *aCurrentTime)
double
HTMLMediaElement::CurrentTime() const
{
if (mSrcStream) {
*aCurrentTime = MediaTimeToSeconds(GetSrcMediaStream()->GetCurrentTime());
} else if (mDecoder) {
*aCurrentTime = mDecoder->GetCurrentTime();
} else {
*aCurrentTime = 0.0;
return MediaTimeToSeconds(GetSrcMediaStream()->GetCurrentTime());
}
if (mDecoder) {
return mDecoder->GetCurrentTime();
}
return 0.0;
}
NS_IMETHODIMP HTMLMediaElement::GetCurrentTime(double* aCurrentTime)
{
*aCurrentTime = CurrentTime();
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::SetCurrentTime(double aCurrentTime)
void
HTMLMediaElement::SetCurrentTime(double aCurrentTime, ErrorResult& aRv)
{
MOZ_ASSERT(aCurrentTime == aCurrentTime);
StopSuspendingAfterFirstFrame();
if (mSrcStream) {
// do nothing since streams aren't seekable; we effectively clamp to
// the current time.
return NS_ERROR_DOM_INVALID_STATE_ERR;
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (mCurrentPlayRangeStart != -1.0) {
double rangeEndTime = 0;
GetCurrentTime(&rangeEndTime);
double rangeEndTime = CurrentTime();
LOG(PR_LOG_DEBUG, ("%p Adding \'played\' a range : [%f, %f]", this, mCurrentPlayRangeStart, rangeEndTime));
// Multiple seek without playing, or seek while playing.
if (mCurrentPlayRangeStart != rangeEndTime) {
@ -1255,18 +1312,14 @@ NS_IMETHODIMP HTMLMediaElement::SetCurrentTime(double aCurrentTime)
if (!mDecoder) {
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: no decoder", this, aCurrentTime));
return NS_ERROR_DOM_INVALID_STATE_ERR;
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: no source", this, aCurrentTime));
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
// Detect for a NaN and invalid values.
if (aCurrentTime != aCurrentTime) {
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: bad time", this, aCurrentTime));
return NS_ERROR_FAILURE;
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
// Clamp the time to [0, duration] as required by the spec.
@ -1280,54 +1333,78 @@ NS_IMETHODIMP HTMLMediaElement::SetCurrentTime(double aCurrentTime)
// The media backend is responsible for dispatching the timeupdate
// event if it changes the playback position as a result of the seek.
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) starting seek", this, aCurrentTime));
nsresult rv = mDecoder->Seek(clampedTime);
aRv = mDecoder->Seek(clampedTime);
// Start a new range at position we seeked to.
mCurrentPlayRangeStart = mDecoder->GetCurrentTime();
// We changed whether we're seeking so we need to AddRemoveSelfReference.
AddRemoveSelfReference();
}
return rv;
NS_IMETHODIMP HTMLMediaElement::SetCurrentTime(double aCurrentTime)
{
// Detect for a NaN and invalid values.
if (aCurrentTime != aCurrentTime) {
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: bad time", this, aCurrentTime));
return NS_ERROR_FAILURE;
}
ErrorResult rv;
SetCurrentTime(aCurrentTime, rv);
return rv.ErrorCode();
}
/* readonly attribute double duration; */
NS_IMETHODIMP HTMLMediaElement::GetDuration(double *aDuration)
double
HTMLMediaElement::Duration() const
{
if (mSrcStream) {
*aDuration = std::numeric_limits<double>::infinity();
} else if (mDecoder) {
*aDuration = mDecoder->GetDuration();
} else {
*aDuration = std::numeric_limits<double>::quiet_NaN();
return std::numeric_limits<double>::infinity();
}
if (mDecoder) {
return mDecoder->GetDuration();
}
return std::numeric_limits<double>::quiet_NaN();
}
NS_IMETHODIMP HTMLMediaElement::GetDuration(double* aDuration)
{
*aDuration = Duration();
return NS_OK;
}
/* readonly attribute nsIDOMHTMLTimeRanges seekable; */
NS_IMETHODIMP HTMLMediaElement::GetSeekable(nsIDOMTimeRanges** aSeekable)
already_AddRefed<TimeRanges>
HTMLMediaElement::Seekable() const
{
nsRefPtr<TimeRanges> ranges = new TimeRanges();
if (mDecoder && mReadyState > nsIDOMHTMLMediaElement::HAVE_NOTHING) {
mDecoder->GetSeekable(ranges);
}
return ranges.forget();
}
/* readonly attribute nsIDOMHTMLTimeRanges seekable; */
NS_IMETHODIMP HTMLMediaElement::GetSeekable(nsIDOMTimeRanges** aSeekable)
{
nsRefPtr<TimeRanges> ranges = Seekable();
ranges.forget(aSeekable);
return NS_OK;
}
/* readonly attribute boolean paused; */
NS_IMETHODIMP HTMLMediaElement::GetPaused(bool* aPaused)
{
*aPaused = mPaused;
*aPaused = Paused();
return NS_OK;
}
/* readonly attribute nsIDOMHTMLTimeRanges played; */
NS_IMETHODIMP HTMLMediaElement::GetPlayed(nsIDOMTimeRanges** aPlayed)
already_AddRefed<TimeRanges>
HTMLMediaElement::Played()
{
TimeRanges* ranges = new TimeRanges();
NS_ADDREF(*aPlayed = ranges);
nsRefPtr<TimeRanges> ranges = new TimeRanges();
uint32_t timeRangeCount = 0;
mPlayed.GetLength(&timeRangeCount);
@ -1340,25 +1417,34 @@ NS_IMETHODIMP HTMLMediaElement::GetPlayed(nsIDOMTimeRanges** aPlayed)
}
if (mCurrentPlayRangeStart != -1.0) {
double now = 0.0;
GetCurrentTime(&now);
double now = CurrentTime();
if (mCurrentPlayRangeStart != now) {
ranges->Add(mCurrentPlayRangeStart, now);
}
}
ranges->Normalize();
return ranges.forget();
}
/* readonly attribute nsIDOMHTMLTimeRanges played; */
NS_IMETHODIMP HTMLMediaElement::GetPlayed(nsIDOMTimeRanges** aPlayed)
{
nsRefPtr<TimeRanges> ranges = Played();
ranges.forget(aPlayed);
return NS_OK;
}
/* void pause (); */
NS_IMETHODIMP HTMLMediaElement::Pause()
void
HTMLMediaElement::Pause(ErrorResult& aRv)
{
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
LOG(PR_LOG_DEBUG, ("Loading due to Pause()"));
nsresult rv = Load();
NS_ENSURE_SUCCESS(rv, rv);
aRv = Load();
if (aRv.Failed()) {
return;
}
} else if (mDecoder) {
mDecoder->Pause();
}
@ -1376,25 +1462,32 @@ NS_IMETHODIMP HTMLMediaElement::Pause()
FireTimeUpdate(false);
DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
}
}
return NS_OK;
NS_IMETHODIMP HTMLMediaElement::Pause()
{
ErrorResult rv;
Pause(rv);
return rv.ErrorCode();
}
/* attribute double volume; */
NS_IMETHODIMP HTMLMediaElement::GetVolume(double* aVolume)
{
*aVolume = mVolume;
*aVolume = Volume();
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::SetVolume(double aVolume)
void
HTMLMediaElement::SetVolume(double aVolume, ErrorResult& aRv)
{
if (!(aVolume >= 0.0 && aVolume <= 1.0))
return NS_ERROR_DOM_INDEX_SIZE_ERR;
if (aVolume < 0.0 || aVolume > 1.0) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
if (aVolume == mVolume)
return NS_OK;
return;
mVolume = aVolume;
@ -1409,30 +1502,51 @@ NS_IMETHODIMP HTMLMediaElement::SetVolume(double aVolume)
}
DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
}
return NS_OK;
NS_IMETHODIMP HTMLMediaElement::SetVolume(double aVolume)
{
ErrorResult rv;
SetVolume(aVolume, rv);
return rv.ErrorCode();
}
uint32_t
HTMLMediaElement::GetMozChannels(ErrorResult& aRv) const
{
if (!mDecoder && !mAudioStream) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return 0;
}
return mChannels;
}
NS_IMETHODIMP
HTMLMediaElement::GetMozChannels(uint32_t* aMozChannels)
{
if (!mDecoder && !mAudioStream) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
ErrorResult rv;
*aMozChannels = GetMozChannels(rv);
return rv.ErrorCode();
}
*aMozChannels = mChannels;
return NS_OK;
uint32_t
HTMLMediaElement::GetMozSampleRate(ErrorResult& aRv) const
{
if (!mDecoder && !mAudioStream) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return 0;
}
return mRate;
}
NS_IMETHODIMP
HTMLMediaElement::GetMozSampleRate(uint32_t* aMozSampleRate)
{
if (!mDecoder && !mAudioStream) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
}
*aMozSampleRate = mRate;
return NS_OK;
ErrorResult rv;
*aMozSampleRate = GetMozSampleRate(rv);
return rv.ErrorCode();
}
// Helper struct with arguments for our hash iterator.
@ -1467,57 +1581,90 @@ HTMLMediaElement::BuildObjectFromTags(nsCStringHashKey::KeyType aKey,
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
HTMLMediaElement::MozGetMetadata(JSContext* cx, JS::Value* aValue)
JSObject*
HTMLMediaElement::MozGetMetadata(JSContext* cx, ErrorResult& aRv)
{
if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr;
}
JSObject* tags = JS_NewObject(cx, NULL, NULL, NULL);
if (!tags) {
return NS_ERROR_FAILURE;
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
if (mTags) {
MetadataIterCx iter = {cx, tags, false};
mTags->EnumerateRead(BuildObjectFromTags, static_cast<void*>(&iter));
if (iter.error) {
NS_WARNING("couldn't create metadata object!");
return NS_ERROR_FAILURE;
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
}
*aValue = OBJECT_TO_JSVAL(tags);
return NS_OK;
return tags;
}
NS_IMETHODIMP
HTMLMediaElement::MozGetMetadata(JSContext* cx, JS::Value* aValue)
{
ErrorResult rv;
JSObject* obj = MozGetMetadata(cx, rv);
if (!rv.Failed()) {
MOZ_ASSERT(obj);
*aValue = JS::ObjectValue(*obj);
}
return rv.ErrorCode();
}
uint32_t
HTMLMediaElement::GetMozFrameBufferLength(ErrorResult& aRv) const
{
// The framebuffer (via MozAudioAvailable events) is only available
// when reading vs. writing audio directly.
if (!mDecoder) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return 0;
}
return mDecoder->GetFrameBufferLength();
}
NS_IMETHODIMP
HTMLMediaElement::GetMozFrameBufferLength(uint32_t* aMozFrameBufferLength)
{
// The framebuffer (via MozAudioAvailable events) is only available
// when reading vs. writing audio directly.
if (!mDecoder) {
return NS_ERROR_DOM_INVALID_STATE_ERR;
ErrorResult rv;
*aMozFrameBufferLength = GetMozFrameBufferLength(rv);
return rv.ErrorCode();
}
*aMozFrameBufferLength = mDecoder->GetFrameBufferLength();
return NS_OK;
void
HTMLMediaElement::SetMozFrameBufferLength(uint32_t aMozFrameBufferLength, ErrorResult& aRv)
{
if (!mDecoder) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
aRv = mDecoder->RequestFrameBufferLength(aMozFrameBufferLength);
}
NS_IMETHODIMP
HTMLMediaElement::SetMozFrameBufferLength(uint32_t aMozFrameBufferLength)
{
if (!mDecoder)
return NS_ERROR_DOM_INVALID_STATE_ERR;
return mDecoder->RequestFrameBufferLength(aMozFrameBufferLength);
ErrorResult rv;
SetMozFrameBufferLength(aMozFrameBufferLength, rv);
return rv.ErrorCode();
}
/* attribute boolean muted; */
NS_IMETHODIMP HTMLMediaElement::GetMuted(bool* aMuted)
{
*aMuted = mMuted;
*aMuted = Muted();
return NS_OK;
}
@ -1543,7 +1690,6 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted(bool aMuted)
SetMutedInternal(aMuted);
DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
return NS_OK;
}
@ -1575,27 +1721,47 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
return result.forget();
}
already_AddRefed<DOMMediaStream>
HTMLMediaElement::MozCaptureStream(ErrorResult& aRv)
{
nsRefPtr<DOMMediaStream> stream = CaptureStreamInternal(false);
if (!stream) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return stream.forget();
}
NS_IMETHODIMP HTMLMediaElement::MozCaptureStream(nsIDOMMediaStream** aStream)
{
*aStream = CaptureStreamInternal(false).get();
if (!*aStream) {
return NS_ERROR_FAILURE;
ErrorResult rv;
*aStream = MozCaptureStream(rv).get();
return rv.ErrorCode();
}
return NS_OK;
already_AddRefed<DOMMediaStream>
HTMLMediaElement::MozCaptureStreamUntilEnded(ErrorResult& aRv)
{
nsRefPtr<DOMMediaStream> stream = CaptureStreamInternal(true);
if (!stream) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return stream.forget();
}
NS_IMETHODIMP HTMLMediaElement::MozCaptureStreamUntilEnded(nsIDOMMediaStream** aStream)
{
*aStream = CaptureStreamInternal(true).get();
if (!*aStream) {
return NS_ERROR_FAILURE;
}
return NS_OK;
ErrorResult rv;
*aStream = MozCaptureStreamUntilEnded(rv).get();
return rv.ErrorCode();
}
NS_IMETHODIMP HTMLMediaElement::GetMozAudioCaptured(bool* aCaptured)
{
*aCaptured = mAudioCaptured;
*aCaptured = MozAudioCaptured();
return NS_OK;
}
@ -1838,14 +2004,17 @@ void HTMLMediaElement::SetPlayedOrSeeked(bool aValue)
NS_FRAME_IS_DIRTY);
}
NS_IMETHODIMP HTMLMediaElement::Play()
void
HTMLMediaElement::Play(ErrorResult& aRv)
{
StopSuspendingAfterFirstFrame();
SetPlayedOrSeeked(true);
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
nsresult rv = Load();
NS_ENSURE_SUCCESS(rv, rv);
aRv = Load();
if (aRv.Failed()) {
return;
}
}
if (mSuspendedForPreloadNone) {
ResumeLoad(PRELOAD_ENOUGH);
@ -1857,13 +2026,15 @@ NS_IMETHODIMP HTMLMediaElement::Play()
SetCurrentTime(0);
}
if (!mPausedForInactiveDocumentOrChannel) {
nsresult rv = mDecoder->Play();
NS_ENSURE_SUCCESS(rv, rv);
aRv = mDecoder->Play();
if (aRv.Failed()) {
return;
}
}
}
if (mCurrentPlayRangeStart == -1.0) {
GetCurrentTime(&mCurrentPlayRangeStart);
mCurrentPlayRangeStart = CurrentTime();
}
// TODO: If the playback has ended, then the user agent must set
@ -1897,8 +2068,13 @@ NS_IMETHODIMP HTMLMediaElement::Play()
// and our preload status.
AddRemoveSelfReference();
UpdatePreloadAction();
}
return NS_OK;
NS_IMETHODIMP HTMLMediaElement::Play()
{
ErrorResult rv;
Play(rv);
return rv.ErrorCode();
}
HTMLMediaElement::WakeLockBoolWrapper& HTMLMediaElement::WakeLockBoolWrapper::operator=(bool val) {
@ -2851,7 +3027,7 @@ void HTMLMediaElement::NotifyAutoplayDataReady()
if (mDecoder) {
SetPlayedOrSeeked(true);
if (mCurrentPlayRangeStart == -1.0) {
GetCurrentTime(&mCurrentPlayRangeStart);
mCurrentPlayRangeStart = CurrentTime();
}
mDecoder->Play();
} else if (mSrcStream) {
@ -3268,7 +3444,8 @@ HTMLMediaElement::CopyInnerTo(Element* aDest)
return rv;
}
nsresult HTMLMediaElement::GetBuffered(nsIDOMTimeRanges** aBuffered)
already_AddRefed<TimeRanges>
HTMLMediaElement::Buffered() const
{
nsRefPtr<TimeRanges> ranges = new TimeRanges();
if (mReadyState > nsIDOMHTMLMediaElement::HAVE_NOTHING && mDecoder) {
@ -3276,6 +3453,12 @@ nsresult HTMLMediaElement::GetBuffered(nsIDOMTimeRanges** aBuffered)
// time ranges we found up till the error.
mDecoder->GetBuffered(ranges);
}
return ranges.forget();
}
nsresult HTMLMediaElement::GetBuffered(nsIDOMTimeRanges** aBuffered)
{
nsRefPtr<TimeRanges> ranges = Buffered();
ranges.forget(aBuffered);
return NS_OK;
}
@ -3309,8 +3492,7 @@ void HTMLMediaElement::FireTimeUpdate(bool aPeriodic)
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
TimeStamp now = TimeStamp::Now();
double time = 0;
GetCurrentTime(&time);
double time = CurrentTime();
// Fire a timeupdate event if this is not a periodic update (i.e. it's a
// timeupdate event mandated by the spec), or if it's a periodic update
@ -3342,31 +3524,41 @@ void HTMLMediaElement::GetCurrentSpec(nsCString& aString)
}
/* attribute double initialTime; */
NS_IMETHODIMP HTMLMediaElement::GetInitialTime(double *aTime)
double
HTMLMediaElement::InitialTime()
{
// If there is no start fragment then the initalTime is zero.
// Clamp to duration if it is greater than duration.
double duration = 0.0;
nsresult rv = GetDuration(&duration);
NS_ENSURE_SUCCESS(rv, rv);
double duration = Duration();
*aTime = mFragmentStart < 0.0 ? 0.0 : mFragmentStart;
if (*aTime > duration) {
*aTime = duration;
double time = mFragmentStart < 0.0 ? 0.0 : mFragmentStart;
if (time > duration) {
time = duration;
}
return time;
}
NS_IMETHODIMP HTMLMediaElement::GetInitialTime(double* aTime)
{
*aTime = InitialTime();
return NS_OK;
}
/* attribute double mozFragmentEnd; */
NS_IMETHODIMP HTMLMediaElement::GetMozFragmentEnd(double *aTime)
double
HTMLMediaElement::MozFragmentEnd()
{
double duration = 0.0;
nsresult rv = GetDuration(&duration);
NS_ENSURE_SUCCESS(rv, rv);
double duration = Duration();
// If there is no end fragment, or the fragment end is greater than the
// duration, return the duration.
*aTime = (mFragmentEnd < 0.0 || mFragmentEnd > duration) ? duration : mFragmentEnd;
return (mFragmentEnd < 0.0 || mFragmentEnd > duration) ? duration : mFragmentEnd;
}
NS_IMETHODIMP HTMLMediaElement::GetMozFragmentEnd(double* aTime)
{
*aTime = MozFragmentEnd();
return NS_OK;
}
@ -3394,34 +3586,44 @@ static double ClampPlaybackRate(double aPlaybackRate)
/* attribute double defaultPlaybackRate; */
NS_IMETHODIMP HTMLMediaElement::GetDefaultPlaybackRate(double* aDefaultPlaybackRate)
{
*aDefaultPlaybackRate = mDefaultPlaybackRate;
*aDefaultPlaybackRate = DefaultPlaybackRate();
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::SetDefaultPlaybackRate(double aDefaultPlaybackRate)
void
HTMLMediaElement::SetDefaultPlaybackRate(double aDefaultPlaybackRate, ErrorResult& aRv)
{
if (aDefaultPlaybackRate < 0) {
return NS_ERROR_NOT_IMPLEMENTED;
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return;
}
mDefaultPlaybackRate = ClampPlaybackRate(aDefaultPlaybackRate);
DispatchAsyncEvent(NS_LITERAL_STRING("ratechange"));
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::SetDefaultPlaybackRate(double aDefaultPlaybackRate)
{
ErrorResult rv;
SetDefaultPlaybackRate(aDefaultPlaybackRate, rv);
return rv.ErrorCode();
}
/* attribute double playbackRate; */
NS_IMETHODIMP HTMLMediaElement::GetPlaybackRate(double* aPlaybackRate)
{
*aPlaybackRate = mPlaybackRate;
*aPlaybackRate = PlaybackRate();
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::SetPlaybackRate(double aPlaybackRate)
void
HTMLMediaElement::SetPlaybackRate(double aPlaybackRate, ErrorResult& aRv)
{
// Changing the playback rate of a media that has more than two channels is
// not supported.
if (aPlaybackRate < 0 || (mChannels > 2 && aPlaybackRate != 1.0)) {
return NS_ERROR_NOT_IMPLEMENTED;
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return;
}
mPlaybackRate = ClampPlaybackRate(aPlaybackRate);
@ -3440,13 +3642,19 @@ NS_IMETHODIMP HTMLMediaElement::SetPlaybackRate(double aPlaybackRate)
mDecoder->SetPlaybackRate(mPlaybackRate);
}
DispatchAsyncEvent(NS_LITERAL_STRING("ratechange"));
return NS_OK;
}
NS_IMETHODIMP HTMLMediaElement::SetPlaybackRate(double aPlaybackRate)
{
ErrorResult rv;
SetPlaybackRate(aPlaybackRate, rv);
return rv.ErrorCode();
}
/* attribute bool mozPreservesPitch; */
NS_IMETHODIMP HTMLMediaElement::GetMozPreservesPitch(bool* aPreservesPitch)
{
*aPreservesPitch = mPreservesPitch;
*aPreservesPitch = MozPreservesPitch();
return NS_OK;
}

View File

@ -353,6 +353,11 @@ MOCHITEST_FILES = \
test_formData.html \
$(NULL)
ifdef MOZ_MEDIA
MOCHITEST_FILES += \
test_mozLoadFrom.html \
$(NULL)
endif
MOCHITEST_BROWSER_FILES = \
browser_bug649778.js \

View File

@ -0,0 +1,19 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for mozLoadFrom</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script>
try {
document.createElement("video").mozLoadFrom({});
ok(false, "This should be throw an exception");
} catch(e) {
ok(true, "This should be throw an exception");
}
</script>
</body>
</html>

View File

@ -156,15 +156,15 @@ is(HTMLElement.prototype.MEDIA_ERR_ABORTED, undefined);
is(HTMLElement.prototype.MEDIA_ERR_NETWORK, undefined);
is(HTMLElement.prototype.MEDIA_ERR_DECODE, undefined);
is(HTMLElement.prototype.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
todo_is(HTMLMediaElement.prototype.NETWORK_EMPTY, 0, "HTMLMediaElement.prototype.NETWORK_EMPTY");
todo_is(HTMLMediaElement.prototype.NETWORK_IDLE, 1, "HTMLMediaElement.prototype.NETWORK_IDLE");
todo_is(HTMLMediaElement.prototype.NETWORK_LOADING, 2, "HTMLMediaElement.prototype.NETWORK_LOADING");
todo_is(HTMLMediaElement.prototype.NETWORK_NO_SOURCE, 3, "HTMLMediaElement.prototype.NETWORK_NO_SOURCE");
todo_is(HTMLMediaElement.prototype.HAVE_NOTHING, 0, "HTMLMediaElement.prototype.HAVE_NOTHING");
todo_is(HTMLMediaElement.prototype.HAVE_METADATA, 1, "HTMLMediaElement.prototype.HAVE_METADATA");
todo_is(HTMLMediaElement.prototype.HAVE_CURRENT_DATA, 2, "HTMLMediaElement.prototype.HAVE_CURRENT_DATA");
todo_is(HTMLMediaElement.prototype.HAVE_FUTURE_DATA, 3, "HTMLMediaElement.prototype.HAVE_FUTURE_DATA");
todo_is(HTMLMediaElement.prototype.HAVE_ENOUGH_DATA, 4, "HTMLMediaElement.prototype.HAVE_ENOUGH_DATA");
is(HTMLMediaElement.prototype.NETWORK_EMPTY, 0, "HTMLMediaElement.prototype.NETWORK_EMPTY");
is(HTMLMediaElement.prototype.NETWORK_IDLE, 1, "HTMLMediaElement.prototype.NETWORK_IDLE");
is(HTMLMediaElement.prototype.NETWORK_LOADING, 2, "HTMLMediaElement.prototype.NETWORK_LOADING");
is(HTMLMediaElement.prototype.NETWORK_NO_SOURCE, 3, "HTMLMediaElement.prototype.NETWORK_NO_SOURCE");
is(HTMLMediaElement.prototype.HAVE_NOTHING, 0, "HTMLMediaElement.prototype.HAVE_NOTHING");
is(HTMLMediaElement.prototype.HAVE_METADATA, 1, "HTMLMediaElement.prototype.HAVE_METADATA");
is(HTMLMediaElement.prototype.HAVE_CURRENT_DATA, 2, "HTMLMediaElement.prototype.HAVE_CURRENT_DATA");
is(HTMLMediaElement.prototype.HAVE_FUTURE_DATA, 3, "HTMLMediaElement.prototype.HAVE_FUTURE_DATA");
is(HTMLMediaElement.prototype.HAVE_ENOUGH_DATA, 4, "HTMLMediaElement.prototype.HAVE_ENOUGH_DATA");
is(HTMLMediaElement.prototype.MEDIA_ERR_ABORTED, undefined, "HTMLMediaElement.prototype.MEDIA_ERR_ABORTED");
is(HTMLMediaElement.prototype.MEDIA_ERR_NETWORK, undefined, "HTMLMediaElement.prototype.MEDIA_ERR_NETWORK");
is(HTMLMediaElement.prototype.MEDIA_ERR_DECODE, undefined, "HTMLMediaElement.prototype.MEDIA_ERR_DECODE");

View File

@ -28,7 +28,7 @@ function doTest() {
b.mozSrcObject = "invalid";
ok(false, "Setting mozSrcObject to an invalid value should throw.");
} catch (e) {
todo(e instanceof TypeError, "Exception should be a TypeError");
ok(e instanceof TypeError, "Exception should be a TypeError");
}
is(b.mozSrcObject, stream, "Stream not set to invalid value");
is(b.src, newSrc, "src attribute not affected by setting srcObject");

View File

@ -13,11 +13,11 @@
<script class="testbody" type="text/javascript">
function test(element, value, shouldThrow) {
var threw = false;
var threw = null;
try {
element.volume = value;
} catch (err if err.name == "IndexSizeError") {
threw = true;
} catch (ex) {
threw = ex.name;
}
is(shouldThrow, threw, "Case: " +element.id+ " setVolume=" + value);
}
@ -27,12 +27,12 @@ var ids = new Array(document.getElementById('v1'), document.getElementById('a1')
for (i=0; i<ids.length; i++) {
var element = ids[i];
test(element, 0.0, false);
test(element, 1.0, false);
test(element, -0.1, true);
test(element, 1.1, true);
test(element, undefined, true);
test(element, NaN, true);
test(element, 0.0, null);
test(element, 1.0, null);
test(element, -0.1, "IndexSizeError");
test(element, 1.1, "IndexSizeError");
test(element, undefined, "TypeError");
test(element, NaN, "TypeError");
}
</script>

View File

@ -0,0 +1,181 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* http://www.whatwg.org/specs/web-apps/current-work/#media-elements
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
*/
interface HTMLMediaElement : HTMLElement {
// error state
readonly attribute MediaError? error;
// network state
[SetterThrows]
attribute DOMString src;
readonly attribute DOMString currentSrc;
// Bug 847370 - crossOrigin vs crossorigin.
[SetterThrows]
attribute DOMString crossorigin;
const unsigned short NETWORK_EMPTY = 0;
const unsigned short NETWORK_IDLE = 1;
const unsigned short NETWORK_LOADING = 2;
const unsigned short NETWORK_NO_SOURCE = 3;
readonly attribute unsigned short networkState;
[SetterThrows]
attribute DOMString preload;
[Creator]
readonly attribute TimeRanges buffered;
void load();
DOMString canPlayType(DOMString type);
// ready state
const unsigned short HAVE_NOTHING = 0;
const unsigned short HAVE_METADATA = 1;
const unsigned short HAVE_CURRENT_DATA = 2;
const unsigned short HAVE_FUTURE_DATA = 3;
const unsigned short HAVE_ENOUGH_DATA = 4;
readonly attribute unsigned short readyState;
readonly attribute boolean seeking;
// playback state
[SetterThrows]
attribute double currentTime;
// TODO: Bug 847375 - void fastSeek(double time);
readonly attribute unrestricted double duration;
// TODO: Bug 847376 - readonly attribute any startDate;
readonly attribute boolean paused;
[SetterThrows]
attribute double defaultPlaybackRate;
[SetterThrows]
attribute double playbackRate;
[Creator]
readonly attribute TimeRanges played;
[Creator]
readonly attribute TimeRanges seekable;
readonly attribute boolean ended;
[SetterThrows]
attribute boolean autoplay;
[SetterThrows]
attribute boolean loop;
[Throws]
void play();
[Throws]
void pause();
// TODO: Bug 847377 - mediaGroup and MediaController
// media controller
// attribute DOMString mediaGroup;
// attribute MediaController? controller;
// controls
[SetterThrows]
attribute boolean controls;
[SetterThrows]
attribute double volume;
attribute boolean muted;
[SetterThrows]
attribute boolean defaultMuted;
// TODO: Bug 847379
// tracks
//readonly attribute AudioTrackList audioTracks;
//readonly attribute VideoTrackList videoTracks;
//readonly attribute TextTrackList textTracks;
//TextTrack addTextTrack(DOMString kind, optional DOMString label, optional DOMString language);
};
// Mozilla extensions:
partial interface HTMLMediaElement {
attribute MediaStream? mozSrcObject;
readonly attribute double initialTime;
attribute boolean mozPreservesPitch;
readonly attribute boolean mozAutoplayEnabled;
// Mozilla extension: stream capture
[Throws]
MediaStream mozCaptureStream();
[Throws]
MediaStream mozCaptureStreamUntilEnded();
readonly attribute boolean mozAudioCaptured;
// Mozilla extension: extra stream metadata information, used as part
// of MozAudioAvailable events and the mozWriteAudio() method. The
// mozFrameBufferLength method allows for the size of the framebuffer
// used within MozAudioAvailable events to be changed. The new size must
// be between 512 and 16384. The default size, for a media element with
// audio is (mozChannels * 1024).
[GetterThrows]
readonly attribute unsigned long mozChannels;
[GetterThrows]
readonly attribute unsigned long mozSampleRate;
[Throws]
attribute unsigned long mozFrameBufferLength;
// Mozilla extension: return embedded metadata from the stream as a
// JSObject with key:value pairs for each tag. This can be used by
// player interfaces to display the song title, artist, etc.
[Throws]
object? mozGetMetadata();
// Mozilla extension: load data from another media element. This is like
// load() but we don't run the resource selection algorithm; instead
// we just set our source to other's currentSrc. This is optimized
// so that this element will get access to all of other's cached/
// buffered data. In fact any future data downloaded by this element or
// other will be sharable by both elements.
[Throws]
void mozLoadFrom(HTMLMediaElement other);
// Mozilla extension: provides access to the fragment end time if
// the media element has a fragment URI for the currentSrc, otherwise
// it is equal to the media duration.
readonly attribute double mozFragmentEnd;
// Mozilla extension: an audio channel type for media elements.
// An exception is thrown if the app tries to change the audio channel type
// without the permission (manifest file for B2G apps).
// The supported values are:
// * normal (default value)
// Automatically paused if "notification" or higher priority channel
// is played
// Use case: normal applications
// * content
// Automatically paused if "notification" or higher priority channel
// is played. Also paused if another app starts using "content"
// channel. Using this channel never affects applications using
// the "normal" channel.
// Use case: video/audio players
// * notification
// Automatically paused if "alarm" or higher priority channel is played.
// Use case: New email, incoming SMS
// * alarm
// Automatically paused if "telephony" or higher priority channel is
// played.
// User case: Alarm clock, calendar alarms
// * telephony
// Automatically paused if "ringer" or higher priority
// channel is played.
// Use case: dialer, voip
// * ringer
// Automatically paused if "publicnotification" or higher priority
// channel is played.
// Use case: dialer, voip
// * publicnotification
// Always plays in speaker, even when headphones are plugged in.
// Use case: Camera shutter sound.
[SetterThrows]
attribute DOMString mozAudioChannelType;
// In addition the media element has this new events:
// * onmozinterruptbegin - called when the media element is interrupted
// because of the audiochannel manager.
// * onmozinterruptend - called when the interruption is concluded
};

View File

@ -96,6 +96,7 @@ webidl_files = \
HTMLLIElement.webidl \
HTMLLinkElement.webidl \
HTMLMapElement.webidl \
HTMLMediaElement.webidl \
HTMLMenuElement.webidl \
HTMLMenuItemElement.webidl \
HTMLMetaElement.webidl \