Bug 738528: Android still image support for getUserMedia r=sicking, r=jesup (plus bustage fix r=jesup)
authorAnant Narayanan <anant@kix.in>
Tue, 05 Jun 2012 08:53:00 -0400
changeset 101043 2304b82d86e4611b1d24e1947a7c7b372e2dad3a
parent 101042 401edab3831f69fbf5de0c73d027d6cf02b39606
child 101044 bef8d091055ac9dd0497b5da6042ce8715d204bf
push id1316
push userakeybl@mozilla.com
push dateMon, 27 Aug 2012 22:37:00 +0000
treeherdermozilla-beta@db4b09302ee2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking, jesup, jesup
bugs738528
milestone16.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 738528: Android still image support for getUserMedia r=sicking, r=jesup (plus bustage fix r=jesup)
content/media/MediaEngineDefault.cpp
dom/base/Navigator.cpp
dom/media/MediaManager.cpp
dom/media/MediaManager.h
mobile/android/confvars.sh
--- a/content/media/MediaEngineDefault.cpp
+++ b/content/media/MediaEngineDefault.cpp
@@ -1,14 +1,23 @@
 /* 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 "MediaEngineDefault.h"
 
+#include "nsCOMPtr.h"
+#include "nsDOMFile.h"
+#include "nsILocalFile.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#include "nsISupportsUtils.h"
+#endif
+
 #define WIDTH 320
 #define HEIGHT 240
 #define FPS 10
 #define CHANNELS 1
 #define RATE USECS_PER_S
 
 namespace mozilla {
 
@@ -144,18 +153,35 @@ MediaEngineDefaultVideoSource::Stop()
 
   mState = kStopped;
   return NS_OK;
 }
 
 nsresult
 MediaEngineDefaultVideoSource::Snapshot(PRUint32 aDuration, nsIDOMFile** aFile)
 {
-   *aFile = nsnull;
-   return NS_ERROR_NOT_IMPLEMENTED;
+  *aFile = nsnull;
+
+#ifndef MOZ_WIDGET_ANDROID
+  return NS_ERROR_NOT_IMPLEMENTED;
+#else
+  if (!AndroidBridge::Bridge()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsAutoString filePath;
+  AndroidBridge::Bridge()->ShowFilePickerForMimeType(filePath, NS_LITERAL_STRING("image/*"));
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = NS_NewLocalFile(filePath, false, getter_AddRefs(file));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ADDREF(*aFile = new nsDOMFileFile(file));
+  return NS_OK;
+#endif
 }
 
 NS_IMETHODIMP
 MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
 {
   VideoSegment segment;
 
   nsRefPtr<layers::PlanarYCbCrImage> image = mImage;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -921,17 +921,17 @@ Navigator::MozGetUserMedia(nsIMediaStrea
   MediaManager *manager = MediaManager::Get();
   nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
 
   if (!win || !win->GetOuterWindow() ||
       win->GetOuterWindow()->GetCurrentInnerWindow() != win) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  return manager->GetUserMedia(win->WindowID(), aParams, onSuccess, onError);
+  return manager->GetUserMedia(win, aParams, onSuccess, onError);
 }
 #endif
 
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorDesktopNotification
 //*****************************************************************************
 
 NS_IMETHODIMP Navigator::GetMozNotification(nsIDOMDesktopNotificationCenter** aRetVal)
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -5,19 +5,21 @@
 #include "MediaManager.h"
 
 #include "MediaStreamGraph.h"
 #include "MediaEngineDefault.h"
 
 #include "nsIDOMFile.h"
 #include "nsIEventTarget.h"
 #include "nsIScriptGlobalObject.h"
+#include "nsIPopupWindowManager.h"
 
 #include "nsJSUtils.h"
 #include "nsDOMFile.h"
+#include "nsGlobalWindow.h"
 
 namespace mozilla {
 
 /**
  * Send an error back to content. The error is the form a string.
  * Do this only on the main thread.
  */
 class ErrorCallbackRunnable : public nsRunnable
