Bug 915705 - Part 2: Add gUM request handler on FirefoxOS. r=jesup
☠☠ backed out by 4d39ed6fb69c ☠ ☠
authorShih-Chiang Chien <schien@mozilla.com>
Tue, 10 Sep 2013 15:27:15 +0200
changeset 160190 f6402ce2b67aa34879d365318ade3412beebc64d
parent 160189 5bdec1cb142883d4e61c3d9821e5e1644d1a5b15
child 160191 557c33ea680da8d4657d4bd8e72cc4e64ee84e3f
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs915705
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 915705 - Part 2: Add gUM request handler on FirefoxOS. r=jesup
dom/media/MediaManager.cpp
dom/media/MediaPermissionGonk.cpp
dom/media/MediaPermissionGonk.h
dom/media/moz.build
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -33,16 +33,20 @@
 #include "mozilla/Preferences.h"
 
 /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
 #include "MediaEngineDefault.h"
 #if defined(MOZ_WEBRTC)
 #include "MediaEngineWebRTC.h"
 #endif
 
+#ifdef MOZ_WIDGET_GONK
+#include "MediaPermissionGonk.h"
+#endif
+
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with MediaStream::GetCurrentTime.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
 namespace mozilla {
 
@@ -1187,16 +1191,20 @@ MediaManager::GetUserMedia(JSContext* aC
   }
 #endif
 
   static bool created = false;
   if (!created) {
     // Force MediaManager to startup before we try to access it from other threads
     // Hack: should init singleton earlier unless it's expensive (mem or CPU)
     (void) MediaManager::Get();
+#ifdef MOZ_WIDGET_GONK
+    // Initialize MediaPermissionManager before send out any permission request.
+    (void) MediaPermissionManager::GetInstance();
+#endif //MOZ_WIDGET_GONK
   }
 
   // Store the WindowID in a hash table and mark as active. The entry is removed
   // when this window is closed or navigated away from.
   uint64_t windowID = aWindow->WindowID();
   nsRefPtr<GetUserMediaRunnable> gUMRunnable;
   // This is safe since we're on main-thread, and the windowlist can only
   // be invalidated from the main-thread (see OnNavigation)
@@ -1247,20 +1255,17 @@ MediaManager::GetUserMedia(JSContext* aC
   } else {
     // Stream from default device from WebRTC backend.
     gUMRunnable = new GetUserMediaRunnable(c, onSuccess.forget(),
       onError.forget(), windowID, listener, mPrefs);
   }
 
 #ifdef MOZ_B2G_CAMERA
   if (mCameraManager == nullptr) {
-    aPrivileged = nsDOMCameraManager::CheckPermission(aWindow);
-    if (aPrivileged) {
-      mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
-    }
+    mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
   if (c.mPicture) {
     // ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
     NS_DispatchToMainThread(gUMRunnable);
     return NS_OK;
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaPermissionGonk.cpp
@@ -0,0 +1,427 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaManager.h"
+#include "MediaPermissionGonk.h"
+
+#include "nsCOMPtr.h"
+#include "nsCxPusher.h"
+#include "nsIContentPermissionPrompt.h"
+#include "nsIDocument.h"
+#include "nsIDOMNavigatorUserMedia.h"
+#include "nsISupportsArray.h"
+#include "nsPIDOMWindow.h"
+#include "nsTArray.h"
+#include "GetUserMediaRequest.h"
+#include "PCOMContentPermissionRequestChild.h"
+#include "mozilla/dom/PBrowserChild.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
+
+#define AUDIO_PERMISSION_NAME "audio-capture"
+
+namespace mozilla {
+
+static MediaPermissionManager *gMediaPermMgr = nullptr;
+
+// Helper function for notifying permission granted
+static nsresult
+NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
+{
+  nsresult rv;
+  nsCOMPtr<nsISupportsArray> array;
+  rv = NS_NewISupportsArray(getter_AddRefs(array));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (uint32_t i = 0; i < aDevices.Length(); ++i) {
+    rv = array->AppendElement(aDevices.ElementAt(i));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
+
+  return obs->NotifyObservers(array, "getUserMedia:response:allow",
+                              aCallID.BeginReading());
+}
+
+// Helper function for notifying permision denial or error
+static nsresult
+NotifyPermissionDeny(const nsAString &aCallID, const nsAString &aErrorMsg)
+{
+  nsresult rv;
+  nsCOMPtr<nsISupportsString> supportsString =
+    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = supportsString->SetData(aErrorMsg);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
+
+  return obs->NotifyObservers(supportsString, "getUserMedia:response:deny",
+                              aCallID.BeginReading());
+}
+
+namespace {
+
+/**
+ * MediaPermissionRequest will send a prompt ipdl request to b2g process according
+ * to its owned type.
+ */
+class MediaPermissionRequest : public nsIContentPermissionRequest
+                             , public PCOMContentPermissionRequestChild
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICONTENTPERMISSIONREQUEST
+
+  MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest,
+                         nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices);
+  virtual ~MediaPermissionRequest() {}
+
+  // It will be called when prompt dismissed.
+  virtual bool Recv__delete__(const bool &allow) MOZ_OVERRIDE;
+  virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
+
+  already_AddRefed<nsPIDOMWindow> GetOwner();
+
+private:
+  bool mAudio; // Request for audio permission
+  nsRefPtr<dom::GetUserMediaRequest> mRequest;
+  nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices; // candiate device list
+};
+
+// MediaPermissionRequest
+NS_IMPL_ISUPPORTS1(MediaPermissionRequest, nsIContentPermissionRequest)
+
+MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest,
+                                               nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices)
+  : mRequest(aRequest)
+{
+  dom::MediaStreamConstraintsInternal constraints;
+  mRequest->GetConstraints(constraints);
+
+  mAudio = constraints.mAudio;
+
+  for (uint32_t i = 0; i < aDevices.Length(); ++i) {
+    nsCOMPtr<nsIMediaDevice> device(aDevices[i]);
+    nsAutoString deviceType;
+    device->GetType(deviceType);
+    if (mAudio && deviceType.EqualsLiteral("audio")) {
+      mDevices.AppendElement(device);
+    }
+  }
+}
+
+// nsIContentPermissionRequest methods
+NS_IMETHODIMP
+MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
+{
+  NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mRequest->GetParentObject());
+  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+  NS_ADDREF(*aRequestingPrincipal = doc->NodePrincipal());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaPermissionRequest::GetType(nsACString &aType)
+{
+  if (mAudio) {
+    aType = AUDIO_PERMISSION_NAME;
+    return NS_OK;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaPermissionRequest::GetAccess(nsACString &aAccess)
+{
+  aAccess = "unused";
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
+{
+  NS_ENSURE_ARG_POINTER(aRequestingWindow);
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mRequest->GetParentObject());
+  window.forget(aRequestingWindow);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaPermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
+{
+  NS_ENSURE_ARG_POINTER(aRequestingElement);
+  *aRequestingElement = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaPermissionRequest::Cancel()
+{
+  nsString callID;
+  mRequest->GetCallID(callID);
+  NotifyPermissionDeny(callID, NS_LITERAL_STRING("Permission Denied"));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaPermissionRequest::Allow()
+{
+  nsString callID;
+  mRequest->GetCallID(callID);
+  NotifyPermissionAllow(callID, mDevices);
+  return NS_OK;
+}
+
+already_AddRefed<nsPIDOMWindow>
+MediaPermissionRequest::GetOwner()
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mRequest->GetParentObject());
+  return window.forget();
+}
+
+//PCOMContentPermissionRequestChild
+bool
+MediaPermissionRequest::Recv__delete__(const bool& allow)
+{
+  if (allow) {
+    (void) Allow();
+  } else {
+    (void) Cancel();
+  }
+  return true;
+}
+
+// Success callback for MediaManager::GetUserMediaDevices().
+class MediaDeviceSuccessCallback: public nsIGetUserMediaDevicesSuccessCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIGETUSERMEDIADEVICESSUCCESSCALLBACK
+
+  MediaDeviceSuccessCallback(nsRefPtr<dom::GetUserMediaRequest> &aRequest)
+    : mRequest(aRequest) {}
+  virtual ~MediaDeviceSuccessCallback() {}
+
+private:
+  nsresult DoPrompt(nsRefPtr<MediaPermissionRequest> &req);
+  nsRefPtr<dom::GetUserMediaRequest> mRequest;
+};
+
+NS_IMPL_ISUPPORTS1(MediaDeviceSuccessCallback, nsIGetUserMediaDevicesSuccessCallback)
+
+// nsIGetUserMediaDevicesSuccessCallback method
+NS_IMETHODIMP
+MediaDeviceSuccessCallback::OnSuccess(nsIVariant* aDevices)
+{
+  nsIID elementIID;
+  uint16_t elementType;
+  void* rawArray;
+  uint32_t arrayLen;
+
+  nsresult rv;
+  rv = aDevices->GetAsArray(&elementType, &elementIID, &arrayLen, &rawArray);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (elementType != nsIDataType::VTYPE_INTERFACE) {
+    NS_Free(rawArray);
+    return NS_ERROR_FAILURE;
+  }
+
+  // Create array for nsIMediaDevice
+  nsTArray<nsCOMPtr<nsIMediaDevice> > devices;
+
+  nsISupports **supportsArray = reinterpret_cast<nsISupports **>(rawArray);
+  for (uint32_t i = 0; i < arrayLen; ++i) {
+    nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supportsArray[i]));
+    devices.AppendElement(device);
+    NS_IF_RELEASE(supportsArray[i]); // explicitly decrease reference count for raw pointer
+  }
+  NS_Free(rawArray); // explicitly free for the memory from nsIVariant::GetAsArray
+
+  // Send MediaPermissionRequest
+  nsRefPtr<MediaPermissionRequest> req = new MediaPermissionRequest(mRequest, devices);
+  rv = DoPrompt(req);
+
+  NS_ENSURE_SUCCESS(rv, rv);
+  return NS_OK;
+}
+
+// Trigger permission prompt UI
+nsresult
+MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
+{
+  // for content process
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread.
+
+    nsresult rv;
+
+    nsCOMPtr<nsPIDOMWindow> window(req->GetOwner());
+    NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+
+    dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell());
+    NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
+
+    nsAutoCString type;
+    rv = req->GetType(type);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoCString access;
+    rv = req->GetAccess(access);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIPrincipal> principal;
+    rv = req->GetPrincipal(getter_AddRefs(principal));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    req->AddRef();
+    child->SendPContentPermissionRequestConstructor(req,
+                                                    type,
+                                                    access,
+                                                    IPC::Principal(principal));
+
+    req->Sendprompt();
+    return NS_OK;
+  }
+
+  // for chrome process
+  nsCOMPtr<nsIContentPermissionPrompt> prompt =
+      do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
+  if (prompt) {
+    prompt->Prompt(req);
+  }
+  return NS_OK;
+}
+
+// Error callback for MediaManager::GetUserMediaDevices()
+class MediaDeviceErrorCallback: public nsIDOMGetUserMediaErrorCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMGETUSERMEDIAERRORCALLBACK
+
+  MediaDeviceErrorCallback(const nsAString &aCallID)
+    : mCallID(aCallID) {}
+
+  virtual ~MediaDeviceErrorCallback() {}
+
+private:
+  const nsString mCallID;
+};
+
+NS_IMPL_ISUPPORTS1(MediaDeviceErrorCallback, nsIDOMGetUserMediaErrorCallback)
+
+// nsIDOMGetUserMediaErrorCallback method
+NS_IMETHODIMP
+MediaDeviceErrorCallback::OnError(const nsAString &aError)
+{
+  return NotifyPermissionDeny(mCallID, aError);
+}
+
+} // namespace anonymous
+
+// MediaPermissionManager
+NS_IMPL_ISUPPORTS1(MediaPermissionManager, nsIObserver)
+
+MediaPermissionManager*
+MediaPermissionManager::GetInstance()
+{
+  if (!gMediaPermMgr) {
+    gMediaPermMgr = new MediaPermissionManager();
+  }
+
+  return gMediaPermMgr;
+}
+
+MediaPermissionManager::MediaPermissionManager()
+{
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(this, "getUserMedia:request", false);
+    obs->AddObserver(this, "xpcom-shutdown", false);
+  }
+}
+
+MediaPermissionManager::~MediaPermissionManager()
+{
+  this->Deinit();
+}
+
+nsresult
+MediaPermissionManager::Deinit()
+{
+  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+  if (obs) {
+    obs->RemoveObserver(this, "getUserMedia:request");
+    obs->RemoveObserver(this, "xpcom-shutdown");
+  }
+  return NS_OK;
+}
+
+// nsIObserver method
+NS_IMETHODIMP
+MediaPermissionManager::Observe(nsISupports* aSubject, const char* aTopic,
+  const PRUnichar* aData)
+{
+  nsresult rv;
+  if (!strcmp(aTopic, "getUserMedia:request")) {
+    nsRefPtr<dom::GetUserMediaRequest> req =
+        static_cast<dom::GetUserMediaRequest*>(aSubject);
+    rv = HandleRequest(req);
+
+    if (NS_FAILED(rv)) {
+      nsString callID;
+      req->GetCallID(callID);
+      NotifyPermissionDeny(callID, NS_LITERAL_STRING("unable to enumerate media device"));
+    }
+  } else if (!strcmp(aTopic, "xpcom-shutdown")) {
+    rv = this->Deinit();
+  } else {
+    // not reachable
+    rv = NS_ERROR_FAILURE;
+  }
+  return rv;
+}
+
+// Handle GetUserMediaRequest, query available media device first.
+nsresult
+MediaPermissionManager::HandleRequest(nsRefPtr<dom::GetUserMediaRequest> &req)
+{
+  nsString callID;
+  req->GetCallID(callID);
+
+  nsCOMPtr<nsPIDOMWindow> innerWindow = do_QueryInterface(req->GetParentObject());
+  if (!innerWindow) {
+    MOZ_ASSERT(false, "No inner window");
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess =
+      new MediaDeviceSuccessCallback(req);
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError =
+      new MediaDeviceErrorCallback(callID);
+
+  dom::MediaStreamConstraintsInternal constraints;
+  req->GetConstraints(constraints);
+
+  nsRefPtr<MediaManager> MediaMgr = MediaManager::GetInstance();
+  nsresult rv = MediaMgr->GetUserMediaDevices(innerWindow, constraints, onSuccess, onError);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaPermissionGonk.h
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DOM_MEDIA_MEDIAPERMISSIONGONK_H
+#define DOM_MEDIA_MEDIAPERMISSIONGONK_H
+
+#include "nsError.h"
+#include "nsIObserver.h"
+#include "nsISupportsImpl.h"
+#include "GetUserMediaRequest.h"
+
+namespace mozilla {
+
+/**
+ * The observer to create the MediaPermissionMgr. This is the entry point of
+ * permission request on b2g.
+ */
+class MediaPermissionManager : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static MediaPermissionManager* GetInstance();
+  virtual ~MediaPermissionManager();
+
+private:
+  MediaPermissionManager();
+  nsresult Deinit();
+  nsresult HandleRequest(nsRefPtr<dom::GetUserMediaRequest> &req);
+};
+
+} // namespace mozilla
+#endif // DOM_MEDIA_MEDIAPERMISSIONGONK_H
+
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -32,14 +32,22 @@ CPP_SOURCES += [
     'MediaManager.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'PeerConnection.js',
     'PeerConnection.manifest',
 ]
 
+if CONFIG['MOZ_B2G']:
+    EXPORTS.mozilla += [
+        'MediaPermissionGonk.h',
+    ]
+    CPP_SOURCES += [
+        'MediaPermissionGonk.cpp',
+    ]
+
 FAIL_ON_WARNINGS = True
 
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'dom_media_s'