Merge mc to tm.

This commit is contained in:
Robert Sayre 2009-01-15 19:04:23 -05:00
commit 3b2b3f024d
27 changed files with 635 additions and 203 deletions

View File

@ -102,9 +102,6 @@ http://sub1.test1.example.com:80 privileged
http://sub1.test2.example.com:80 privileged
http://sub2.test1.example.com:80 privileged
http://sub2.test2.example.com:80 privileged
http://nocert.example.com:80 privileged
http://requestclientcert.example.com:80 privileged
http://requireclientcert.example.com:80 privileged
https://example.com:443 privileged
https://test1.example.com:443 privileged

View File

@ -1058,8 +1058,10 @@ nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event));
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);
PRBool dummy;

View File

@ -68,8 +68,11 @@ class nsChannelToPipeListener : public nsIStreamListener
public:
// If aSeeking is PR_TRUE then this listener was created as part of a
// seek request and is expecting a byte range partial result.
nsChannelToPipeListener(nsMediaDecoder* aDecoder, PRBool aSeeking = PR_FALSE);
// seek request and is expecting a byte range partial result. aOffset
// is the offset in bytes that this listener started reading from.
nsChannelToPipeListener(nsMediaDecoder* aDecoder,
PRBool aSeeking = PR_FALSE,
PRInt64 aOffset = 0);
nsresult Init();
nsresult GetInputStream(nsIInputStream** aStream);
void Stop();
@ -95,7 +98,12 @@ private:
// bytes per second download rate.
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;
// PR_TRUE if this listener is expecting a byte range request result

View File

@ -164,8 +164,11 @@ class nsMediaDecoder : public nsIObserver
// Invalidate the frame.
virtual void Invalidate();
// Update progress information.
virtual void Progress();
// Fire progress events if needed according to the time and byte
// 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
virtual void UpdateBytesDownloaded(PRUint64 aBytes) = 0;
@ -210,6 +213,16 @@ protected:
PRInt32 mRGBWidth;
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?
PRPackedBool mSizeChanged;

View File

