Bug 827145 - When no A/V devices are available, return that error to the page's getUserMedia callbacks. r=jesup a=akeybl
authorJustin Dolske <dolske@mozilla.com>
Fri, 15 Feb 2013 19:22:27 -0800
changeset 127536 f6b62b398ebf24bfc8d3d76eaa2a71ca01ac2943
parent 127535 5ffe19b04456862863dd1b1a662b9a073a302684
child 127537 32a3591ea82670b77098f0cfcbd835de2125814a
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, akeybl
bugs827145
milestone20.0a2
Bug 827145 - When no A/V devices are available, return that error to the page's getUserMedia callbacks. r=jesup a=akeybl
browser/modules/webrtcUI.jsm
dom/media/MediaManager.cpp
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -73,16 +73,22 @@ function handleRequest(aSubject, aTopic,
   let browser = getBrowserForWindowId(windowID);
   let params = aSubject.QueryInterface(Ci.nsIMediaStreamOptions);
 
   browser.ownerDocument.defaultView.navigator.mozGetUserMediaDevices(
     function (devices) {
       prompt(browser, callID, params.audio, params.video, 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.
+      let msg = Cc["@mozilla.org/supports-string;1"].
+                createInstance(Ci.nsISupportsString);
+      msg.data = error;
+      Services.obs.notifyObservers(msg, "getUserMedia:response:deny", callID);
       Cu.reportError(error);
     }
   );
 }
 
 function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) {
   let audioDevices = [];
   let videoDevices = [];
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -50,17 +50,17 @@ GetMediaManagerLog()
  * so it can be released correctly.
  */
 class ErrorCallbackRunnable : public nsRunnable
 {
 public:
   ErrorCallbackRunnable(
     already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
-    const nsString& aErrorMsg, uint64_t aWindowID)
+    const nsAString& aErrorMsg, uint64_t aWindowID)
     : mSuccess(aSuccess)
     , mError(aError)
     , mErrorMsg(aErrorMsg)
     , mWindowID(aWindowID)
     , mManager(MediaManager::GetInstance()) {}
 
   NS_IMETHOD
   Run()
@@ -518,34 +518,34 @@ public:
 
     // There's a bug in the permission code that can leave us with mAudio but no audio device
     ProcessGetUserMedia((mAudio && mAudioDevice) ? mAudioDevice->GetSource() : nullptr,
                         (mVideo && mVideoDevice) ? mVideoDevice->GetSource() : nullptr);
     return NS_OK;
   }
 
   nsresult
-  Denied()
+  Denied(const nsAString& aErrorMsg)
   {
       // 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"));
+      error->OnError(aErrorMsg);
 
       // 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
+        mSuccess, mError, aErrorMsg, mWindowID
       ));
 
       // MUST happen after ErrorCallbackRunnable Run()s, as it checks the active window list
       NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
     }
 
     return NS_OK;
   }
@@ -1163,17 +1163,17 @@ MediaManager::Observe(nsISupports* aSubj
         static_cast<GetUserMediaRunnable*>(runnable.get());
 
       nsCOMPtr<nsISupportsArray> array(do_QueryInterface(aSubject));
       MOZ_ASSERT(array);
       uint32_t len = 0;
       array->Count(&len);
       MOZ_ASSERT(len);
       if (!len) {
-        gUMRunnable->Denied(); // neither audio nor video were selected
+        gUMRunnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED")); // neither audio nor video were selected
         return NS_OK;
       }
       for (uint32_t i = 0; i < len; i++) {
         nsCOMPtr<nsISupports> supports;
         array->GetElementAt(i,getter_AddRefs(supports));
         nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supports));
         MOZ_ASSERT(device); // shouldn't be returning anything else...
         if (device) {
@@ -1191,26 +1191,36 @@ MediaManager::Observe(nsISupports* aSubj
     }
 
     // Reuse the same thread to save memory.
     mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "getUserMedia:response:deny")) {
+    nsString errorMessage(NS_LITERAL_STRING("PERMISSION_DENIED"));
+
+    if (aSubject) {
+      nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
+      MOZ_ASSERT(msg);
+      msg->GetData(errorMessage);
+      if (errorMessage.IsEmpty())
+        errorMessage.Assign(NS_LITERAL_STRING("UNKNOWN_ERROR"));
+    }
+
     nsString key(aData);
     nsRefPtr<nsRunnable> runnable;
     if (!mActiveCallbacks.Get(key, getter_AddRefs(runnable))) {
       return NS_OK;
     }
     mActiveCallbacks.Remove(key);
 
     GetUserMediaRunnable* gUMRunnable =
       static_cast<GetUserMediaRunnable*>(runnable.get());
-    gUMRunnable->Denied();
+    gUMRunnable->Denied(errorMessage);
     return NS_OK;
   }
 
   if (!strcmp(aTopic, "getUserMedia:revoke")) {
     nsresult rv;
     uint64_t windowID = nsString(aData).ToInteger64(&rv);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     if (NS_SUCCEEDED(rv)) {