Skip to content

Instantly share code, notes, and snippets.

@anantn

anantn/gist:2871209

Created Jun 4, 2012
Embed
What would you like to do?
Add popup blocking to android getUserMedia({picture})
diff --git a/content/media/MediaEngineDefault.cpp b/content/media/MediaEngineDefault.cpp
--- a/content/media/MediaEngineDefault.cpp
+++ b/content/media/MediaEngineDefault.cpp
@@ -1,13 +1,19 @@
/* 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 "nsDOMFile.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 +150,37 @@ 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<nsILocalFile> localFile;
+ nsresult rv = NS_NewLocalFile(filePath, false, getter_AddRefs(localFile));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ NS_ADDREF(*aFile = new nsDOMFileFile(localFile));
+ return NS_OK;
+#endif
}
NS_IMETHODIMP
MediaEngineDefaultVideoSource::Notify(nsITimer* aTimer)
{
VideoSegment segment;
nsRefPtr<layers::PlanarYCbCrImage> image = mImage;
diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp
--- 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)
diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp
--- 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,52 @@ 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.
+ nsCOMPtr<nsIPopupWindowManager> pm =
+ do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
+ if (!pm) {
+ return NS_OK;
+ }
+
+ if (mWindow->GetPopupControlState() <= openControlled) {
+ 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 +224,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 +243,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 +308,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 +361,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 +402,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);
}
diff --git a/dom/media/MediaManager.h b/dom/media/MediaManager.h
--- 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)
diff --git a/mobile/android/confvars.sh b/mobile/android/confvars.sh
--- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment