Bug 752352: Implement getUserMediaDevices for privileged chrome code; r=smaug, r=jesup

This commit is contained in:
Anant Narayanan 2012-09-20 12:54:00 -07:00
parent 402924f707
commit ef6ae63b3f
6 changed files with 446 additions and 130 deletions

View File

@ -113,6 +113,7 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDesktopNotification)
NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorSms)
#ifdef MOZ_MEDIA_NAVIGATOR
NS_INTERFACE_MAP_ENTRY(nsINavigatorUserMedia)
NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorUserMedia)
#endif
#ifdef MOZ_B2G_RIL
@ -939,7 +940,7 @@ NS_IMETHODIMP Navigator::GetDeviceStorage(const nsAString &aType, nsIDOMDeviceSt
}
NS_ADDREF(*_retval = storage.get());
mDeviceStorageStores.AppendElement(storage);
mDeviceStorageStores.AppendElement(storage);
return NS_OK;
}
@ -987,22 +988,45 @@ NS_IMETHODIMP Navigator::GetGeolocation(nsIDOMGeoGeolocation** _retval)
#ifdef MOZ_MEDIA_NAVIGATOR
NS_IMETHODIMP
Navigator::MozGetUserMedia(nsIMediaStreamOptions* aParams,
nsIDOMGetUserMediaSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError)
nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnError)
{
if (!Preferences::GetBool("media.navigator.enabled", false)) {
return NS_OK;
}
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, aParams, onSuccess, onError);
bool privileged = nsContentUtils::IsChromeDoc(win->GetExtantDoc());
MediaManager* manager = MediaManager::Get();
return manager->GetUserMedia(privileged, win, aParams, aOnSuccess, aOnError);
}
//*****************************************************************************
// Navigator::nsINavigatorUserMedia (mozGetUserMediaDevices)
//*****************************************************************************
NS_IMETHODIMP
Navigator::MozGetUserMediaDevices(nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnError)
{
nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
if (!win || !win->GetOuterWindow() ||
win->GetOuterWindow()->GetCurrentInnerWindow() != win) {
return NS_ERROR_NOT_AVAILABLE;
}
// Check if the caller is chrome privileged, bail if not
if (!nsContentUtils::IsChromeDoc(win->GetExtantDoc())) {
return NS_ERROR_FAILURE;
}
MediaManager* manager = MediaManager::Get();
return manager->GetUserMediaDevices(win, aOnSuccess, aOnError);
}
#endif

View File

@ -87,6 +87,7 @@ class Navigator : public nsIDOMNavigator
, public nsINavigatorBattery
, public nsIDOMMozNavigatorSms
#ifdef MOZ_MEDIA_NAVIGATOR
, public nsINavigatorUserMedia
, public nsIDOMNavigatorUserMedia
#endif
#ifdef MOZ_B2G_RIL
@ -116,6 +117,7 @@ public:
NS_DECL_NSINAVIGATORBATTERY
NS_DECL_NSIDOMMOZNAVIGATORSMS
#ifdef MOZ_MEDIA_NAVIGATOR
NS_DECL_NSINAVIGATORUSERMEDIA
NS_DECL_NSIDOMNAVIGATORUSERMEDIA
#endif
#ifdef MOZ_B2G_RIL

View File

@ -2480,6 +2480,7 @@ nsDOMClassInfo::Init()
battery::BatteryManager::HasSupport())
DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozNavigatorSms)
#ifdef MOZ_MEDIA_NAVIGATOR
DOM_CLASSINFO_MAP_ENTRY(nsINavigatorUserMedia)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorUserMedia)
#endif
#ifdef MOZ_B2G_RIL

View File

