diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp index 18172f23e52b..3be9f83ee936 100644 --- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -109,6 +109,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaRecorder, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStream) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioNode) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityDomException) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnknownDomException) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -116,6 +118,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaRecorder, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStream) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioNode) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityDomException) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnknownDomException) tmp->UnRegisterActivityObserver(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -1055,6 +1059,9 @@ void MediaRecorder::Start(const Optional& aTimeSlice, ErrorResult& aResult) { LOG(LogLevel::Debug, ("MediaRecorder.Start %p", this)); + + InitializeDomExceptions(); + if (mState != RecordingState::Inactive) { aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; @@ -1397,12 +1404,27 @@ MediaRecorder::NotifyError(nsresult aRv) MediaRecorderErrorEventInit init; init.mBubbles = false; init.mCancelable = false; + // These DOMExceptions have been created earlier so they can contain stack + // traces. We attach the appropriate one here to be fired. We should have + // exceptions here, but defensively check. switch (aRv) { case NS_ERROR_DOM_SECURITY_ERR: - init.mError = DOMException::Create(aRv); + if (!mSecurityDomException) { + LOG(LogLevel::Debug, ("MediaRecorder.NotifyError: " + "mSecurityDomException was not initialized")); + mSecurityDomException = DOMException::Create(NS_ERROR_DOM_SECURITY_ERR); + } + init.mError = mSecurityDomException.forget(); break; default: - init.mError = DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR); + if (!mUnknownDomException) { + LOG(LogLevel::Debug, ("MediaRecorder.NotifyError: " + "mUnknownDomException was not initialized")); + mUnknownDomException = DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR); + } + LOG(LogLevel::Debug, ("MediaRecorder.NotifyError: " + "mUnknownDomException being fired for aRv: %X", uint32_t(aRv))); + init.mError = mUnknownDomException.forget(); } RefPtr event = MediaRecorderErrorEvent::Constructor( @@ -1450,6 +1472,13 @@ MediaRecorder::GetSourceMediaStream() return mPipeStream ? mPipeStream.get() : mAudioNode->GetStream(); } +void +MediaRecorder::InitializeDomExceptions() +{ + mSecurityDomException = DOMException::Create(NS_ERROR_DOM_SECURITY_ERR); + mUnknownDomException = DOMException::Create(NS_ERROR_DOM_UNKNOWN_ERR); +} + size_t MediaRecorder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { diff --git a/dom/media/MediaRecorder.h b/dom/media/MediaRecorder.h index 936faaea8deb..5bab46880bf7 100644 --- a/dom/media/MediaRecorder.h +++ b/dom/media/MediaRecorder.h @@ -28,6 +28,7 @@ class GlobalObject; namespace dom { class AudioNode; +class DOMException; /** * Implementation of https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html @@ -133,6 +134,12 @@ protected: void RemoveSession(Session* aSession); // Functions for Session to query input source info. MediaStream* GetSourceMediaStream(); + // Create DOMExceptions capturing the JS stack for async errors. These are + // created ahead of time rather than on demand when firing an error as the JS + // stack of the operation that started the async behavior will not be + // available at the time the error event is fired. Note, depending on when + // this is called there may not be a JS stack to capture. + void InitializeDomExceptions(); // DOM wrapper for source media stream. Will be null when input is audio node. RefPtr mDOMStream; // Source audio node. Will be null when input is a media stream. @@ -159,6 +166,12 @@ protected: uint32_t mVideoBitsPerSecond; uint32_t mBitsPerSecond; + // DOMExceptions that are created early and possibly thrown in NotifyError. + // Creating them early allows us to capture the JS stack for which cannot be + // done at the time the error event is fired. + RefPtr mSecurityDomException; + RefPtr mUnknownDomException; + private: // Register MediaRecorder into Document to listen the activity changes. void RegisterActivityObserver();