mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 752352: Implement getUserMediaDevices for privileged chrome code; r=smaug, r=jesup
This commit is contained in:
parent
402924f707
commit
ef6ae63b3f
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user