@@ -163,26 +165,53 @@ private:
  */
 class GetUserMediaSnapshotCallbackRunable : public nsRunnable
 {
 public:
   GetUserMediaSnapshotCallbackRunable(MediaEngineSource* aSource,
     PRUint32 aDuration,
     nsIDOMGetUserMediaSuccessCallback* aSuccessCallback,
     nsIDOMGetUserMediaErrorCallback* aErrorCallback,
-    PRUint64 aWindowID)
+    nsPIDOMWindow* aWindow)
     : mSource(aSource)
     , mDuration(aDuration)
     , mSuccessCallback(aSuccessCallback)
     , mErrorCallback(aErrorCallback)
-    , mWindowID(aWindowID) {}
+    , mWindow(aWindow) {}
 
   NS_IMETHOD
   Run()
   {
+    mWindowID = mWindow->WindowID();
+
+    // Before getting a snapshot, check if page is allowed to open a popup.
+    // We do this because {picture:true} on all platforms will open a new
+    // "window" to let the user preview or select an image.
+
+    if (mWindow->GetPopupControlState() <= openControlled) {
+      return NS_OK;
+    }
+    
+    nsCOMPtr<nsIPopupWindowManager> pm =
+      do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
+    if (!pm) {
+      return NS_OK;
+    }
+
+    PRUint32 permission;
+    nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
+    pm->TestPermission(doc->GetDocumentURI(), &permission);
+    if (permission == nsIPopupWindowManager::DENY_POPUP) {
+      nsCOMPtr<nsIDOMDocument> domDoc = mWindow->GetExtantDocument();
+      nsGlobalWindow::FirePopupBlockedEvent(
+        domDoc, mWindow, nsnull, EmptyString(), EmptyString()
+      );
+      return NS_OK;
+    }
+
     nsCOMPtr<nsDOMMediaStream> comStream = mSource->Allocate();
     if (!comStream) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
         mErrorCallback, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
       ));
       return NS_OK;
     }
 
@@ -196,16 +225,18 @@ public:
     return NS_OK;
   }
 
 private:
   nsCOMPtr<MediaEngineSource> mSource;
   PRUint32 mDuration;
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccessCallback;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback>  mErrorCallback;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+
   PRUint64 mWindowID;
 };
 
 /**
  * Runs on a seperate thread and is responsible for enumerating devices.
  * Depending on whether a picture or stream was asked for, either
  * GetUserMediaCallbackRunnable or GetUserMediaSnapshotCallbackRunnable
  * will be dispatched to the main thread to return the result to DOM.
@@ -213,37 +244,38 @@ private:
  * Do not run this on the main thread.
  */
 class GetUserMediaRunnable : public nsRunnable
 {
 public:
   GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
     nsIDOMGetUserMediaSuccessCallback* aSuccess,
     nsIDOMGetUserMediaErrorCallback* aError,
-    PRUint64 aWindowID, StreamListeners* aListeners)
+    nsPIDOMWindow* aWindow, StreamListeners* aListeners)
     : mAudio(aAudio)
     , mVideo(aVideo)
     , mPicture(aPicture)
     , mSuccess(aSuccess)
     , mError(aError)
