diff --git a/browser/modules/webrtcUI.jsm b/browser/modules/webrtcUI.jsm index 5817e3b1755..8bb28f9d5ab 100644 --- a/browser/modules/webrtcUI.jsm +++ b/browser/modules/webrtcUI.jsm @@ -66,19 +66,20 @@ function getBrowserForWindow(aContentWindow) { function handleRequest(aSubject, aTopic, aData) { let constraints = aSubject.getConstraints(); + let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID); - Services.wm.getMostRecentWindow(null).navigator.mozGetUserMediaDevices( + contentWindow.navigator.mozGetUserMediaDevices( constraints, function (devices) { - prompt(aSubject.windowID, aSubject.callID, constraints.audio, + prompt(contentWindow, aSubject.callID, constraints.audio, constraints.video || constraints.picture, devices); }, function (error) { // bug 827146 -- In the future, the UI should catch NO_DEVICES_FOUND // and allow the user to plug in a device, instead of immediately failing. denyRequest(aSubject.callID, error); - } - ); + }, + aSubject.innerWindowID); } function denyRequest(aCallID, aError) { @@ -90,7 +91,7 @@ function denyRequest(aCallID, aError) { Services.obs.notifyObservers(msg, "getUserMedia:response:deny", aCallID); } -function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) { +function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevices) { let audioDevices = []; let videoDevices = []; for (let device of aDevices) { @@ -119,9 +120,8 @@ function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) return; } - let contentWindow = Services.wm.getOuterWindowWithId(aWindowID); - let host = contentWindow.document.documentURIObject.host; - let browser = getBrowserForWindow(contentWindow); + let host = aContentWindow.document.documentURIObject.host; + let browser = getBrowserForWindow(aContentWindow); let chromeDoc = browser.ownerDocument; let chromeWin = chromeDoc.defaultView; let stringBundle = chromeWin.gNavigatorBundle; diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 0bca612c5e7..9d9b10444de 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1022,6 +1022,7 @@ void Navigator::MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints, MozGetUserMediaDevicesSuccessCallback& aOnSuccess, NavigatorUserMediaErrorCallback& aOnError, + uint64_t aInnerWindowID, ErrorResult& aRv) { CallbackObjectHolderGetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror); + aRv = manager->GetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror, + aInnerWindowID); } #endif diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index ef02e19ec7e..5e97ace1c4b 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -230,6 +230,7 @@ public: void MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints, MozGetUserMediaDevicesSuccessCallback& aOnSuccess, NavigatorUserMediaErrorCallback& aOnError, + uint64_t aInnerWindowID, ErrorResult& aRv); #endif // MOZ_MEDIA_NAVIGATOR bool DoNewResolve(JSContext* aCx, JS::Handle aObject, diff --git a/dom/media/GetUserMediaRequest.cpp b/dom/media/GetUserMediaRequest.cpp index 48b38310a49..cb39a284e6b 100644 --- a/dom/media/GetUserMediaRequest.cpp +++ b/dom/media/GetUserMediaRequest.cpp @@ -17,15 +17,15 @@ GetUserMediaRequest::GetUserMediaRequest( nsPIDOMWindow* aInnerWindow, const nsAString& aCallID, const MediaStreamConstraintsInternal& aConstraints) - : mInnerWindow(aInnerWindow) - , mWindowID(aInnerWindow->GetOuterWindow()->WindowID()) + : mInnerWindowID(aInnerWindow->WindowID()) + , mOuterWindowID(aInnerWindow->GetOuterWindow()->WindowID()) , mCallID(aCallID) , mConstraints(aConstraints) { SetIsDOMBinding(); } -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(GetUserMediaRequest, mInnerWindow) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(GetUserMediaRequest) NS_IMPL_CYCLE_COLLECTING_ADDREF(GetUserMediaRequest) NS_IMPL_CYCLE_COLLECTING_RELEASE(GetUserMediaRequest) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GetUserMediaRequest) @@ -41,7 +41,7 @@ GetUserMediaRequest::WrapObject(JSContext* aCx, JS::Handle aScope) nsISupports* GetUserMediaRequest::GetParentObject() { - return mInnerWindow; + return nullptr; } void GetUserMediaRequest::GetCallID(nsString& retval) @@ -51,7 +51,12 @@ void GetUserMediaRequest::GetCallID(nsString& retval) uint64_t GetUserMediaRequest::WindowID() { - return mWindowID; + return mOuterWindowID; +} + +uint64_t GetUserMediaRequest::InnerWindowID() +{ + return mInnerWindowID; } void diff --git a/dom/media/GetUserMediaRequest.h b/dom/media/GetUserMediaRequest.h index c535edab834..38d2c85918b 100644 --- a/dom/media/GetUserMediaRequest.h +++ b/dom/media/GetUserMediaRequest.h @@ -20,7 +20,8 @@ class MediaStreamConstraintsInternal; class GetUserMediaRequest : public nsISupports, public nsWrapperCache { public: - GetUserMediaRequest(nsPIDOMWindow* aInnerWindow, const nsAString& aCallID, + GetUserMediaRequest(nsPIDOMWindow* aInnerWindow, + const nsAString& aCallID, const MediaStreamConstraintsInternal& aConstraints); virtual ~GetUserMediaRequest() {}; @@ -32,12 +33,12 @@ public: nsISupports* GetParentObject(); uint64_t WindowID(); + uint64_t InnerWindowID(); void GetCallID(nsString& retval); void GetConstraints(MediaStreamConstraintsInternal &result); private: - nsCOMPtr mInnerWindow; - uint64_t mWindowID; + uint64_t mInnerWindowID, mOuterWindowID; const nsString mCallID; MediaStreamConstraintsInternal mConstraints; }; diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 93f0a1f7a6d..9c67666ad28 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -225,12 +225,15 @@ class DeviceSuccessCallbackRunnable: public nsRunnable { public: DeviceSuccessCallbackRunnable( + uint64_t aWindowID, already_AddRefed aSuccess, already_AddRefed aError, nsTArray >* aDevices) : mSuccess(aSuccess) , mError(aError) - , mDevices(aDevices) {} + , mDevices(aDevices) + , mWindowID(aWindowID) + , mManager(MediaManager::GetInstance()) {} NS_IMETHOD Run() @@ -240,6 +243,11 @@ public: nsCOMPtr success(mSuccess); nsCOMPtr error(mError); + // Only run if window is still on our active list. + if (!mManager->IsWindowStillActive(mWindowID)) { + return NS_OK; + } + nsCOMPtr devices = do_CreateInstance("@mozilla.org/variant;1"); @@ -273,6 +281,8 @@ private: already_AddRefed mSuccess; already_AddRefed mError; nsAutoPtr > > mDevices; + uint64_t mWindowID; + nsRefPtr mManager; }; // Handle removing GetUserMediaCallbackMediaStreamListener from main thread @@ -1076,7 +1086,8 @@ public: mLoopbackAudioDevice)); final->MoveElementsFrom(*s); } - NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mSuccess, mError, + NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId, + mSuccess, mError, final.forget())); return NS_OK; } @@ -1087,6 +1098,7 @@ private: already_AddRefed mError; nsRefPtr mManager; uint64_t mWindowId; + const nsString mCallId; // Audio & Video loopback devices to be used based on // the preference settings. This is currently used for // automated media tests only. @@ -1449,7 +1461,8 @@ nsresult MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow, const MediaStreamConstraintsInternal& aConstraints, nsIGetUserMediaDevicesSuccessCallback* aOnSuccess, - nsIDOMGetUserMediaErrorCallback* aOnError) + nsIDOMGetUserMediaErrorCallback* aOnError, + uint64_t aInnerWindowID) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); @@ -1474,9 +1487,9 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow, #endif nsCOMPtr gUMDRunnable = new GetUserMediaDevicesRunnable( - aConstraints, onSuccess.forget(), onError.forget(), aWindow->WindowID(), - loopbackAudioDevice, loopbackVideoDevice - ); + aConstraints, onSuccess.forget(), onError.forget(), + (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()), + loopbackAudioDevice, loopbackVideoDevice); nsCOMPtr deviceThread; rv = NS_NewThread(getter_AddRefs(deviceThread)); diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h index 319a8b0035c..531381dc816 100644 --- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -494,7 +494,8 @@ public: nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow, const dom::MediaStreamConstraintsInternal& aConstraints, nsIGetUserMediaDevicesSuccessCallback* onSuccess, - nsIDOMGetUserMediaErrorCallback* onError); + nsIDOMGetUserMediaErrorCallback* onError, + uint64_t aInnerWindowID = 0); void OnNavigation(uint64_t aWindowID); MediaEnginePrefs mPrefs; diff --git a/dom/media/MediaPermissionGonk.cpp b/dom/media/MediaPermissionGonk.cpp index 46a6c5df921..770ff2a5af6 100644 --- a/dom/media/MediaPermissionGonk.cpp +++ b/dom/media/MediaPermissionGonk.cpp @@ -173,7 +173,8 @@ MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal) { NS_ENSURE_ARG_POINTER(aRequestingPrincipal); - nsCOMPtr window = do_QueryInterface(mRequest->GetParentObject()); + nsCOMPtr window = static_cast + (nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID())); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsCOMPtr doc = window->GetExtantDoc(); @@ -187,7 +188,8 @@ NS_IMETHODIMP MediaPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow) { NS_ENSURE_ARG_POINTER(aRequestingWindow); - nsCOMPtr window = do_QueryInterface(mRequest->GetParentObject()); + nsCOMPtr window = static_cast + (nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID())); window.forget(aRequestingWindow); return NS_OK; } @@ -221,7 +223,8 @@ MediaPermissionRequest::Allow() already_AddRefed MediaPermissionRequest::GetOwner() { - nsCOMPtr window = do_QueryInterface(mRequest->GetParentObject()); + nsCOMPtr window = static_cast + (nsGlobalWindow::GetInnerWindowWithId(mRequest->InnerWindowID())); return window.forget(); } @@ -434,7 +437,8 @@ MediaPermissionManager::HandleRequest(nsRefPtr &req) nsString callID; req->GetCallID(callID); - nsCOMPtr innerWindow = do_QueryInterface(req->GetParentObject()); + nsCOMPtr innerWindow = static_cast + (nsGlobalWindow::GetInnerWindowWithId(req->InnerWindowID())); if (!innerWindow) { MOZ_ASSERT(false, "No inner window"); return NS_ERROR_FAILURE; diff --git a/dom/webidl/GetUserMediaRequest.webidl b/dom/webidl/GetUserMediaRequest.webidl index 9f61fb9185e..0d3b49a5991 100644 --- a/dom/webidl/GetUserMediaRequest.webidl +++ b/dom/webidl/GetUserMediaRequest.webidl @@ -9,6 +9,7 @@ [NoInterfaceObject] interface GetUserMediaRequest { readonly attribute unsigned long long windowID; + readonly attribute unsigned long long innerWindowID; readonly attribute DOMString callID; MediaStreamConstraintsInternal getConstraints(); }; diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index 35a4b8173b5..4e72897a5fb 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -340,6 +340,10 @@ partial interface Navigator { [Throws, ChromeOnly] void mozGetUserMediaDevices(MediaStreamConstraintsInternal constraints, MozGetUserMediaDevicesSuccessCallback onsuccess, - NavigatorUserMediaErrorCallback onerror); + NavigatorUserMediaErrorCallback onerror, + // The originating innerWindowID is needed to + // avoid calling the callbacks if the window has + // navigated away. It is optional only as legacy. + optional unsigned long long innerWindowID = 0); }; #endif // MOZ_MEDIA_NAVIGATOR diff --git a/mobile/android/chrome/content/WebrtcUI.js b/mobile/android/chrome/content/WebrtcUI.js index 3c7def88f0c..2ef8fd2616c 100644 --- a/mobile/android/chrome/content/WebrtcUI.js +++ b/mobile/android/chrome/content/WebrtcUI.js @@ -74,17 +74,18 @@ var WebrtcUI = { handleRequest: function handleRequest(aSubject, aTopic, aData) { let constraints = aSubject.getConstraints(); + let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID); - Services.wm.getMostRecentWindow(null).navigator.mozGetUserMediaDevices( + contentWindow.navigator.mozGetUserMediaDevices( constraints, function (devices) { - WebrtcUI.prompt(aSubject.windowID, aSubject.callID, constraints.audio, + WebrtcUI.prompt(contentWindow, aSubject.callID, constraints.audio, constraints.video, devices); }, function (error) { Cu.reportError(error); - } - ); + }, + aSubject.innerWindowID); }, getDeviceButtons: function(audioDevices, videoDevices, aCallID) { @@ -153,7 +154,8 @@ var WebrtcUI = { } }, - prompt: function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) { + prompt: function prompt(aContentWindow, aCallID, aAudioRequested, + aVideoRequested, aDevices) { let audioDevices = []; let videoDevices = []; for (let device of aDevices) { @@ -180,8 +182,7 @@ var WebrtcUI = { else return; - let contentWindow = Services.wm.getOuterWindowWithId(aWindowID); - let host = contentWindow.document.documentURIObject.host; + let host = aContentWindow.document.documentURIObject.host; let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host; let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1);