@ -371,6 +371,15 @@ protected:
return mPlayState;
}
// Stop updating the bytes downloaded for progress notifications. Called
// when seeking to prevent wild changes to the progress notification.
// Must be called with the decoder monitor held.
void StopProgressUpdates();
// Allow updating the bytes downloaded for progress notifications. Must
// be called with the decoder monitor held.
void StartProgressUpdates();
/******
* The following methods must only be called on the main
* thread.
@ -512,6 +521,17 @@ private:
// when writing to the state, or when reading from a non-main thread.
// Any change to the state must call NotifyAll on the monitor.
PlayState mNextState;
// True when the media resource has completely loaded. Accessed on
// the main thread only.
PRPackedBool mResourceLoaded;
// 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. Read/Write from any thread. Must have decode monitor
// locked before accessing.
PRPackedBool mIgnoreProgressData;
};
#endif

View File

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

View File

@ -47,10 +47,12 @@
nsChannelToPipeListener::nsChannelToPipeListener(
nsMediaDecoder* aDecoder,
PRBool aSeeking) :
PRBool aSeeking,
PRInt64 aOffset) :
mDecoder(aDecoder),
mIntervalStart(0),
mIntervalEnd(0),
mOffset(aOffset),
mTotalBytes(0),
mSeeking(aSeeking)
{
@ -99,7 +101,7 @@ nsresult nsChannelToPipeListener::OnStartRequest(nsIRequest* aRequest, nsISuppor
mIntervalStart = PR_IntervalNow();
mIntervalEnd = mIntervalStart;
mTotalBytes = 0;
mDecoder->UpdateBytesDownloaded(mTotalBytes);
mDecoder->UpdateBytesDownloaded(mOffset);
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
if (hc) {
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;
}
@ -184,11 +190,16 @@ nsresult nsChannelToPipeListener::OnDataAvailable(nsIRequest* aRequest,
aCount -= bytes;
mTotalBytes += bytes;
mDecoder->UpdateBytesDownloaded(mTotalBytes);
mDecoder->UpdateBytesDownloaded(mOffset + aOffset + bytes);
} while (aCount) ;
nsresult rv = mOutput->Flush();
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();
return NS_OK;
}

View File

@ -52,6 +52,12 @@
#include "nsPresContext.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
// Logging object for decoder
PRLogModuleInfo* gVideoDecoderLog = nsnull;
@ -61,6 +67,8 @@ nsMediaDecoder::nsMediaDecoder() :
mElement(0),
mRGBWidth(-1),
mRGBHeight(-1),
mProgressTime(0),
mDataTime(0),
mSizeChanged(PR_FALSE),
mVideoUpdateLock(nsnull),
mFramerate(0.0),
@ -129,38 +137,55 @@ void nsMediaDecoder::Invalidate()
static void ProgressCallback(nsITimer* aTimer, void* aClosure)
{
nsMediaDecoder* decoder = static_cast<nsMediaDecoder*>(aClosure);
decoder->Progress();
decoder->Progress(PR_TRUE);
}
void nsMediaDecoder::Progress()
void nsMediaDecoder::Progress(PRBool aTimer)
{
if (!mElement)
return;
mElement->DispatchProgressEvent(NS_LITERAL_STRING("progress"));
PRIntervalTime now = PR_IntervalNow();
if (mProgressTime == 0 ||
PR_IntervalToMilliseconds(PR_IntervalNow() - mProgressTime) >= PROGRESS_MS) {
mElement->DispatchAsyncProgressEvent(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->DispatchAsyncProgressEvent(NS_LITERAL_STRING("stalled"));
mDataTime = 0;
}
if (!aTimer) {
mDataTime = now;
}
}
nsresult nsMediaDecoder::StartProgress()
{
nsresult rv = NS_OK;
if (mProgressTimer)
return NS_OK;
if (!mProgressTimer) {
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
rv = mProgressTimer->InitWithFuncCallback(ProgressCallback,
mProgressTimer = do_CreateInstance("@mozilla.org/timer;1");
return mProgressTimer->InitWithFuncCallback(ProgressCallback,
this,
350, // Number of milliseconds defined in spec
nsITimer::TYPE_REPEATING_PRECISE);
}
return rv;
PROGRESS_MS,
nsITimer::TYPE_REPEATING_SLACK);
}
nsresult nsMediaDecoder::StopProgress()
{
nsresult rv = NS_OK;
if (mProgressTimer) {
rv = mProgressTimer->Cancel();
mProgressTimer = nsnull;
}
if (!mProgressTimer)
return NS_OK;
nsresult rv = mProgressTimer->Cancel();
mProgressTimer = nsnull;
return rv;
}

View File

@ -270,6 +270,7 @@ nsresult nsFileStreamStrategy::Open(nsIStreamListener** aStreamListener)
rv = mInput->Available(&size);
if (NS_SUCCEEDED(rv)) {
mDecoder->SetTotalBytes(size);
mDecoder->UpdateBytesDownloaded(size);
}
/* Get our principal */
@ -546,7 +547,7 @@ public:
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);
mResult = mListener->Init();

View File

@ -958,6 +958,7 @@ nsresult nsOggDecodeStateMachine::Run()
// acquiring the lock again in case shutdown has occurred
// during the time when we didn't have the lock.
float seekTime = mSeekTime;
mDecoder->StopProgressUpdates();
mon.Exit();
nsCOMPtr<nsIRunnable> startEvent =
NS_NEW_RUNNABLE_METHOD(nsOggDecoder, mDecoder, SeekingStarted);
@ -975,6 +976,7 @@ nsresult nsOggDecodeStateMachine::Run()
}
mon.Enter();
mDecoder->StartProgressUpdates();
if (mState == DECODER_STATE_SHUTDOWN)
continue;
@ -1128,6 +1130,7 @@ void nsOggDecodeStateMachine::LoadOggHeaders()
if (mState != DECODER_STATE_SHUTDOWN &&
mContentLength >= 0 &&
mSeekable) {
mDecoder->StopProgressUpdates();
// Don't hold the monitor during the duration
// call as it can issue seek requests
// and blocks until these are completed.
@ -1135,6 +1138,7 @@ void nsOggDecodeStateMachine::LoadOggHeaders()
PRInt64 d = oggplay_get_duration(mPlayer);
mon.Enter();
mDuration = d;
mDecoder->StartProgressUpdates();
}
if (mState == DECODER_STATE_SHUTDOWN)
return;
@ -1198,7 +1202,9 @@ nsOggDecoder::nsOggDecoder() :
mReader(0),
mMonitor(0),
mPlayState(PLAY_STATE_PAUSED),
mNextState(PLAY_STATE_PAUSED)
mNextState(PLAY_STATE_PAUSED),
mResourceLoaded(PR_FALSE),
mIgnoreProgressData(PR_FALSE)
{
MOZ_COUNT_CTOR(nsOggDecoder);
}
@ -1232,6 +1238,10 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
// reusing decoder.
mStopping = PR_FALSE;
// Reset progress member variables
mBytesDownloaded = 0;
mResourceLoaded = PR_FALSE;
NS_ASSERTION(!mReader, "Didn't shutdown properly!");
NS_ASSERTION(!mDecodeStateMachine, "Didn't shutdown properly!");
NS_ASSERTION(!mDecodeThread, "Didn't shutdown properly!");
@ -1254,8 +1264,6 @@ nsresult nsOggDecoder::Load(nsIURI* aURI, nsIChannel* aChannel,
NS_ENSURE_SUCCESS(rv, rv);
}
StartProgress();
RegisterShutdownObserver();
mReader = new nsChannelReader();
@ -1441,6 +1449,16 @@ void nsOggDecoder::MetadataLoaded()
if (mElement && notifyElement) {
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->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
}
}
void nsOggDecoder::FirstFrameLoaded()
@ -1473,17 +1491,48 @@ void nsOggDecoder::FirstFrameLoaded()
ChangeState(mNextState);
}
}
if (!mResourceLoaded && mBytesDownloaded == mContentLength) {
ResourceLoaded();
}
}
void nsOggDecoder::ResourceLoaded()
{
// 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)
return;
PRBool ignoreProgress = 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);
ignoreProgress = mIgnoreProgressData;
if (ignoreProgress || mResourceLoaded || mPlayState == PLAY_STATE_LOADING)
return;
}
Progress(PR_FALSE);
// 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) {
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
mElement->ResourceLoaded();
}
StopProgress();
}
void nsOggDecoder::NetworkError()
@ -1549,7 +1598,11 @@ void nsOggDecoder::SetTotalBytes(PRInt64 aBytes)
void nsOggDecoder::UpdateBytesDownloaded(PRUint64 aBytes)
{
mBytesDownloaded = aBytes;
nsAutoMonitor mon(mMonitor);
if (!mIgnoreProgressData) {
mBytesDownloaded = aBytes;
}
}
void nsOggDecoder::BufferingStopped()
@ -1732,3 +1785,12 @@ PRBool nsOggDecoder::GetSeekable()
return mSeekable;
}
void nsOggDecoder::StopProgressUpdates()
{
mIgnoreProgressData = PR_TRUE;
}
void nsOggDecoder::StartProgressUpdates()
{
mIgnoreProgressData = PR_FALSE;
}

View File

@ -982,7 +982,8 @@ nsWaveDecoder::nsWaveDecoder()
mEndedDuration(std::numeric_limits<float>::quiet_NaN()),
mEnded(PR_FALSE),
mNotifyOnShutdown(PR_FALSE),
mSeekable(PR_TRUE)
mSeekable(PR_TRUE),
mResourceLoaded(PR_FALSE)
{
MOZ_COUNT_CTOR(nsWaveDecoder);
}
@ -1130,6 +1131,10 @@ nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStr
{
mStopping = PR_FALSE;
// Reset progress member variables
mBytesDownloaded = 0;
mResourceLoaded = PR_FALSE;
if (aStreamListener) {
*aStreamListener = nsnull;
}
@ -1145,7 +1150,6 @@ nsWaveDecoder::Load(nsIURI* aURI, nsIChannel* aChannel, nsIStreamListener** aStr
NS_ENSURE_SUCCESS(rv, rv);
}
StartProgress();
RegisterShutdownObserver();
mStream = new nsMediaStream();
@ -1177,6 +1181,16 @@ nsWaveDecoder::MetadataLoaded()
mElement->MetadataLoaded();
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->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
}
}
void
@ -1198,13 +1212,28 @@ nsWaveDecoder::ResourceLoaded()
if (mShuttingDown) {
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) {
mElement->ResourceLoaded();
}
if (mPlaybackStateMachine) {
mPlaybackStateMachine->StreamEnded();
}
StopProgress();
// Ensure the final progress event gets fired
if (mElement) {
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
}
}
void

