# HG changeset patch # User Randy Lin # Date 1375763888 -28800 # Node ID f0276575385c39afc7165c47d5ebe94c84da7818 # Parent a0dd80f800e2ed726b98fe994a3d870ff8831a9b Bug 897776 - MediaRecorder infinite recursion with requestData() calls in "dataavailable" event diff --git a/content/media/MediaRecorder.cpp b/content/media/MediaRecorder.cpp --- a/content/media/MediaRecorder.cpp +++ b/content/media/MediaRecorder.cpp @@ -110,31 +110,35 @@ public: private: nsRefPtr mRecorder; nsRefPtr mEncoder; }; MediaRecorder::~MediaRecorder() { + if (mStreamPort) { + mStreamPort->Destroy(); + } if (mTrackUnionStream) { mTrackUnionStream->Destroy(); } } void MediaRecorder::Init(JSContext* aCx, nsPIDOMWindow* aOwnerWindow) { MOZ_ASSERT(aOwnerWindow); MOZ_ASSERT(aOwnerWindow->IsInnerWindow()); BindToOwner(aOwnerWindow); } MediaRecorder::MediaRecorder(DOMMediaStream& aStream) : mTimeSlice(0), + mAllowedZeroBlobs(0), mState(RecordingState::Inactive) { mStream = &aStream; SetIsDOMBinding(); } void MediaRecorder::ExtractEncodedData() @@ -187,18 +191,17 @@ MediaRecorder::Start(const OptionalSetAutofinish(true); - nsRefPtr port = - mTrackUnionStream->AllocateInputPort(mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT); + mStreamPort = mTrackUnionStream->AllocateInputPort(mStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT); if (mEncoder) { mTrackUnionStream->AddListener(mEncoder); } else { aResult.Throw(NS_ERROR_DOM_ABORT_ERR); } if (!mReadThread) { @@ -251,16 +254,21 @@ MediaRecorder::Resume(ErrorResult& aResu void MediaRecorder::RequestData(ErrorResult& aResult) { if (mState != RecordingState::Recording) { aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } + if (mAllowedZeroBlobs > MAX_ALLOW_CONTINUOUS_ZERO_BLOBS) { + NS_WARNING("Too many requestData function call in a short time!!"); + aResult.Throw(NS_ERROR_FAILURE); + return; + } nsresult rv = CreateAndDispatchBlobEvent(); if (NS_FAILED(rv)) { aResult.Throw(rv); return; } } JSObject* @@ -298,16 +306,20 @@ MediaRecorder::CreateAndDispatchBlobEven if (!CheckPrincipal()) { // Media is not same-origin, don't allow the data out. return NS_ERROR_DOM_SECURITY_ERR; } nsCOMPtr blob; blob = mEncodedBufferCache->ExtractBlob(mMimeType); + uint64_t blobsize; + blob->GetSize(&blobsize); + blobsize > 0 ? mAllowedZeroBlobs = 0 : mAllowedZeroBlobs ++; + // create an event that uses the MessageEvent interface, // which does not bubble, is not cancelable, and has no default action nsCOMPtr event; nsresult rv = NS_NewDOMBlobEvent(getter_AddRefs(event), this, nullptr, nullptr); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr blobEvent = do_QueryInterface(event); rv = blobEvent->InitBlobEvent(NS_LITERAL_STRING("dataavailable"), diff --git a/content/media/MediaRecorder.h b/content/media/MediaRecorder.h --- a/content/media/MediaRecorder.h +++ b/content/media/MediaRecorder.h @@ -11,16 +11,19 @@ #include "MediaEncoder.h" #include "mozilla/dom/MediaRecorderBinding.h" #include "nsDOMEventTargetHelper.h" #include "EncodedBufferCache.h" #include "TrackUnionStream.h" // Max size for allowing queue encoded data in memory #define MAX_ALLOW_MEMORY_BUFFER 1024000 +// Maximal allowed continuous zero Blob +#define MAX_ALLOW_CONTINUOUS_ZERO_BLOBS 10 + namespace mozilla { class ErrorResult; namespace dom { /** * Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html @@ -105,23 +108,27 @@ protected: // Runnable thread for read data from mediaEncoder. It starts at MediaRecorder::Start() and stops at MediaRecorder::Stop(). nsCOMPtr mReadThread; // The MediaEncoder object initializes on start() and destroys in ~MediaRecorder. nsRefPtr mEncoder; // MediaStream passed from js context nsRefPtr mStream; // This media stream is used for notifying raw data to encoder and can be blocked. nsRefPtr mTrackUnionStream; + // This is used for destroing the inputport when destroy the mediaRecorder + nsRefPtr mStreamPort; // This object creates on start() and destroys in ~MediaRecorder. nsAutoPtr mEncodedBufferCache; // It specifies the container format as well as the audio and video capture formats. nsString mMimeType; // The interval of timer passed from Start(). On every mTimeSlice milliseconds, if there are buffers store in the EncodedBufferCache, // a dataavailable event will be fired. int32_t mTimeSlice; + // Count of continues of zero size Blob, avoid the UA call of requestData method crazily. + int32_t mAllowedZeroBlobs; // The current state of the MediaRecorder object. RecordingState mState; }; } } #endif diff --git a/content/media/test/Makefile.in b/content/media/test/Makefile.in --- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -117,16 +117,17 @@ MOCHITEST_FILES = \ test_audiowrite.html \ test_mediarecorder_creation.html \ test_mozHasAudio.html \ test_source_media.html \ test_autoplay_contentEditable.html \ test_bug448534.html \ test_bug463162.xhtml \ test_decoder_disable.html \ + test_mediarecorder_avoid_recursive.html \ test_mediarecorder_reload_crash.html \ test_media_selection.html \ test_playback.html \ test_seekLies.html \ test_media_sniffer.html \ contentType.sjs \ test_streams_srcObject.html \ test_reset_src.html \ diff --git a/content/media/test/test_mediarecorder_avoid_recursive.html b/content/media/test/test_mediarecorder_avoid_recursive.html new file mode 100644 --- /dev/null +++ b/content/media/test/test_mediarecorder_avoid_recursive.html @@ -0,0 +1,44 @@ + + + + Test MediaRecorder should avoild the recursive dataavailable event + + + + + +
+
+
+ +