mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 826576: Manage the lifetimes of GUMCMSL objects (with inactive Listeners) r=roc
This commit is contained in:
parent
307173aba3
commit
5cb83be2c9
@ -192,6 +192,29 @@ private:
|
||||
nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices;
|
||||
};
|
||||
|
||||
// Handle removing GetUserMediaCallbackMediaStreamListener from main thread
|
||||
class GetUserMediaListenerRemove: public nsRunnable
|
||||
{
|
||||
public:
|
||||
GetUserMediaListenerRemove(uint64_t aWindowID,
|
||||
GetUserMediaCallbackMediaStreamListener *aListener)
|
||||
: mWindowID(aWindowID)
|
||||
, mListener(aListener) {}
|
||||
|
||||
NS_IMETHOD
|
||||
Run()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
nsRefPtr<MediaManager> manager(MediaManager::GetInstance());
|
||||
manager->RemoveFromWindowList(mWindowID, mListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||
};
|
||||
|
||||
/**
|
||||
* nsIMediaDevice implementation.
|
||||
*/
|
||||
@ -286,6 +309,7 @@ public:
|
||||
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
|
||||
uint64_t aWindowID,
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource)
|
||||
: mSuccess(aSuccess)
|
||||
@ -293,6 +317,7 @@ public:
|
||||
, mAudioSource(aAudioSource)
|
||||
, mVideoSource(aVideoSource)
|
||||
, mWindowID(aWindowID)
|
||||
, mListener(aListener)
|
||||
, mManager(MediaManager::GetInstance()) {}
|
||||
|
||||
~GetUserMediaStreamRunnable() {}
|
||||
@ -306,7 +331,7 @@ public:
|
||||
// be invalidated from the main-thread (see OnNavigation)
|
||||
StreamListeners* listeners = mManager->GetWindowListeners(mWindowID);
|
||||
if (!listeners) {
|
||||
// This window is no longer live.
|
||||
// This window is no longer live. mListener has already been removed
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -339,27 +364,19 @@ public:
|
||||
trackunion->CombineWithPrincipal(window->GetExtantDoc()->NodePrincipal());
|
||||
}
|
||||
|
||||
// Ensure there's a thread for gum to proxy to off main thread
|
||||
nsIThread *mediaThread = MediaManager::GetThread();
|
||||
|
||||
// Add our listener. We'll call Start() on the source when get a callback
|
||||
// The listener was added at the begining in an inactive state.
|
||||
// Activate our listener. We'll call Start() on the source when get a callback
|
||||
// that the MediaStream has started consuming. The listener is freed
|
||||
// when the page is invalidated (on navigation or close).
|
||||
GetUserMediaCallbackMediaStreamListener* listener =
|
||||
new GetUserMediaCallbackMediaStreamListener(mediaThread, stream.forget(),
|
||||
port.forget(),
|
||||
mAudioSource,
|
||||
mVideoSource);
|
||||
listener->Stream()->AddListener(listener);
|
||||
|
||||
// No need for locking because we always do this in the main thread.
|
||||
listeners->AppendElement(listener);
|
||||
mListener->Activate(stream.forget(), port.forget(),
|
||||
mAudioSource, mVideoSource);
|
||||
|
||||
// Dispatch to the media thread to ask it to start the sources,
|
||||
// because that can take a while
|
||||
nsIThread *mediaThread = MediaManager::GetThread();
|
||||
nsRefPtr<MediaOperationRunnable> runnable(
|
||||
new MediaOperationRunnable(MEDIA_START, listener,
|
||||
mAudioSource, mVideoSource));
|
||||
new MediaOperationRunnable(MEDIA_START, mListener,
|
||||
mAudioSource, mVideoSource, false));
|
||||
mediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
|
||||
// We're in the main thread, so no worries here either.
|
||||
@ -383,6 +400,7 @@ private:
|
||||
nsRefPtr<MediaEngineSource> mAudioSource;
|
||||
nsRefPtr<MediaEngineSource> mVideoSource;
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
|
||||
};
|
||||
|
||||
@ -405,13 +423,15 @@ public:
|
||||
GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
|
||||
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
|
||||
uint64_t aWindowID, MediaDevice* aAudioDevice, MediaDevice* aVideoDevice)
|
||||
uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
|
||||
MediaDevice* aAudioDevice, MediaDevice* aVideoDevice)
|
||||
: mAudio(aAudio)
|
||||
, mVideo(aVideo)
|
||||
, mPicture(aPicture)
|
||||
, mSuccess(aSuccess)
|
||||
, mError(aError)
|
||||
, mWindowID(aWindowID)
|
||||
, mListener(aListener)
|
||||
, mDeviceChosen(true)
|
||||
, mBackendChosen(false)
|
||||
, mManager(MediaManager::GetInstance())
|
||||
@ -427,13 +447,14 @@ public:
|
||||
GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
|
||||
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
|
||||
uint64_t aWindowID)
|
||||
uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener)
|
||||
: mAudio(aAudio)
|
||||
, mVideo(aVideo)
|
||||
, mPicture(aPicture)
|
||||
, mSuccess(aSuccess)
|
||||
, mError(aError)
|
||||
, mWindowID(aWindowID)
|
||||
, mListener(aListener)
|
||||
, mDeviceChosen(false)
|
||||
, mBackendChosen(false)
|
||||
, mManager(MediaManager::GetInstance()) {}
|
||||
@ -445,13 +466,15 @@ public:
|
||||
GetUserMediaRunnable(bool aAudio, bool aVideo,
|
||||
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
|
||||
uint64_t aWindowID, MediaEngine* aBackend)
|
||||
uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
|
||||
MediaEngine* aBackend)
|
||||
: mAudio(aAudio)
|
||||
, mVideo(aVideo)
|
||||
, mPicture(false)
|
||||
, mSuccess(aSuccess)
|
||||
, mError(aError)
|
||||
, mWindowID(aWindowID)
|
||||
, mListener(aListener)
|
||||
, mDeviceChosen(false)
|
||||
, mBackendChosen(true)
|
||||
, mBackend(aBackend)
|
||||
@ -503,16 +526,26 @@ public:
|
||||
nsresult
|
||||
Denied()
|
||||
{
|
||||
// We add a disabled listener to the StreamListeners array until accepted
|
||||
// If this was the only active MediaStream, remove the window from the list.
|
||||
if (NS_IsMainThread()) {
|
||||
// This is safe since we're on main-thread, and the window can only
|
||||
// be invalidated from the main-thread (see OnNavigation)
|
||||
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
|
||||
error->OnError(NS_LITERAL_STRING("PERMISSION_DENIED"));
|
||||
|
||||
// Should happen *after* error runs for consistency, but may not matter
|
||||
nsRefPtr<MediaManager> manager(MediaManager::GetInstance());
|
||||
manager->RemoveFromWindowList(mWindowID, mListener);
|
||||
} else {
|
||||
// This will re-check the window being alive on main-thread
|
||||
// Note: we must remove the listener on MainThread as well
|
||||
NS_DispatchToMainThread(new ErrorCallbackRunnable(
|
||||
mSuccess, mError, NS_LITERAL_STRING("PERMISSION_DENIED"), mWindowID
|
||||
));
|
||||
|
||||
// MUST happen after ErrorCallbackRunnable Run()s, as it checks the active window list
|
||||
NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -637,7 +670,7 @@ public:
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(new GetUserMediaStreamRunnable(
|
||||
mSuccess, mError, mWindowID, aAudioSource, aVideoSource
|
||||
mSuccess, mError, mWindowID, mListener, aAudioSource, aVideoSource
|
||||
));
|
||||
return;
|
||||
}
|
||||
@ -678,6 +711,7 @@ private:
|
||||
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
|
||||
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
|
||||
nsRefPtr<MediaDevice> mAudioDevice;
|
||||
nsRefPtr<MediaDevice> mVideoDevice;
|
||||
|
||||
@ -876,6 +910,15 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
|
||||
listeners = new StreamListeners;
|
||||
GetActiveWindows()->Put(windowID, listeners);
|
||||
}
|
||||
// Ensure there's a thread for gum to proxy to off main thread
|
||||
nsIThread *mediaThread = MediaManager::GetThread();
|
||||
|
||||
// Create a disabled listener to act as a placeholder
|
||||
GetUserMediaCallbackMediaStreamListener* listener =
|
||||
new GetUserMediaCallbackMediaStreamListener(mediaThread, windowID);
|
||||
|
||||
// No need for locking because we always do this in the main thread.
|
||||
listeners->AppendElement(listener);
|
||||
|
||||
// Developer preference for turning off permission check.
|
||||
if (Preferences::GetBool("media.navigator.permission.disabled", false)) {
|
||||
@ -894,20 +937,20 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
|
||||
if (fake) {
|
||||
// Fake stream from default backend.
|
||||
gUMRunnable = new GetUserMediaRunnable(
|
||||
audio, video, onSuccess.forget(), onError.forget(), windowID,
|
||||
audio, video, onSuccess.forget(), onError.forget(), windowID, listener,
|
||||
new MediaEngineDefault()
|
||||
);
|
||||
} else if (audiodevice || videodevice) {
|
||||
// Stream from provided device.
|
||||
gUMRunnable = new GetUserMediaRunnable(
|
||||
audio, video, picture, onSuccess.forget(), onError.forget(), windowID,
|
||||
audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener,
|
||||
static_cast<MediaDevice*>(audiodevice.get()),
|
||||
static_cast<MediaDevice*>(videodevice.get())
|
||||
);
|
||||
} else {
|
||||
// Stream from default device from WebRTC backend.
|
||||
gUMRunnable = new GetUserMediaRunnable(
|
||||
audio, video, picture, onSuccess.forget(), onError.forget(), windowID
|
||||
audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener
|
||||
);
|
||||
}
|
||||
|
||||
@ -1026,12 +1069,33 @@ MediaManager::OnNavigation(uint64_t aWindowID)
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> listener =
|
||||
listeners->ElementAt(i);
|
||||
listener->Invalidate();
|
||||
listener->Invalidate(true);
|
||||
listener->Remove();
|
||||
}
|
||||
listeners->Clear();
|
||||
|
||||
GetActiveWindows()->Remove(aWindowID);
|
||||
RemoveWindowID(aWindowID);
|
||||
// listeners has been deleted
|
||||
}
|
||||
|
||||
void
|
||||
MediaManager::RemoveFromWindowList(uint64_t aWindowID,
|
||||
GetUserMediaCallbackMediaStreamListener *aListener)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "RemoveFromWindowList called off main thread");
|
||||
|
||||
// This is defined as safe on an inactive GUMCMSListener
|
||||
aListener->Remove(); // really queues the remove
|
||||
|
||||
StreamListeners* listeners = GetWindowListeners(aWindowID);
|
||||
if (!listeners) {
|
||||
return;
|
||||
}
|
||||
listeners->RemoveElement(aListener);
|
||||
if (listeners->Length() == 0) {
|
||||
RemoveWindowID(aWindowID);
|
||||
// listeners has been deleted here
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -1169,7 +1233,7 @@ MediaManager::GetActiveMediaCaptureWindows(nsISupportsArray **aArray)
|
||||
}
|
||||
|
||||
void
|
||||
GetUserMediaCallbackMediaStreamListener::Invalidate()
|
||||
GetUserMediaCallbackMediaStreamListener::Invalidate(bool aNeedsFinish)
|
||||
{
|
||||
nsRefPtr<MediaOperationRunnable> runnable;
|
||||
// We can't take a chance on blocking here, so proxy this to another
|
||||
@ -1177,8 +1241,16 @@ GetUserMediaCallbackMediaStreamListener::Invalidate()
|
||||
// Pass a ref to us (which is threadsafe) so it can query us for the
|
||||
// source stream info.
|
||||
runnable = new MediaOperationRunnable(MEDIA_STOP,
|
||||
this, mAudioSource, mVideoSource);
|
||||
this, mAudioSource, mVideoSource,
|
||||
aNeedsFinish);
|
||||
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
|
||||
{
|
||||
Invalidate(false);
|
||||
NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, this));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -69,18 +69,11 @@ class GetUserMediaNotificationEvent: public nsRunnable
|
||||
class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
|
||||
{
|
||||
public:
|
||||
// Create in an inactive state
|
||||
GetUserMediaCallbackMediaStreamListener(nsIThread *aThread,
|
||||
already_AddRefed<SourceMediaStream> aStream,
|
||||
already_AddRefed<MediaInputPort> aPort,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource)
|
||||
uint64_t aWindowID)
|
||||
: mMediaThread(aThread)
|
||||
, mAudioSource(aAudioSource)
|
||||
, mVideoSource(aVideoSource)
|
||||
, mStream(aStream)
|
||||
, mPort(aPort)
|
||||
, mLastEndTimeAudio(0)
|
||||
, mLastEndTimeVideo(0) {}
|
||||
, mWindowID(aWindowID) {}
|
||||
|
||||
~GetUserMediaCallbackMediaStreamListener()
|
||||
{
|
||||
@ -88,24 +81,41 @@ public:
|
||||
// refcounts.
|
||||
}
|
||||
|
||||
void Activate(already_AddRefed<SourceMediaStream> aStream,
|
||||
already_AddRefed<MediaInputPort> aPort,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource)
|
||||
{
|
||||
mStream = aStream; // also serves as IsActive();
|
||||
mPort = aPort;
|
||||
mAudioSource = aAudioSource;
|
||||
mVideoSource = aVideoSource;
|
||||
mLastEndTimeAudio = 0;
|
||||
mLastEndTimeVideo = 0;
|
||||
|
||||
mStream->AddListener(this);
|
||||
}
|
||||
|
||||
MediaStream *Stream()
|
||||
{
|
||||
return mStream;
|
||||
}
|
||||
SourceMediaStream *GetSourceStream()
|
||||
{
|
||||
MOZ_ASSERT(mStream);
|
||||
return mStream->AsSourceStream();
|
||||
}
|
||||
|
||||
void
|
||||
Invalidate(); // implement in .cpp to avoid circular dependency with MediaOperationRunnable
|
||||
// implement in .cpp to avoid circular dependency with MediaOperationRunnable
|
||||
void Invalidate(bool aNeedsFinish);
|
||||
|
||||
void
|
||||
Remove()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
// Caller holds strong reference to us, so no death grip required
|
||||
mStream->RemoveListener(this);
|
||||
if (mStream) // allow even if inactive for easier cleanup
|
||||
mStream->RemoveListener(this);
|
||||
}
|
||||
|
||||
// Proxy NotifyPull() to sources
|
||||
@ -123,14 +133,11 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
NotifyFinished(MediaStreamGraph* aGraph)
|
||||
{
|
||||
Invalidate();
|
||||
// XXX right now this calls Finish, which isn't ideal but doesn't hurt
|
||||
}
|
||||
NotifyFinished(MediaStreamGraph* aGraph);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIThread> mMediaThread;
|
||||
uint64_t mWindowID;
|
||||
nsRefPtr<MediaEngineSource> mAudioSource;
|
||||
nsRefPtr<MediaEngineSource> mVideoSource;
|
||||
nsRefPtr<SourceMediaStream> mStream;
|
||||
@ -154,11 +161,13 @@ public:
|
||||
MediaOperationRunnable(MediaOperation aType,
|
||||
GetUserMediaCallbackMediaStreamListener* aListener,
|
||||
MediaEngineSource* aAudioSource,
|
||||
MediaEngineSource* aVideoSource)
|
||||
MediaEngineSource* aVideoSource,
|
||||
bool aNeedsFinish)
|
||||
: mType(aType)
|
||||
, mAudioSource(aAudioSource)
|
||||
, mVideoSource(aVideoSource)
|
||||
, mListener(aListener)
|
||||
, mFinish(aNeedsFinish)
|
||||
{}
|
||||
|
||||
~MediaOperationRunnable()
|
||||
@ -218,7 +227,9 @@ public:
|
||||
mVideoSource->Deallocate();
|
||||
}
|
||||
// Do this after stopping all tracks with EndTrack()
|
||||
source->Finish();
|
||||
if (mFinish) {
|
||||
source->Finish();
|
||||
}
|
||||
// the TrackUnion destination of the port will autofinish
|
||||
|
||||
nsRefPtr<GetUserMediaNotificationEvent> event =
|
||||
@ -240,6 +251,7 @@ private:
|
||||
nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
|
||||
nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
|
||||
nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
|
||||
bool mFinish;
|
||||
};
|
||||
|
||||
typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
|
||||
@ -309,9 +321,15 @@ public:
|
||||
|
||||
return mActiveWindows.Get(aWindowId);
|
||||
}
|
||||
void RemoveWindowID(uint64_t aWindowId) {
|
||||
mActiveWindows.Remove(aWindowId);
|
||||
}
|
||||
bool IsWindowStillActive(uint64_t aWindowId) {
|
||||
return !!GetWindowListeners(aWindowId);
|
||||
}
|
||||
// Note: also calls aListener->Remove(), even if inactive
|
||||
void RemoveFromWindowList(uint64_t aWindowID,
|
||||
GetUserMediaCallbackMediaStreamListener *aListener);
|
||||
|
||||
nsresult GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
|
||||
nsIMediaStreamOptions* aParams,
|
||||
|
Loading…
Reference in New Issue
Block a user