View File

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

Binary file not shown.

View File

@ -0,0 +1,69 @@
<!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 load_count = 0;
var last_progress = false;
function on_loadedmetadata() {
var v = document.getElementById('v');
ok(v, "Found video element after metadata loaded: " + v);
v.play();
dump('test_progress1: on_loadedmetadata exiting\n');
}
function do_progress(e) {
dump('test_progress1: do_progress ' + e.loaded + '\n');
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() {
dump('test_progress1: do_ended\n');
ok(!completed, "Check for duplicate ended event");
completed = true;
ok(last_progress == 285310, "Last progress event size: " + last_progress);
ok(load_count == 1, "load event raised: " + load_count);
SimpleTest.finish();
}
function do_load(e) {
load_count++;
dump('test_progress1: do_loaded ' + e.loaded + "\n");
}
function do_timeupdate() {
var v = document.getElementById('v');
dump('test_progress1: timeupdate: ' + v.currentTime + "\n");
}
function do_play() {
dump('test_progress1: do_play\n');
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<video id='v'
src='seek.ogg'
onloadedmetadata='on_loadedmetadata()'
onended='do_ended()'
ontimeupdate='do_timeupdate()'
onload='do_load(event)'
onplay='do_play()'
onprogress='do_progress(event)'>
</video>
</body>
</html>

View File

@ -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>

View File

@ -0,0 +1,70 @@
<!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">
// Same as test_progress1 but uses a smaller file to test resource loaded
// before metadata loaded is fired.
var completed = false;
var load_count = 0;
var last_progress = false;
function on_loadedmetadata() {
var v = document.getElementById('v');
ok(v, "Found video element after metadata loaded: " + v);
v.play();
dump('test_progress1: on_loadedmetadata exiting\n');
}
function do_progress(e) {
dump('test_progress1: do_progress ' + e.loaded + '\n');
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 == 28942, "Check progress total: " + e.total);
last_progress = e.loaded;
}
function do_ended() {
dump('test_progress1: do_ended\n');
ok(!completed, "Check for duplicate ended event");
completed = true;
ok(last_progress == 28942, "Last progress event size: " + last_progress);
ok(load_count == 1, "load event raised: " + load_count);
SimpleTest.finish();
}
function do_load(e) {
load_count++;
dump('test_progress1: do_loaded ' + e.loaded + "\n");
}
function do_timeupdate() {
var v = document.getElementById('v');
dump('test_progress1: timeupdate: ' + v.currentTime + "\n");
}
function do_play() {
dump('test_progress1: do_play\n');
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<video id='v'
src='320x240.ogg'
onloadedmetadata='on_loadedmetadata()'
onended='do_ended()'
ontimeupdate='do_timeupdate()'
onload='do_load(event)'
onplay='do_play()'
onprogress='do_progress(event)'>
</video>
</body>
</html>

View File

@ -0,0 +1,48 @@
<!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
// Same as test_progress2 but uses a smaller file to test resource loaded
// before metadata loaded is fired.
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 == 11069, "Check progress total: " + e.total);
last_progress = e.loaded;
}
function do_ended() {
ok(!completed, "Check for duplicate ended event");
completed = true;
ok(last_progress == 11069, "Last progress event size: " + last_progress);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<audio id='v'
src='r11025_u8_c1.wav'
onloadedmetadata='on_loadedmetadata()'
onended='do_ended()'
onprogress='do_progress(event)'>
</audio>
</body>
</html>

View File

@ -1171,7 +1171,7 @@ NS_IMETHODIMP
nsJSProtocolHandler::GetProtocolFlags(PRUint32 *result)
{
*result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE;
URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_OPENING_EXECUTES_SCRIPT;
return NS_OK;
}

View File

@ -0,0 +1,16 @@
<html class="reftest-wait"><head><script>
function doe2() {
document.getElementById('a').setAttribute('style', 'display: inline-block;');
document.body.offsetHeight;
document.getElementById('b').removeAttribute('style');
document.body.offsetHeight;
document.documentElement.className = '';
}
setTimeout(doe2,200,0);
</script>
</head>
<body style="display: -moz-inline-box;"><span style="display: inline-block;"><span style="display: inline-block;"></span></span><span id="a">
<iframe></iframe>
<div id="b" style="display: table-footer-group;"></div>
</span></body>
</html>

View File

@ -42,6 +42,7 @@ load 369038-1.xhtml
load 369150-1.html
load 369150-2.html
load 369227-1.xhtml
load 369547-1.html
load 370699-1.html
load 370794-1.html
load 370866-1.xhtml

View File

@ -215,7 +215,7 @@ random == 99850-1b.html 99850-1-ref.html # bug 471629
== 273681-1.html 273681-1-ref.html
== 278266-1a.html 278266-1-ref.html
== 278266-1b.html 278266-1-ref.html
== 280708-1a.html 280708-1-ref.html
fails == 280708-1a.html 280708-1-ref.html # bug 473824
== 280708-1b.html 280708-1-ref.html
== 283686-1.html about:blank
== 283686-2.html 283686-2-ref.html

View File

@ -1670,7 +1670,7 @@ _hasmethod(NPP npp, NPObject* npobj, NPIdentifier methodName)
("NPN_HasMethod(npp %p, npobj %p, property %p) called\n",
npp, npobj, methodName));
return npobj->_class->hasProperty(npobj, methodName);
return npobj->_class->hasMethod(npobj, methodName);
}
bool NP_CALLBACK