-    , mWindowID(aWindowID)
+    , mWindow(aWindow)
     , mListeners(aListeners) {}
 
   ~GetUserMediaRunnable() {}
 
   // We only support 1 audio and 1 video track for now.
   enum {
     kVideoTrack = 1,
     kAudioTrack = 2
   };
 
   NS_IMETHOD
   Run()
   {
     mManager = MediaManager::Get();
+    mWindowID = mWindow->WindowID();
 
     if (mPicture) {
       SendPicture();
       return NS_OK;
     }
 
     // XXX: Implement merging two streams (See bug 758391).
     if (mAudio && mVideo) {
@@ -277,17 +309,17 @@ public:
     if (!count) {
       NS_DispatchToMainThread(new ErrorCallbackRunnable(
         mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
       ));
       return;
     }
     MediaEngineVideoSource* videoSource = videoSources[count - 1];
     NS_DispatchToMainThread(new GetUserMediaSnapshotCallbackRunable(
-      videoSource, 0 /* duration */, mSuccess, mError, mWindowID
+      videoSource, 0 /* duration */, mSuccess, mError, mWindow
     ));
   }
 
   // {video:true}
   void
   SendVideo()
   {
     nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
@@ -330,34 +362,35 @@ public:
 
 private:
   bool mAudio;
   bool mVideo;
   bool mPicture;
 
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
-  PRUint64 mWindowID;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
   StreamListeners* mListeners;
 
   MediaManager* mManager;
+  PRUint64 mWindowID;
 };
 
 
 nsRefPtr<MediaManager> MediaManager::sSingleton;
 
 NS_IMPL_ISUPPORTS1(MediaManager, nsIObserver)
 
 /**
  * The entry point for this file. A call from Navigator::mozGetUserMedia
  * will end up here. MediaManager is a singleton that is responsible
  * for handling all incoming getUserMedia calls from every window.
  */
 nsresult
-MediaManager::GetUserMedia(PRUint64 aWindowID, nsIMediaStreamOptions* aParams,
+MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
   nsIDOMGetUserMediaSuccessCallback* onSuccess,
   nsIDOMGetUserMediaErrorCallback* onError)
 {
   NS_ENSURE_TRUE(aParams, NS_ERROR_NULL_POINTER);
 
   bool audio, video, picture;
   nsresult rv = aParams->GetPicture(&picture);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -370,26 +403,27 @@ MediaManager::GetUserMedia(PRUint64 aWin
 
   // We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
   nsString cameraType;
   rv = aParams->GetCamera(cameraType);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Store the WindowID in a hash table and mark as active. The entry is removed
   // when this window is closed or navigated away from.
-  StreamListeners* listeners = mActiveWindows.Get(aWindowID);
+  PRUint64 windowID = aWindow->WindowID();
+  StreamListeners* listeners = mActiveWindows.Get(windowID);
   if (!listeners) {
     listeners = new StreamListeners;
-    mActiveWindows.Put(aWindowID, listeners);
+    mActiveWindows.Put(windowID, listeners);
   }
 
   // Pass runanbles along to GetUserMediaRunnable so it can add the
   // MediaStreamListener to the runnable list.
   nsCOMPtr<nsIRunnable> gUMRunnable = new GetUserMediaRunnable(
-    audio, video, picture, onSuccess, onError, aWindowID, listeners
+    audio, video, picture, onSuccess, onError, aWindow, listeners
   );
 
   // Reuse the same thread to save memory.
   if (!mMediaThread) {
     rv = NS_NewThread(getter_AddRefs(mMediaThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -4,20 +4,19 @@
 
 #include "MediaEngine.h"
 #include "mozilla/Services.h"
 
 #include "nsHashKeys.h"
 #include "nsClassHashtable.h"
 #include "nsObserverService.h"
 
+#include "nsPIDOMWindow.h"
 #include "nsIDOMNavigatorUserMedia.h"
 
-#include "stdio.h"
-
 namespace mozilla {
 
 /**
  * This class is an implementation of MediaStreamListener. This is used
  * to Start() and Stop() the underlying MediaEngineSource when MediaStreams
  * are assigned and deassigned in content.
  */
 class GetUserMediaCallbackMediaStreamListener : public MediaStreamListener
@@ -91,17 +90,17 @@ public:
 
   Mutex* GetLock() {
     return mLock;
   }
 
   MediaEngine* GetBackend();
   WindowTable* GetActiveWindows();
 
-  nsresult GetUserMedia(PRUint64 aWindowID, nsIMediaStreamOptions* aParams,
+  nsresult GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
   void OnNavigation(PRUint64 aWindowID);
 
 private:
   // Make private because we want only one instance of this class
   MediaManager()
   : mBackend(nsnull)
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -12,16 +12,19 @@ MOZ_BRANDING_DIRECTORY=mobile/android/br
 MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 MOZ_SAFE_BROWSING=
 MOZ_SERVICES_SYNC=
 
 MOZ_DISABLE_DOMCRYPTO=1
 
+# Enable getUserMedia
+MOZ_MEDIA_NAVIGATOR=1
+
 if test "$LIBXUL_SDK"; then
 MOZ_XULRUNNER=1
 else
 MOZ_XULRUNNER=
 MOZ_PLACES=1
 fi
 
 MOZ_CAPTURE=1