@ -25,14 +25,18 @@ namespace mozilla {
/**
* Send an error back to content. The error is the form a string.
* Do this only on the main thread.
* Do this only on the main thread. The success callback is also passed here
* so it can be released correctly.
*/
class ErrorCallbackRunnable : public nsRunnable
{
public:
ErrorCallbackRunnable(nsIDOMGetUserMediaErrorCallback* aError,
ErrorCallbackRunnable(
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
const nsString& aErrorMsg, uint64_t aWindowID)
: mError(aError)
: mSuccess(aSuccess)
, mError(aError)
, mErrorMsg(aErrorMsg)
, mWindowID(aWindowID) {}
@ -40,15 +44,21 @@ public:
Run()
{
// Only run if the window is still active.
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
if (activeWindows->Get(mWindowID)) {
mError->OnError(mErrorMsg);
error->OnError(mErrorMsg);
}
return NS_OK;
}
private:
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
const nsString mErrorMsg;
uint64_t mWindowID;
};
@ -62,9 +72,12 @@ private:
class SuccessCallbackRunnable : public nsRunnable
{
public:
SuccessCallbackRunnable(nsIDOMGetUserMediaSuccessCallback* aSuccess,
SuccessCallbackRunnable(
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
nsIDOMFile* aFile, uint64_t aWindowID)
: mSuccess(aSuccess)
, mError(aError)
, mFile(aFile)
, mWindowID(aWindowID) {}
@ -72,33 +85,124 @@ public:
Run()
{
// Only run if the window is still active.
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
if (activeWindows->Get(mWindowID)) {
// XPConnect is a magical unicorn.
mSuccess->OnSuccess(mFile);
success->OnSuccess(mFile);
}
return NS_OK;
}
private:
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
nsCOMPtr<nsIDOMFile> mFile;
uint64_t mWindowID;
};
/**
* Invoke the GetUserMediaDevices success callback. Wrapped in a runnable
* so that it may be called on the main thread. The error callback is also
* passed so it can be released correctly.
*/
class DeviceSuccessCallbackRunnable: public nsRunnable
{
public:
DeviceSuccessCallbackRunnable(
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
const nsTArray<nsCOMPtr<nsIMediaDevice> >& aDevices)
: mSuccess(aSuccess)
, mError(aError)
, mDevices(aDevices) {}
NS_IMETHOD
Run()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
nsCOMPtr<nsIWritableVariant> devices =
do_CreateInstance("@mozilla.org/variant;1");
int32_t len = mDevices.Length();
if (len == 0) {
devices->SetAsEmptyArray();
success->OnSuccess(devices);
return NS_OK;
}
nsTArray<nsIMediaDevice*> tmp(len);
for (int32_t i = 0; i < len; i++) {
tmp.AppendElement(mDevices.ElementAt(i));
}
devices->SetAsArray(nsIDataType::VTYPE_INTERFACE,
&NS_GET_IID(nsIMediaDevice),
mDevices.Length(),
const_cast<void*>(
static_cast<const void*>(tmp.Elements())
));
success->OnSuccess(devices);
return NS_OK;
}
private:
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices;
};
/**
* nsIMediaDevice implementation.
*/
NS_IMPL_THREADSAFE_ISUPPORTS1(MediaDevice, nsIMediaDevice)
NS_IMETHODIMP
MediaDevice::GetName(nsAString& aName)
{
aName.Assign(mName);
return NS_OK;
}
NS_IMETHODIMP
MediaDevice::GetType(nsAString& aType)
{
aType.Assign(mType);
return NS_OK;
}
MediaEngineSource*
MediaDevice::GetSource()
{
return mSource;
}
/**
* Creates a MediaStream, attaches a listener and fires off a success callback
* to the DOM with the stream.
* to the DOM with the stream. We also pass in the error callback so it can
* be released correctly.
*
* All of this must be done on the main thread!
*/
class GetUserMediaStreamRunnable : public nsRunnable
{
public:
GetUserMediaStreamRunnable(nsIDOMGetUserMediaSuccessCallback* aSuccess,
GetUserMediaStreamRunnable(
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
MediaEngineSource* aSource, StreamListeners* aListeners,
uint64_t aWindowID, TrackID aTrackID)
: mSuccess(aSuccess)
, mError(aError)
, mSource(aSource)
, mListeners(aListeners)
, mWindowID(aWindowID)
@ -109,6 +213,8 @@ public:
NS_IMETHOD
Run()
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
// Create a media stream.
nsCOMPtr<nsDOMMediaStream> stream = nsDOMMediaStream::CreateInputStream();
@ -130,16 +236,20 @@ public:
mListeners->AppendElement(listener);
// We're in the main thread, so no worries here either.
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> success(mSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> error(mError);
WindowTable* activeWindows = MediaManager::Get()->GetActiveWindows();
if (activeWindows->Get(mWindowID)) {
mSuccess->OnSuccess(stream);
success->OnSuccess(stream);
}
return NS_OK;
}
private:
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
nsRefPtr<MediaEngineSource> mSource;
StreamListeners* mListeners;
uint64_t mWindowID;
@ -158,9 +268,27 @@ private:
class GetUserMediaRunnable : public nsRunnable
{
public:
/**
* The caller can choose to provide a MediaDevice as the last argument,
* if one is not provided, a default device is automatically chosen.
*/
GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
nsIDOMGetUserMediaSuccessCallback* aSuccess,
nsIDOMGetUserMediaErrorCallback* aError,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
StreamListeners* aListeners, uint64_t aWindowID, MediaDevice* aDevice)
: mAudio(aAudio)
, mVideo(aVideo)
, mPicture(aPicture)
, mSuccess(aSuccess)
, mError(aError)
, mListeners(aListeners)
, mWindowID(aWindowID)
, mDevice(aDevice)
, mInited(true) {}
GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
StreamListeners* aListeners, uint64_t aWindowID)
: mAudio(aAudio)
, mVideo(aVideo)
@ -168,7 +296,8 @@ public:
, mSuccess(aSuccess)
, mError(aError)
, mListeners(aListeners)
, mWindowID(aWindowID) {}
, mWindowID(aWindowID)
, mInited(false) {}
~GetUserMediaRunnable() {}
@ -181,42 +310,86 @@ public:
NS_IMETHOD
Run()
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
mManager = MediaManager::Get();
// Was a device provided?
if (!mInited) {
nsresult rv = SelectDevice();
if (rv != NS_OK) {
return rv;
}
mInited = true;
}
// It is an error if audio or video are requested along with picture.
if (mPicture && (mAudio || mVideo)) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mError, NS_LITERAL_STRING("NOT_SUPPORTED_ERR"), mWindowID
mSuccess, mError, NS_LITERAL_STRING("NOT_SUPPORTED_ERR"), mWindowID
));
return NS_OK;
}
if (mPicture) {
SendPicture();
return NS_OK;
}
// XXX: Implement merging two streams (See bug 758391).
if (mAudio && mVideo) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mError, NS_LITERAL_STRING("NOT_IMPLEMENTED"), mWindowID
mSuccess, mError, NS_LITERAL_STRING("NOT_IMPLEMENTED"), mWindowID
));
return NS_OK;
}
if (mPicture) {
ProcessGetUserMediaSnapshot(mDevice->GetSource(), 0);
return NS_OK;
}
if (mVideo) {
SendVideo();
ProcessGetUserMedia(mDevice->GetSource(), kVideoTrack);
return NS_OK;
}
if (mAudio) {
SendAudio();
ProcessGetUserMedia(mDevice->GetSource(), kAudioTrack);
return NS_OK;
}
return NS_OK;
}
nsresult
SelectDevice()
{
uint32_t count;
if (mPicture || mVideo) {
nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
count = videoSources.Length();
if (count <= 0) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
));
return NS_ERROR_FAILURE;
}
mDevice = new MediaDevice(videoSources[0]);
} else {
nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
mManager->GetBackend()->EnumerateAudioDevices(&audioSources);
count = audioSources.Length();
if (count <= 0) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
));
return NS_ERROR_FAILURE;
}
mDevice = new MediaDevice(audioSources[0]);
}
return NS_OK;
}
/**
* Allocates a video or audio device and returns a MediaStream via
* a GetUserMediaStreamRunnable. Runs off the main thread.
@ -224,24 +397,16 @@ public:
void
ProcessGetUserMedia(MediaEngineSource* aSource, TrackID aTrackID)
{
/**
* Normally we would now get the name & UUID for the device and ask the
* user permission. We will do that when we have some UI. Currently,
* only the Android {picture:true} backend is functional, which does not
* need a permission prompt, as permission is implicit by user action.
*
* See bug 748835 for progress on the desktop UI.
*/
nsresult rv = aSource->Allocate();
if (NS_FAILED(rv)) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
));
return;
}
NS_DispatchToMainThread(new GetUserMediaStreamRunnable(
mSuccess.get(), aSource, mListeners, mWindowID, aTrackID
mSuccess, mError, aSource, mListeners, mWindowID, aTrackID
));
return;
}
@ -256,97 +421,97 @@ public:
nsresult rv = aSource->Allocate();
if (NS_FAILED(rv)) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
));
return;
}
/**
* Display picture capture UI here before calling Snapshot() - Bug 748835.
*/
nsCOMPtr<nsIDOMFile> file;
aSource->Snapshot(aDuration, getter_AddRefs(file));
aSource->Deallocate();
NS_DispatchToMainThread(new SuccessCallbackRunnable(
mSuccess, file, mWindowID
mSuccess, mError, file, mWindowID
));
return;
}
// {picture:true}
void
SendPicture()
{
nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
uint32_t count = videoSources.Length();
if (!count) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
));
return;
}
// We pick the first source as the "default". Work is needed here in the
// form of UI to let the user pick a source. (Also true for audio).
MediaEngineVideoSource* videoSource = videoSources[0];
ProcessGetUserMediaSnapshot(videoSource, 0 /* duration */);
}
// {video:true}
void
SendVideo()
{
nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
mManager->GetBackend()->EnumerateVideoDevices(&videoSources);
uint32_t count = videoSources.Length();
if (!count) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
));
return;
}
MediaEngineVideoSource* videoSource = videoSources[0];
ProcessGetUserMedia(videoSource, kVideoTrack);
}
// {audio:true}
void
SendAudio()
{
nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
mManager->GetBackend()->EnumerateAudioDevices(&audioSources);
uint32_t count = audioSources.Length();
if (!count) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
));
return;
}
MediaEngineAudioSource* audioSource = audioSources[0];
ProcessGetUserMedia(audioSource, kAudioTrack);
}
private:
bool mAudio;
bool mVideo;
bool mPicture;
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mSuccess;
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
StreamListeners* mListeners;
uint64_t mWindowID;
nsRefPtr<MediaDevice> mDevice;
bool mInited;
MediaManager* mManager;
};
/**
* Similar to GetUserMediaRunnable, but used for the chrome-only
* GetUserMediaDevices function. Enumerates a list of audio & video devices,
* wraps them up in nsIMediaDevice objects and returns it to the success
* callback.
*/
class GetUserMediaDevicesRunnable : public nsRunnable
{
public:
GetUserMediaDevicesRunnable(
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
: mSuccess(aSuccess)
, mError(aError) {}
~GetUserMediaDevicesRunnable() {}
NS_IMETHOD
Run()
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
uint32_t audioCount, videoCount, total, i;
MediaManager* manager = MediaManager::Get();
nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
manager->GetBackend()->EnumerateVideoDevices(&videoSources);
videoCount = videoSources.Length();
nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
manager->GetBackend()->EnumerateAudioDevices(&audioSources);
audioCount = videoSources.Length();
total = videoCount + audioCount;
nsTArray<nsCOMPtr<nsIMediaDevice> > *devices =
new nsTArray<nsCOMPtr<nsIMediaDevice> >;
for (i = 0; i < videoCount; i++) {
devices->AppendElement(new MediaDevice(videoSources[i]));
}
for (i = 0; i < audioCount; i++) {
devices->AppendElement(new MediaDevice(audioSources[i]));
}
NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(
mSuccess, mError, *devices
));
return NS_OK;
}
private:
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
};
nsRefPtr<MediaManager> MediaManager::sSingleton;
NS_IMPL_ISUPPORTS1(MediaManager, nsIObserver)
NS_IMPL_THREADSAFE_ISUPPORTS1(MediaManager, nsIObserver)
/**
* The entry point for this file. A call from Navigator::mozGetUserMedia
@ -354,16 +519,51 @@ NS_IMPL_ISUPPORTS1(MediaManager, nsIObserver)
* for handling all incoming getUserMedia calls from every window.
*/
nsresult
MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
nsIDOMGetUserMediaSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError)
MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsIMediaStreamOptions* aParams,
nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnError)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
NS_ENSURE_TRUE(aParams, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess(aOnSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
/* Get options */
nsresult rv;
bool audio, video, picture;
nsresult rv = aParams->GetPicture(&picture);
rv = aParams->GetPicture(&picture);
NS_ENSURE_SUCCESS(rv, rv);
rv = aParams->GetAudio(&audio);
NS_ENSURE_SUCCESS(rv, rv);
rv = aParams->GetVideo(&video);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMediaDevice> device;
rv = aParams->GetDevice(getter_AddRefs(device));
NS_ENSURE_SUCCESS(rv, rv);
// If a device was provided, make sure it support the type of stream requested.
if (device) {
nsString type;
device->GetType(type);
if ((picture || video) && !type.EqualsLiteral("video")) {
return NS_ERROR_FAILURE;
}
if (audio && !type.EqualsLiteral("audio")) {
return NS_ERROR_FAILURE;
}
}
// We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
nsString cameraType;
rv = aParams->GetCamera(cameraType);
NS_ENSURE_SUCCESS(rv, rv);
/**
@ -374,13 +574,13 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParam
* may point we can decide whether to extend this test there as well.
*/
#if !defined(MOZ_WEBRTC)
if (picture) {
if (picture && !aPrivileged) {
if (aWindow->GetPopupControlState() > openControlled) {
nsCOMPtr<nsIPopupWindowManager> pm =
do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
if (!pm)
if (!pm) {
return NS_OK;
}
uint32_t permission;
nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
pm->TestPermission(doc->NodePrincipal(), &permission);
@ -388,23 +588,22 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParam
nsCOMPtr<nsIDOMDocument> domDoc = aWindow->GetExtantDocument();
nsGlobalWindow::FirePopupBlockedEvent(
domDoc, aWindow, nullptr, EmptyString(), EmptyString()
);
);
return NS_OK;
}
}
}
#endif
rv = aParams->GetAudio(&audio);
NS_ENSURE_SUCCESS(rv, rv);
rv = aParams->GetVideo(&video);
NS_ENSURE_SUCCESS(rv, rv);
// We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
nsString cameraType;
rv = aParams->GetCamera(cameraType);
NS_ENSURE_SUCCESS(rv, rv);
/**
* UI integration point. Check for permission with the user!
* No UI for picture:true here, since user permission is implied by the
* preview dialog that will be shown by GetUserMediaRunnable in SendPicture.
*/
if (!aPrivileged && !picture) {
// To be filled in by code from bug 729522. If permission is denied, call
// onError, and do not continue.
}
// Store the WindowID in a hash table and mark as active. The entry is removed
// when this window is closed or navigated away from.
@ -415,11 +614,25 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParam
mActiveWindows.Put(windowID, listeners);
}
// Pass runnables along to GetUserMediaRunnable so it can add the
// MediaStreamListener to the runnable list.
nsCOMPtr<nsIRunnable> gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess, onError, listeners, windowID
);
/**
* Pass runnables along to GetUserMediaRunnable so it can add the
* MediaStreamListener to the runnable list. The last argument can
* optionally be a MediaDevice object, which should provided if one was
* selected by the user via the UI, or was provided by privileged code
* via the device: attribute via nsIMediaStreamOptions.
*/
nsCOMPtr<nsIRunnable> gUMRunnable;
if (device) {
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
windowID, static_cast<MediaDevice*>(device.get())
);
} else {
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), listeners,
windowID
);
}
if (picture) {
// ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
@ -436,6 +649,29 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParam
return NS_OK;
}
nsresult
MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnError)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
nsCOMPtr<nsIRunnable> gUMDRunnable = new GetUserMediaDevicesRunnable(
onSuccess.forget(), onError.forget()
);
nsCOMPtr<nsIThread> deviceThread;
nsresult rv = NS_NewThread(getter_AddRefs(deviceThread));
NS_ENSURE_SUCCESS(rv, rv);
deviceThread->Dispatch(gUMDRunnable, NS_DISPATCH_NORMAL);
return NS_OK;
}
MediaEngine*
MediaManager::GetBackend()
{

View File

@ -117,7 +117,33 @@ private:
typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
class MediaManager MOZ_FINAL : public nsIObserver {
class MediaDevice : public nsIMediaDevice
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEDIADEVICE
MediaDevice(MediaEngineVideoSource* aSource) {
mSource = aSource;
mType.Assign(NS_LITERAL_STRING("video"));
mSource->GetName(mName);
};
MediaDevice(MediaEngineAudioSource* aSource) {
mSource = aSource;
mType.Assign(NS_LITERAL_STRING("audio"));
mSource->GetName(mName);
};
virtual ~MediaDevice() {};
MediaEngineSource* GetSource();
private:
nsString mName;
nsString mType;
nsRefPtr<MediaEngineSource> mSource;
};
class MediaManager MOZ_FINAL : public nsIObserver
{
public:
static MediaManager* Get() {
if (!sSingleton) {
@ -135,9 +161,13 @@ public:
MediaEngine* GetBackend();
WindowTable* GetActiveWindows();
nsresult GetUserMedia(nsPIDOMWindow* aWindow, nsIMediaStreamOptions* aParams,
nsresult GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsIMediaStreamOptions* aParams,
nsIDOMGetUserMediaSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError);
nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
nsIGetUserMediaDevicesSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError);
void OnNavigation(uint64_t aWindowID);
private:

View File

@ -3,8 +3,22 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
#include "nsIVariant.idl"
#include "nsIDOMMediaStream.idl"
[scriptable, builtinclass, uuid(6de854f9-acf8-4383-b464-4803631ef309)]
interface nsIMediaDevice : nsISupports
{
readonly attribute DOMString type;
readonly attribute DOMString name;
};
[scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)]
interface nsIGetUserMediaDevicesSuccessCallback : nsISupports
{
void onSuccess(in nsIVariant devices);
};
[scriptable, function, uuid(f2a144fc-3534-4761-8c5d-989ae720f89a)]
interface nsIDOMGetUserMediaSuccessCallback : nsISupports
{
@ -21,13 +35,14 @@ interface nsIDOMGetUserMediaErrorCallback : nsISupports
void onError(in DOMString error);
};
[scriptable, uuid(8a26205e-e8f7-4372-bd15-97ff982b4c1c)]
[scriptable, uuid(92a19f9e-9fed-40d1-aeeb-b07fa7f191e8)]
interface nsIMediaStreamOptions : nsISupports
{
readonly attribute boolean audio;
readonly attribute boolean video;
readonly attribute boolean picture;
readonly attribute DOMString camera;
readonly attribute nsIMediaDevice device;
};
[scriptable, uuid(381e0071-0be5-4f6b-ae21-8e3407a37faa)]
@ -37,3 +52,11 @@ interface nsIDOMNavigatorUserMedia : nsISupports
in nsIDOMGetUserMediaSuccessCallback onsuccess,
in nsIDOMGetUserMediaErrorCallback onerror);
};
[scriptable, uuid(20e9c794-fdfe-43f4-a81b-ebd9069e0af1)]
interface nsINavigatorUserMedia : nsISupports
{
void mozGetUserMediaDevices(
in nsIGetUserMediaDevicesSuccessCallback onsuccess,
in nsIDOMGetUserMediaErrorCallback onerror);
};