View File

@ -229,6 +229,11 @@ interface nsIProtocolHandler : nsISupports
*/
const unsigned long URI_IS_LOCAL_RESOURCE = (1<<12);
/**
* URIs for this protocol execute script when they are opened.
*/
const unsigned long URI_OPENING_EXECUTES_SCRIPT = (1<<13);
/**
* This protocol handler can be proxied via a proxy (socks or http)
* (e.g., irc, smtp, http, etc.). If the protocol supports transparent

View File

@ -1046,13 +1046,19 @@ void CViewSourceHTML::WriteHrefAttribute(nsTokenAllocator* allocator,
CreateViewSourceURL(hrefProper, viewSourceUrl);
// Construct the HTML that will represent the HREF.
NS_NAMED_LITERAL_STRING(HREF, "href");
if (fullPrecedingText.Length() > 0) {
WriteTextInSpan(fullPrecedingText, allocator, EmptyString(), EmptyString());
}
WriteTextInAnchor(hrefProper, allocator, HREF, viewSourceUrl);
if (succeedingText.Length() > 0) {
WriteTextInSpan(succeedingText, allocator, EmptyString(), EmptyString());
if (viewSourceUrl.IsEmpty()) {
nsAutoString equalsHref(kEqual);
equalsHref.Append(href);
WriteTextInSpan(equalsHref, allocator, EmptyString(), EmptyString());
} else {
NS_NAMED_LITERAL_STRING(HREF, "href");
if (fullPrecedingText.Length() > 0) {
WriteTextInSpan(fullPrecedingText, allocator, EmptyString(), EmptyString());
}
WriteTextInAnchor(hrefProper, allocator, HREF, viewSourceUrl);
if (succeedingText.Length() > 0) {
WriteTextInSpan(succeedingText, allocator, EmptyString(), EmptyString());
}
}
}
@ -1062,9 +1068,7 @@ nsresult CViewSourceHTML::CreateViewSourceURL(const nsAString& linkUrl,
nsCOMPtr<nsIURI> hrefURI;
nsresult rv;
// Default the view source URL to the empty string in case we fail. Links
// with empty HREFs are essentially non-functional, at least as of Firefox
// 3.03. This is preferrable behavior to links that look good but then 404.
// Default the view source URL to the empty string in case we fail.
viewSourceUrl.Truncate();
// Get the character set.
@ -1084,8 +1088,29 @@ nsresult CViewSourceHTML::CreateViewSourceURL(const nsAString& linkUrl,
nsCString absoluteLinkUrl;
hrefURI->GetSpec(absoluteLinkUrl);
// Prepend "view-source:" onto the absolute URL and store it in the out param.
viewSourceUrl.AssignLiteral("view-source:");
// URLs that execute script (e.g. "javascript:" URLs) should just be
// ignored. There's nothing reasonable we can do with them, and allowing
// them to execute in the context of the view-source window presents a
// security risk. Just return the empty string in this case.
PRBool openingExecutesScript = PR_FALSE;
rv = NS_URIChainHasFlags(hrefURI, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT,
&openingExecutesScript);
NS_ENSURE_SUCCESS(rv, NS_OK); // if there's an error, return the empty string
if (openingExecutesScript) {
return NS_OK;
}
// URLs that return data (e.g. "http:" URLs) should be prefixed with
// "view-source:". URLs that don't return data should just be returned
// undecorated.
PRBool doesNotReturnData = PR_FALSE;
rv = NS_URIChainHasFlags(hrefURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
&doesNotReturnData);
NS_ENSURE_SUCCESS(rv, NS_OK); // if there's an error, return the empty string
if (!doesNotReturnData) {
viewSourceUrl.AssignLiteral("view-source:");
}
viewSourceUrl.AppendWithConversion(absoluteLinkUrl);
return NS_OK;

View File

@ -20,7 +20,8 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
* Ted Mielczarek <ted.mielczarek@gmail.com>
* Honza Bambas <honzab@firemni.cz>
*
* 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
@ -36,6 +37,16 @@
*
* ***** END LICENSE BLOCK ***** */
/*
* WARNING: DO NOT USE THIS CODE IN PRODUCTION SYSTEMS. It is highly likely to
* be plagued with the usual problems endemic to C (buffer overflows
* and the like). We don't especially care here (but would accept
* patches!) because this is only intended for use in our test
* harnesses in controlled situations where input is guaranteed not to
* be malicious.
*/
#include <assert.h>
#include <stdio.h>
#include <string>
#include <vector>
@ -75,6 +86,37 @@ typedef struct {
server_info_t* server_info;
} connection_info_t;
const PRInt32 BUF_SIZE = 16384;
const PRInt32 BUF_MARGIN = 1024;
const PRInt32 BUF_TOTAL = BUF_SIZE + BUF_MARGIN;
struct relayBuffer
{
char *buffer, *bufferhead, *buffertail, *bufferend;
relayBuffer()
{
// Leave 1024 bytes more for request line manipulations
bufferhead = buffertail = buffer = new char[BUF_TOTAL];
bufferend = buffer + BUF_SIZE;
}
~relayBuffer()
{
delete [] buffer;
}
void compact() {
if (buffertail == bufferhead)
buffertail = bufferhead = buffer;
}
bool empty() { return bufferhead == buffertail; }
size_t free() { return bufferend - buffertail; }
size_t margin() { return free() + BUF_MARGIN; }
size_t present() { return buffertail - bufferhead; }
};
// A couple of stack classes for managing NSS/NSPR resources
class AutoCert {
public:
@ -118,7 +160,6 @@ private:
const PRInt32 INITIAL_THREADS = 1;
const PRInt32 MAX_THREADS = 5;
const PRInt32 DEFAULT_STACKSIZE = (512 * 1024);
const PRInt32 BUF_SIZE = 4096;
// global data
string nssconfigdir;
@ -155,17 +196,18 @@ void SignalShutdown()
}
bool ReadConnectRequest(server_info_t* server_info,
char* bufferhead, char* buffertail, PRInt32* result, string* certificate, client_auth_option* clientauth)
relayBuffer& buffer, PRInt32* result, string& certificate,
client_auth_option* clientauth, string& host)
{
if (buffertail - bufferhead < 4)
if (buffer.present() < 4)
return false;
if (strncmp(buffertail-4, "\r\n\r\n", 4))
if (strncmp(buffer.buffertail-4, "\r\n\r\n", 4))
return false;
*result = 400;
char* token;
token = strtok(bufferhead, " ");
token = strtok(buffer.bufferhead, " ");
if (!token)
return true;
if (strcmp(token, "CONNECT"))
@ -174,7 +216,10 @@ bool ReadConnectRequest(server_info_t* server_info,
token = strtok(NULL, " ");
void* c = PL_HashTableLookup(server_info->host_cert_table, token);
if (c)
*certificate = (char*)c;
certificate = static_cast<char*>(c);
host = "https://";
host += token;
c = PL_HashTableLookup(server_info->host_clientauth_table, token);
if (c)
@ -236,6 +281,45 @@ bool ConfigureSSLServerSocket(PRFileDesc* socket, server_info_t* si, string &cer
return true;
}
/**
* This function prefixes Request-URI path with a full scheme-host-port
* string.
*/
bool AdjustRequestURI(relayBuffer& buffer, string *host)
{
assert(buffer.margin());
// Cannot use strnchr so add a null char at the end. There is always some space left
// because we preserve a margin.
buffer.buffertail[1] = '\0';
char *token, *path;
path = strchr(buffer.bufferhead, ' ') + 1;
if (!path)
return false;
// If the path doesn't start with a slash don't change it, it is probably '*' or a full
// path already. Return true, we are done with this request adjustment.
if (*path != '/')
return true;
token = strchr(path, ' ') + 1;
if (!token)
return false;
if (strncmp(token, "HTTP/", 5))
return false;
size_t hostlength = host->length();
assert(hostlength <= buffer.margin());
memmove(path + hostlength, path, buffer.buffertail - path);
memcpy(path, host->c_str(), hostlength);
buffer.buffertail += hostlength;
return true;
}
bool ConnectSocket(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
{
PRStatus stat = PR_Connect(fd, addr, timeout);
@ -267,44 +351,16 @@ void HandleConnection(void* data)
bool client_error = false;
bool connect_accepted = !do_http_proxy;
bool ssl_updated = !do_http_proxy;
bool expect_request_start = do_http_proxy;
string certificateToUse;
client_auth_option clientAuth;
string fullHost;
if (other_sock)
{
PRInt32 numberOfSockets = 1;
struct relayBuffer
{
char *buffer, *bufferhead, *buffertail, *bufferend;
relayBuffer()
{
bufferhead = buffertail = buffer = new char[BUF_SIZE];
bufferend = buffer + BUF_SIZE;
}
~relayBuffer()
{
delete [] buffer;
}
bool empty()
{
return bufferhead == buffertail;
}
PRInt32 free()
{
return bufferend - buffertail;
}
PRInt32 present()
{
return buffertail - bufferhead;
}
void compact()
{
if (buffertail == bufferhead)
buffertail = bufferhead = buffer;
}
} buffers[2];
relayBuffer buffers[2];
if (!do_http_proxy)
{
@ -372,8 +428,8 @@ void HandleConnection(void* data)
// We have to accept and handle the initial CONNECT request here
PRInt32 response;
if (!connect_accepted && ReadConnectRequest(ci->server_info, buffers[s].bufferhead, buffers[s].buffertail,
&response, &certificateToUse, &clientAuth))
if (!connect_accepted && ReadConnectRequest(ci->server_info, buffers[s],
&response, certificateToUse, &clientAuth, fullHost))
{
// Clean the request as it would be read
buffers[s].bufferhead = buffers[s].buffertail = buffers[s].buffer;
@ -406,7 +462,12 @@ void HandleConnection(void* data)
in_flags &= ~PR_POLL_READ;
if (ssl_updated)
{
if (s == 0 && expect_request_start)
expect_request_start = !AdjustRequestURI(buffers[s], &fullHost);
in_flags2 |= PR_POLL_WRITE;
}
}
} // PR_POLL_READ handling

View File

@ -104,7 +104,6 @@ protected:
PRPackedBool mFollowLinksDirty;
static const char kPathSepChar;
static const PRUnichar kPathSepUnichar;
static const PRInt64 kJanuaryFirst1970Seconds;
};

View File

@ -24,6 +24,7 @@
* Jungshik Shin <jshin@mailaps.org>
* Asaf Romano <mozilla.mano@sent.com>
* Mark Mentovai <mark@moxienet.com>
* Josh Aas <josh@mozilla.com>
*
* 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
@ -67,12 +68,6 @@
#include <sys/stat.h>
#include <stdlib.h>
#if !defined(MAC_OS_X_VERSION_10_4) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
#define GetAliasSizeFromRecord(aliasRecord) aliasRecord.aliasSize
#else
#define GetAliasSizeFromRecord(aliasRecord) GetAliasSizeFromPtr(&aliasRecord)
#endif
#define CHECK_mBaseRef() \
PR_BEGIN_MACRO \
if (!mBaseRef) \
@ -80,7 +75,6 @@
PR_END_MACRO
static nsresult MacErrorMapper(OSErr inErr);
static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn);
static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult);
#pragma mark -
@ -287,32 +281,9 @@ class nsDirEnumerator : public nsISimpleEnumerator,
NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
#pragma mark -
#pragma mark [StAEDesc]
class StAEDesc: public AEDesc
{
public:
StAEDesc()
{
descriptorType = typeNull;
dataHandle = nil;
}
~StAEDesc()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
::AEDisposeDesc(this);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
};
#define FILENAME_BUFFER_SIZE 512
const char nsLocalFile::kPathSepChar = '/';
const PRUnichar nsLocalFile::kPathSepUnichar = '/';
const char nsLocalFile::kPathSepChar = '/';
// The HFS+ epoch is Jan. 1, 1904 GMT - differs from HFS in which times were local
// The NSPR epoch is Jan. 1, 1970 GMT
@ -1660,7 +1631,7 @@ NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistent
// Cast to an alias record and resolve.
AliasRecord aliasHeader = *(AliasPtr)decodedData;
PRInt32 aliasSize = GetAliasSizeFromRecord(aliasHeader);
PRInt32 aliasSize = ::GetAliasSizeFromPtr(&aliasHeader);
if (aliasSize > ((PRInt32)dataSize * 3) / 4) { // be paranoid about having too few data
PR_Free(decodedData);
return NS_ERROR_FAILURE;
@ -1693,47 +1664,18 @@ NS_IMETHODIMP nsLocalFile::Reveal()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
FSRef fsRefToReveal;
AppleEvent aeEvent = {0, nil};
AppleEvent aeReply = {0, nil};
StAEDesc aeDirDesc, listElem, myAddressDesc, fileList;
OSErr err;
ProcessSerialNumber process;
nsresult rv = GetFSRefInternal(fsRefToReveal);
if (NS_FAILED(rv))
return rv;
err = ::FindRunningAppBySignature ('MACS', process);
if (err == noErr) {
err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc);
if (err == noErr) {
// Create the FinderEvent
err = ::AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc,
kAutoGenerateReturnID, kAnyTransactionID, &aeEvent);
if (err == noErr) {
// Create the file list
err = ::AECreateList(nil, 0, false, &fileList);
if (err == noErr) {
FSSpec fsSpecToReveal;
err = ::FSRefMakeFSSpec(&fsRefToReveal, &fsSpecToReveal);
if (err == noErr) {
err = ::AEPutPtr(&fileList, 0, typeFSS, &fsSpecToReveal, sizeof(FSSpec));
if (err == noErr) {
err = ::AEPutParamDesc(&aeEvent, keyDirectObject, &fileList);
if (err == noErr) {
err = ::AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
if (err == noErr)
::SetFrontProcess(&process);
}
}
}
}
}
}
BOOL success = NO;
CFURLRef urlRef;
if (NS_SUCCEEDED(GetCFURL(&urlRef)) && urlRef) {
NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)urlRef path] inFileViewerRootedAtPath:@""];
[ap release];
::CFRelease(urlRef);
}
return NS_OK;
return (success ? NS_OK : NS_ERROR_FAILURE);
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
@ -1742,13 +1684,18 @@ NS_IMETHODIMP nsLocalFile::Launch()
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
FSRef fsRef;
nsresult rv = GetFSRefInternal(fsRef);
if (NS_FAILED(rv))
return rv;
BOOL success = NO;
OSErr err = ::LSOpenFSRef(&fsRef, NULL);
return MacErrorMapper(err);
CFURLRef urlRef;
if (NS_SUCCEEDED(GetCFURL(&urlRef)) && urlRef) {
NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)urlRef];
[ap release];
::CFRelease(urlRef);
}
return (success ? NS_OK : NS_ERROR_FAILURE);
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
@ -2539,38 +2486,6 @@ static nsresult MacErrorMapper(OSErr inErr)
return outErr;
}
static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
ProcessInfoRec info;
OSErr err = noErr;
outPsn.highLongOfPSN = 0;
outPsn.lowLongOfPSN = kNoProcess;
while (PR_TRUE)
{
err = ::GetNextProcess(&outPsn);
if (err == procNotFound)
break;
if (err != noErr)
return err;
info.processInfoLength = sizeof(ProcessInfoRec);
info.processName = nil;
info.processAppSpec = nil;
err = ::GetProcessInformation(&outPsn, &info);
if (err != noErr)
return err;
if (info.processSignature == aAppSig)
return noErr;
}
return procNotFound;
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(procNotFound);
}
// Convert a UTF-8 string to a UTF-16 string while normalizing to
// Normalization Form C (composed Unicode). We need this because
// Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode)