Bug 464376. Fire progress events reliably and especially be sure to fire a final progress event when the resource finishes loading. r+sr=roc

--HG--
extra : rebase_source : 387866a3c9ad9f05e35e9876ebd1db1224237798
This commit is contained in:
Chris Double 2008-12-18 20:56:32 +13:00
Родитель 91f22a27a1
Коммит c1901dc279
14 изменённых файлов: 292 добавлений и 38 удалений

Просмотреть файл

@ -1065,7 +1065,9 @@ nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event)); nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event));
NS_ENSURE_TRUE(progressEvent, NS_ERROR_FAILURE); NS_ENSURE_TRUE(progressEvent, NS_ERROR_FAILURE);
rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE, PR_FALSE, mDecoder->GetBytesLoaded(), mDecoder->GetTotalBytes()); PRInt64 length = mDecoder->GetTotalBytes();
rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE,
length >= 0, mDecoder->GetBytesLoaded(), length);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
PRBool dummy; PRBool dummy;

Просмотреть файл

@ -68,8 +68,11 @@ class nsChannelToPipeListener : public nsIStreamListener
public: public:
// If aSeeking is PR_TRUE then this listener was created as part of a // If aSeeking is PR_TRUE then this listener was created as part of a
// seek request and is expecting a byte range partial result. // seek request and is expecting a byte range partial result. aOffset
nsChannelToPipeListener(nsMediaDecoder* aDecoder, PRBool aSeeking = PR_FALSE); // is the offset in bytes that this listener started reading from.
nsChannelToPipeListener(nsMediaDecoder* aDecoder,
PRBool aSeeking = PR_FALSE,
PRInt64 aOffset = 0);
nsresult Init(); nsresult Init();
nsresult GetInputStream(nsIInputStream** aStream); nsresult GetInputStream(nsIInputStream** aStream);
void Stop(); void Stop();
@ -95,7 +98,12 @@ private:
// bytes per second download rate. // bytes per second download rate.
PRIntervalTime mIntervalEnd; PRIntervalTime mIntervalEnd;
// Total bytes transferred so far // Offset from the beginning of the resource where the listener
// started reading. This is used for computing the current file
// position for progress events.
PRInt64 mOffset;
// Total bytes transferred so far. Used for computing download rates.
PRInt64 mTotalBytes; PRInt64 mTotalBytes;
// PR_TRUE if this listener is expecting a byte range request result // PR_TRUE if this listener is expecting a byte range request result

Просмотреть файл

@ -164,8 +164,11 @@ class nsMediaDecoder : public nsIObserver
// Invalidate the frame. // Invalidate the frame.
virtual void Invalidate(); virtual void Invalidate();
// Update progress information. // Fire progress events if needed according to the time and byte
virtual void Progress(); // constraints outlined in the specification. aTimer is PR_TRUE
// if the method is called as a result of the progress timer rather
// than the result of downloaded data.
virtual void Progress(PRBool aTimer);
// Keep track of the number of bytes downloaded // Keep track of the number of bytes downloaded
virtual void UpdateBytesDownloaded(PRUint64 aBytes) = 0; virtual void UpdateBytesDownloaded(PRUint64 aBytes) = 0;
@ -210,6 +213,16 @@ protected:
PRInt32 mRGBWidth; PRInt32 mRGBWidth;
PRInt32 mRGBHeight; PRInt32 mRGBHeight;
// Time that the last progress event was fired. Read/Write from the
// main thread only.
PRIntervalTime mProgressTime;
// Time that data was last read from the media resource. Used for
// computing if the download has stalled. A value of 0 indicates that
// a stall event has already fired and not to fire another one until
// more data is received. Read/Write from the main thread only.
PRIntervalTime mDataTime;
// Has our size changed since the last repaint? // Has our size changed since the last repaint?
PRPackedBool mSizeChanged; PRPackedBool mSizeChanged;
@ -239,6 +252,12 @@ protected:
// waiting for the playback event loop to shutdown. Read/Write from the // waiting for the playback event loop to shutdown. Read/Write from the
// main thread only. // main thread only.
PRPackedBool mStopping; PRPackedBool mStopping;
// True when seeking or otherwise moving the play position around in
// such a manner that progress event data is inaccurate. This is set
// before a seek or during loading of metadata to prevent the progress indicator
// from jumping around. Accessed on the main thread only.
PRPackedBool mIgnoreProgressData;
}; };
#endif #endif

Просмотреть файл

@ -512,6 +512,10 @@ private:
// when writing to the state, or when reading from a non-main thread. // when writing to the state, or when reading from a non-main thread.
// Any change to the state must call NotifyAll on the monitor. // Any change to the state must call NotifyAll on the monitor.
PlayState mNextState; PlayState mNextState;
// True when the media resource has completely loaded. Accessed on
// the main thread only.
PRPackedBool mResourceLoaded;
}; };
#endif #endif

Просмотреть файл

@ -274,6 +274,10 @@ private:
// True if the media resource is seekable. // True if the media resource is seekable.
PRPackedBool mSeekable; PRPackedBool mSeekable;
// True when the media resource has completely loaded. Accessed on
// the main thread only.
PRPackedBool mResourceLoaded;
}; };
#endif #endif

Просмотреть файл

@ -47,10 +47,12 @@
nsChannelToPipeListener::nsChannelToPipeListener( nsChannelToPipeListener::nsChannelToPipeListener(
nsMediaDecoder* aDecoder, nsMediaDecoder* aDecoder,
PRBool aSeeking) : PRBool aSeeking,
PRInt64 aOffset) :
mDecoder(aDecoder), mDecoder(aDecoder),
mIntervalStart(0), mIntervalStart(0),
mIntervalEnd(0), mIntervalEnd(0),
mOffset(aOffset),
mTotalBytes(0), mTotalBytes(0),
mSeeking(aSeeking) mSeeking(aSeeking)
{ {
@ -99,7 +101,7 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
mIntervalStart = PR_IntervalNow(); mIntervalStart = PR_IntervalNow();
mIntervalEnd = mIntervalStart; mIntervalEnd = mIntervalStart;
mTotalBytes = 0; mTotalBytes = 0;
mDecoder->UpdateBytesDownloaded(mTotalBytes); mDecoder->UpdateBytesDownloaded(mOffset);
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest); nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
if (hc) { if (hc) {
PRUint32 responseStatus = 0; PRUint32 responseStatus = 0;
@ -150,6 +152,10 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
} }
} }
// Fires an initial progress event and sets up the stall counter so stall events
// fire if no download occurs within the required time frame.
mDecoder->Progress(PR_FALSE);
return NS_OK; return NS_OK;
} }
@ -183,11 +189,16 @@ nsresult nsChannelToPipeListener::OnDataAvailable(nsIRequest* aRequest,
aCount -= bytes; aCount -= bytes;
mTotalBytes += bytes; mTotalBytes += bytes;
mDecoder->UpdateBytesDownloaded(mTotalBytes); mDecoder->UpdateBytesDownloaded(mOffset + aOffset + bytes);
} while (aCount) ; } while (aCount) ;
nsresult rv = mOutput->Flush(); nsresult rv = mOutput->Flush();
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Fire a progress events according to the time and byte constraints outlined
// in the spec.
mDecoder->Progress(PR_FALSE);
mIntervalEnd = PR_IntervalNow(); mIntervalEnd = PR_IntervalNow();
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -52,6 +52,12 @@
#include "nsPresContext.h" #include "nsPresContext.h"
#include "nsMediaDecoder.h" #include "nsMediaDecoder.h"
// Number of milliseconds between progress events as defined by spec
#define PROGRESS_MS 350
// Number of milliseconds of no data before a stall event is fired as defined by spec
#define STALL_MS 3000
#ifdef PR_LOGGING #ifdef PR_LOGGING
// Logging object for decoder // Logging object for decoder
PRLogModuleInfo* gVideoDecoderLog = nsnull; PRLogModuleInfo* gVideoDecoderLog = nsnull;
@ -61,11 +67,14 @@ nsMediaDecoder::nsMediaDecoder() :
mElement(0), mElement(0),
mRGBWidth(-1), mRGBWidth(-1),
mRGBHeight(-1), mRGBHeight(-1),
mProgressTime(0),
mDataTime(0),
mSizeChanged(PR_FALSE), mSizeChanged(PR_FALSE),
mVideoUpdateLock(nsnull), mVideoUpdateLock(nsnull),
mFramerate(0.0), mFramerate(0.0),
mShuttingDown(PR_FALSE), mShuttingDown(PR_FALSE),
mStopping(PR_FALSE) mStopping(PR_FALSE),
mIgnoreProgressData(PR_TRUE)
{ {
MOZ_COUNT_CTOR(nsMediaDecoder); MOZ_COUNT_CTOR(nsMediaDecoder);
} }
@ -129,38 +138,55 @@ void nsMediaDecoder::Invalidate()
static void ProgressCallback(nsITimer* aTimer, void* aClosure) static void ProgressCallback(nsITimer* aTimer, void* aClosure)
{ {
nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure); nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
decoder->Progress(); decoder->Progress(PR_TRUE);
} }
void nsMediaDecoder::Progress() void nsMediaDecoder::Progress(PRBool aTimer)
{ {
if (!mElement) if (!mElement || mIgnoreProgressData)
return; return;
PRIntervalTime now = PR_IntervalNow();
if (mProgressTime == 0 ||
PR_IntervalToMilliseconds(PR_IntervalNow() - mProgressTime) >= PROGRESS_MS) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress")); mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
mProgressTime = now;
}
// The test for aTimer is to ensure that we dispatch 'stalled'
// only when we are not receiving data.
if (aTimer &&
mDataTime != 0 &&
PR_IntervalToMilliseconds(now - mDataTime) >= STALL_MS) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("stalled"));
mDataTime = 0;
}
if (!aTimer) {
mDataTime = now;
}
} }
nsresult nsMediaDecoder::StartProgress() nsresult nsMediaDecoder::StartProgress()
{ {
nsresult rv = NS_OK; if (mProgressTimer)
return NS_OK;
if (!mProgressTimer) {
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1"); mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
rv = mProgressTimer->InitWithFuncCallback(ProgressCallback, return mProgressTimer->InitWithFuncCallback(ProgressCallback,
this, this,
350, // Number of milliseconds defined in spec PROGRESS_MS,
nsITimer::TYPE_REPEATING_PRECISE); nsITimer::TYPE_REPEATING_PRECISE);
} }
return rv;
}
nsresult nsMediaDecoder::StopProgress() nsresult nsMediaDecoder::StopProgress()
{ {
nsresult rv = NS_OK; if (!mProgressTimer)
if (mProgressTimer) { return NS_OK;
rv = mProgressTimer->Cancel();
nsresult rv = mProgressTimer->Cancel();
mProgressTimer = nsnull; mProgressTimer = nsnull;
}
return rv; return rv;
} }

Просмотреть файл

@ -270,6 +270,7 @@ nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
rv = mInput->Available(&size); rv = mInput->Available(&size);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
mDecoder->SetTotalBytes(size); mDecoder->SetTotalBytes(size);
mDecoder->UpdateBytesDownloaded(size);
} }
/* Get our principal */ /* Get our principal */
@ -546,7 +547,7 @@ public:
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE); hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
} }
mListener = new nsChannelToPipeListener(mDecoder, PR_TRUE); mListener = new nsChannelToPipeListener(mDecoder, PR_TRUE, mOffset);
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
mResult = mListener->Init(); mResult = mListener->Init();

Просмотреть файл

@ -1198,7 +1198,8 @@ nsOggDecoder::nsOggDecoder() :
mReader(0), mReader(0),
mMonitor(0), mMonitor(0),
mPlayState(PLAY_STATE_PAUSED), mPlayState(PLAY_STATE_PAUSED),
mNextState(PLAY_STATE_PAUSED) mNextState(PLAY_STATE_PAUSED),
mResourceLoaded(PR_FALSE)
{ {
MOZ_COUNT_CTOR(nsOggDecoder); MOZ_COUNT_CTOR(nsOggDecoder);
} }
@ -1232,6 +1233,11 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
// reusing decoder. // reusing decoder.
mStopping = PR_FALSE; mStopping = PR_FALSE;
// Reset progress member variables
mIgnoreProgressData = PR_TRUE;
mBytesDownloaded = 0;
mResourceLoaded = PR_FALSE;
NS_ASSERTION(!mReader, "Didn't shutdown properly!"); NS_ASSERTION(!mReader, "Didn't shutdown properly!");
NS_ASSERTION(!mDecodeStateMachine, "Didn't shutdown properly!"); NS_ASSERTION(!mDecodeStateMachine, "Didn't shutdown properly!");
NS_ASSERTION(!mDecodeThread, "Didn't shutdown properly!"); NS_ASSERTION(!mDecodeThread, "Didn't shutdown properly!");
@ -1254,8 +1260,6 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
StartProgress();
RegisterShutdownObserver(); RegisterShutdownObserver();
mReader = new nsChannelReader(); mReader = new nsChannelReader();
@ -1365,6 +1369,7 @@ void nsOggDecoder::Stop()
ChangeState(PLAY_STATE_ENDED); ChangeState(PLAY_STATE_ENDED);
mIgnoreProgressData = PR_TRUE;
StopProgress(); StopProgress();
// Force any outstanding seek and byterange requests to complete // Force any outstanding seek and byterange requests to complete
@ -1441,6 +1446,18 @@ void nsOggDecoder::MetadataLoaded()
if (mElement && notifyElement) { if (mElement && notifyElement) {
mElement->MetadataLoaded(); mElement->MetadataLoaded();
} }
if (!mResourceLoaded) {
StartProgress();
}
else if (mElement)
{
// Resource was loaded during metadata loading, when progress
// events are being ignored. Fire the final progress event.
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
}
mIgnoreProgressData = PR_FALSE;
} }
void nsOggDecoder::FirstFrameLoaded() void nsOggDecoder::FirstFrameLoaded()
@ -1477,13 +1494,39 @@ void nsOggDecoder::FirstFrameLoaded()
void nsOggDecoder::ResourceLoaded() void nsOggDecoder::ResourceLoaded()
{ {
if (mShuttingDown) // Don't handle ResourceLoaded if we are shutting down, or if
// we need to ignore progress data due to seeking (in the case
// that the seek results in reaching end of file, we get a bogus call
// to ResourceLoaded).
if (mShuttingDown || mIgnoreProgressData)
return; return;
Progress(PR_FALSE);
{
// If we are seeking or loading then the resource loaded notification we get
// should be ignored, since it represents the end of the seek request.
nsAutoMonitor mon(mMonitor);
if (mPlayState == PLAY_STATE_SEEKING || mPlayState == PLAY_STATE_LOADING)
return;
}
// If we know the content length, set the bytes downloaded to this
// so the final progress event gets the correct final value.
if (mContentLength >= 0) {
mBytesDownloaded = mContentLength;
}
mResourceLoaded = PR_TRUE;
StopProgress();
// Ensure the final progress event gets fired
if (mElement && !mIgnoreProgressData) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
}
if (mElement) { if (mElement) {
mElement->ResourceLoaded(); mElement->ResourceLoaded();
} }
StopProgress();
} }
void nsOggDecoder::NetworkError() void nsOggDecoder::NetworkError()
@ -1549,8 +1592,12 @@ void nsOggDecoder::SetTotalBytes(PRInt64 aBytes)
void nsOggDecoder::UpdateBytesDownloaded(PRUint64 aBytes) void nsOggDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
{ {
nsAutoMonitor mon(mMonitor);
if (!mIgnoreProgressData) {
mBytesDownloaded = aBytes; mBytesDownloaded = aBytes;
} }
}
void nsOggDecoder::BufferingStopped() void nsOggDecoder::BufferingStopped()
{ {
@ -1577,6 +1624,8 @@ void nsOggDecoder::SeekingStopped()
if (mShuttingDown) if (mShuttingDown)
return; return;
mIgnoreProgressData = PR_FALSE;
{ {
nsAutoMonitor mon(mMonitor); nsAutoMonitor mon(mMonitor);
@ -1598,6 +1647,8 @@ void nsOggDecoder::SeekingStarted()
if (mShuttingDown) if (mShuttingDown)
return; return;
mIgnoreProgressData = PR_TRUE;
if (mElement) { if (mElement) {
mElement->SeekStarted(); mElement->SeekStarted();
} }
@ -1731,4 +1782,3 @@ PRBool nsOggDecoder::GetSeekable()
{ {
return mSeekable; return mSeekable;
} }

Просмотреть файл

@ -982,7 +982,8 @@ nsWaveDecoder::nsWaveDecoder()
mEndedDuration(std::numeric_limits<float>::quiet_NaN()), mEndedDuration(std::numeric_limits<float>::quiet_NaN()),
mEnded(PR_FALSE), mEnded(PR_FALSE),
mNotifyOnShutdown(PR_FALSE), mNotifyOnShutdown(PR_FALSE),
mSeekable(PR_TRUE) mSeekable(PR_TRUE),
mResourceLoaded(PR_FALSE)
{ {
MOZ_COUNT_CTOR(nsWaveDecoder); MOZ_COUNT_CTOR(nsWaveDecoder);
} }
@ -1098,6 +1099,7 @@ nsWaveDecoder::Stop()
mStopping = PR_TRUE; mStopping = PR_TRUE;
mIgnoreProgressData = PR_TRUE;
StopProgress(); StopProgress();
if (mPlaybackStateMachine) { if (mPlaybackStateMachine) {
@ -1130,6 +1132,11 @@ nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStr
{ {
mStopping = PR_FALSE; mStopping = PR_FALSE;
// Reset progress member variables
mIgnoreProgressData = PR_TRUE;
mBytesDownloaded = 0;
mResourceLoaded = PR_FALSE;
if (aStreamListener) { if (aStreamListener) {
*aStreamListener = nsnull; *aStreamListener = nsnull;
} }
@ -1145,7 +1152,6 @@ nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStr
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
StartProgress();
RegisterShutdownObserver(); RegisterShutdownObserver();
mStream = new nsMediaStream(); mStream = new nsMediaStream();
@ -1177,6 +1183,18 @@ nsWaveDecoder::MetadataLoaded()
mElement->MetadataLoaded(); mElement->MetadataLoaded();
mElement->FirstFrameLoaded(); mElement->FirstFrameLoaded();
} }
if (!mResourceLoaded) {
StartProgress();
}
else if (mElement)
{
// Resource was loaded during metadata loading, when progress
// events are being ignored. Fire the final progress event.
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
}
mIgnoreProgressData = PR_FALSE;
} }
void void
@ -1198,13 +1216,28 @@ nsWaveDecoder::ResourceLoaded()
if (mShuttingDown) { if (mShuttingDown) {
return; return;
} }
// If we know the content length, set the bytes downloaded to this
// so the final progress event gets the correct final value.
if (mContentLength >= 0) {
mBytesDownloaded = mContentLength;
}
mResourceLoaded = PR_TRUE;
if (mElement) { if (mElement) {
mElement->ResourceLoaded(); mElement->ResourceLoaded();
} }
if (mPlaybackStateMachine) { if (mPlaybackStateMachine) {
mPlaybackStateMachine->StreamEnded(); mPlaybackStateMachine->StreamEnded();
} }
StopProgress(); StopProgress();
// Ensure the final progress event gets fired
if (mElement && !mIgnoreProgressData) {
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
}
} }
void void
@ -1261,8 +1294,10 @@ nsWaveDecoder::SetTotalBytes(PRInt64 aBytes)
void void
nsWaveDecoder::UpdateBytesDownloaded(PRUint64 aBytes) nsWaveDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
{ {
if (!mIgnoreProgressData) {
mBytesDownloaded = aBytes; mBytesDownloaded = aBytes;
} }
}
// An event that gets posted to the main thread, when the media element is // An event that gets posted to the main thread, when the media element is
// being destroyed, to destroy the decoder. Since the decoder shutdown can // being destroyed, to destroy the decoder. Since the decoder shutdown can

Просмотреть файл

@ -67,6 +67,7 @@ _TEST_FILES += \
test_ended1.html \ test_ended1.html \
test_ended2.html \ test_ended2.html \
test_onloadedmetadata.html \ test_onloadedmetadata.html \
test_progress1.html \
test_seek1.html \ test_seek1.html \
test_seek3.html \ test_seek3.html \
test_seek4.html \ test_seek4.html \
@ -90,10 +91,12 @@ endif
ifdef MOZ_WAVE ifdef MOZ_WAVE
_TEST_FILES += \ _TEST_FILES += \
big.wav \
test_bug463162.xhtml \ test_bug463162.xhtml \
test_can_play_type_wave.html \ test_can_play_type_wave.html \
test_wav_8bit.html \ test_wav_8bit.html \
test_wav_ended1.html \ test_wav_ended1.html \
test_progress2.html \
test_wav_seek3.html \ test_wav_seek3.html \
test_wav_seek4.html \ test_wav_seek4.html \
test_wav_seek5.html \ test_wav_seek5.html \

Двоичные данные
content/media/video/test/big.wav Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Media test: progress events</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var completed = false;
var last_progress = false;
function on_loadedmetadata() {
var v = document.getElementById('v');
v.play();
}
function do_progress(e) {
ok(!completed, "Check for progress event after completed: " + completed);
ok(e.lengthComputable, "Check progress lengthComputable");
ok(e.loaded >= 0 && e.loaded <= e.total, "Check progress loaded: " + e.loaded);
ok(e.total == 285310, "Check progress total: " + e.total);
last_progress = e.loaded;
}
function do_ended() {
ok(!completed, "Check for duplicate ended event");
completed = true;
ok(last_progress == 285310, "Last progress event size: " + last_progress);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<video id='v'
src='seek.ogg'
onloadedmetadata='on_loadedmetadata()'
onended='do_ended()'
onprogress='do_progress(event)'>
</video>
</body>
</html>

Просмотреть файл

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Media test: progress events</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
// Test progress events with wav backend
var completed = false;
var last_progress = false;
function on_loadedmetadata() {
var v = document.getElementById('v');
v.play();
}
function do_progress(e) {
ok(!completed, "Check for progress event after completed: " + completed);
ok(e.lengthComputable, "Check progress lengthComputable");
ok(e.loaded >= 0 && e.loaded <= e.total, "Check progress loaded: " + e.loaded);
ok(e.total == 102444, "Check progress total: " + e.total);
last_progress = e.loaded;
}
function do_ended() {
ok(!completed, "Check for duplicate ended event");
completed = true;
ok(last_progress == 102444, "Last progress event size: " + last_progress);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<audio id='v'
src='big.wav'
onloadedmetadata='on_loadedmetadata()'
onended='do_ended()'
onprogress='do_progress(event)'>
</audio>
</body>
</html>