Bug 1046245 - enumerateDevices (harmless interface version). r=smaug, r=jesup

This commit is contained in:
Jan-Ivar Bruaroey 2015-03-03 09:51:05 -05:00
parent 98536af974
commit 08e703b65e
11 changed files with 283 additions and 19 deletions

View File

@ -0,0 +1,67 @@
/* 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 "mozilla/dom/MediaDeviceInfo.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/MediaManager.h"
#include "nsIScriptGlobalObject.h"
namespace mozilla {
namespace dom {
MediaDeviceInfo::MediaDeviceInfo(const nsAString& aDeviceId,
MediaDeviceKind aKind,
const nsAString& aLabel,
const nsAString& aGroupId)
: mKind(aKind)
, mDeviceId(aDeviceId)
, mLabel(aLabel)
, mGroupId(aGroupId) {}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(MediaDeviceInfo)
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaDeviceInfo)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaDeviceInfo)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaDeviceInfo)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
MediaDeviceInfo::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return MediaDeviceInfoBinding::Wrap(aCx, this, aGivenProto);
}
nsISupports* MediaDeviceInfo::GetParentObject()
{
return nullptr;
}
void MediaDeviceInfo::GetDeviceId(nsString& retval)
{
retval = mDeviceId;
}
MediaDeviceKind
MediaDeviceInfo::Kind()
{
return mKind;
}
void MediaDeviceInfo::GetGroupId(nsString& retval)
{
retval = mGroupId;
}
void MediaDeviceInfo::GetLabel(nsString& retval)
{
retval = mLabel;
}
MediaDeviceKind Kind();
void GetLabel(nsString& retval);
void GetGroupId(nsString& retval);
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,60 @@
/* 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 mozilla_dom_MediaDeviceInfo_h
#define mozilla_dom_MediaDeviceInfo_h
#include "mozilla/ErrorResult.h"
#include "nsISupportsImpl.h"
#include "mozilla/dom/BindingUtils.h"
#include "MediaDeviceInfoBinding.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
class Promise;
struct MediaStreamConstraints;
#define MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID \
{0x25091870, 0x84d6, 0x4acf, {0xaf, 0x97, 0x6e, 0xd5, 0x5b, 0xe0, 0x47, 0xb2}}
class MediaDeviceInfo final : public nsISupports, public nsWrapperCache
{
public:
explicit MediaDeviceInfo(const nsAString& aDeviceId,
MediaDeviceKind aKind,
const nsAString& aLabel,
const nsAString& aGroupId = nsString());
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaDeviceInfo)
NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
JSObject*
WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
nsISupports* GetParentObject();
void GetDeviceId(nsString& retval);
MediaDeviceKind Kind();
void GetLabel(nsString& retval);
void GetGroupId(nsString& retval);
private:
MediaDeviceKind mKind;
nsString mDeviceId;
nsString mLabel;
nsString mGroupId;
virtual ~MediaDeviceInfo() {}
};
NS_DEFINE_STATIC_IID_ACCESSOR(MediaDeviceInfo,
MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_MediaDeviceInfo_h

View File

@ -37,6 +37,67 @@ private:
nsRefPtr<Promise> mPromise;
};
class MediaDevices::EnumDevResolver : public nsIGetUserMediaDevicesSuccessCallback
{
public:
NS_DECL_ISUPPORTS
explicit EnumDevResolver(Promise* aPromise) : mPromise(aPromise) {}
NS_IMETHOD
OnSuccess(nsIVariant* aDevices) override
{
// Cribbed from MediaPermissionGonk.cpp
nsIID elementIID;
uint16_t elementType;
// Create array for nsIMediaDevice
nsTArray<nsCOMPtr<nsIMediaDevice>> devices;
// Contain the fumes
{
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;
}
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 refcount for rawptr
}
NS_Free(rawArray); // explicitly free memory from nsIVariant::GetAsArray
}
nsTArray<nsRefPtr<MediaDeviceInfo>> infos;
for (auto& device : devices) {
nsString type;
device->GetType(type);
bool isVideo = type.EqualsLiteral("video");
bool isAudio = type.EqualsLiteral("audio");
if (isVideo || isAudio) {
MediaDeviceKind kind = isVideo ?
MediaDeviceKind::Videoinput : MediaDeviceKind::Audioinput;
// TODO: return anonymized id, +label (origins w/gUM permission) (1046245)
nsRefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(nsString(), kind,
nsString());
infos.AppendElement(info);
}
}
mPromise->MaybeResolve(infos);
return NS_OK;
}
private:
virtual ~EnumDevResolver() {}
nsRefPtr<Promise> mPromise;
};
class MediaDevices::GumRejecter : public nsIDOMGetUserMediaErrorCallback
{
public:
@ -61,17 +122,17 @@ private:
};
NS_IMPL_ISUPPORTS(MediaDevices::GumResolver, nsIDOMGetUserMediaSuccessCallback)
NS_IMPL_ISUPPORTS(MediaDevices::EnumDevResolver, nsIGetUserMediaDevicesSuccessCallback)
NS_IMPL_ISUPPORTS(MediaDevices::GumRejecter, nsIDOMGetUserMediaErrorCallback)
already_AddRefed<Promise>
MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
ErrorResult &aRv)
{
ErrorResult rv;
nsPIDOMWindow* window = GetOwner();
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
nsRefPtr<Promise> p = Promise::Create(go, aRv);
NS_ENSURE_TRUE(!rv.Failed(), nullptr);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
nsRefPtr<GumResolver> resolver = new GumResolver(p);
nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
@ -81,6 +142,21 @@ MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
return p.forget();
}
already_AddRefed<Promise>
MediaDevices::EnumerateDevices(ErrorResult &aRv)
{
nsPIDOMWindow* window = GetOwner();
nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
nsRefPtr<Promise> p = Promise::Create(go, aRv);
NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
nsRefPtr<EnumDevResolver> resolver = new EnumDevResolver(p);
nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
aRv = MediaManager::Get()->EnumerateDevices(window, resolver, rejecter);
return p.forget();
}
NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN(MediaDevices)

View File

@ -2,8 +2,8 @@
* 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 MediaDevices_h__
#define MediaDevices_h__
#ifndef mozilla_dom_MediaDevices_h
#define mozilla_dom_MediaDevices_h
#include "mozilla/ErrorResult.h"
#include "nsISupportsImpl.h"
@ -35,8 +35,12 @@ public:
already_AddRefed<Promise>
GetUserMedia(const MediaStreamConstraints& aConstraints, ErrorResult &aRv);
already_AddRefed<Promise>
EnumerateDevices(ErrorResult &aRv);
private:
class GumResolver;
class EnumDevResolver;
class GumRejecter;
virtual ~MediaDevices() {}
@ -48,4 +52,4 @@ NS_DEFINE_STATIC_IID_ACCESSOR(MediaDevices,
} // namespace dom
} // namespace mozilla
#endif // MediaDevices_h__
#endif // mozilla_dom_MediaDevices_h

View File

@ -1577,16 +1577,8 @@ MediaManager::GetUserMedia(
#endif //MOZ_B2G
}
// 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();
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
StreamListeners* listeners = GetActiveWindows()->Get(windowID);
if (!listeners) {
listeners = new StreamListeners;
GetActiveWindows()->Put(windowID, listeners);
}
StreamListeners* listeners = AddWindowID(windowID);
// Create a disabled listener to act as a placeholder
GetUserMediaCallbackMediaStreamListener* listener =
@ -1828,6 +1820,21 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
return NS_OK;
}
nsresult
MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnFailure)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
MediaStreamConstraints c;
c.mVideo.SetAsBoolean() = true;
c.mAudio.SetAsBoolean() = true;
AddWindowID(aWindow->WindowID());
return GetUserMediaDevices(aWindow, c, aOnSuccess, aOnFailure, 0);
}
MediaEngine*
MediaManager::GetBackend(uint64_t aWindowId)
{
@ -1896,6 +1903,22 @@ MediaManager::OnNavigation(uint64_t aWindowID)
}
}
StreamListeners*
MediaManager::AddWindowID(uint64_t aWindowId)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
// Store the WindowID in a hash table and mark as active. The entry is removed
// when this window is closed or navigated away from.
// This is safe since we're on main-thread, and the windowlist can only
// be invalidated from the main-thread (see OnNavigation)
StreamListeners* listeners = GetActiveWindows()->Get(aWindowId);
if (!listeners) {
listeners = new StreamListeners;
GetActiveWindows()->Put(aWindowId, listeners);
}
return listeners;
}
void
MediaManager::RemoveWindowID(uint64_t aWindowId)
{

View File

@ -596,11 +596,18 @@ public:
nsIGetUserMediaDevicesSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError,
uint64_t aInnerWindowID = 0);
nsresult EnumerateDevices(nsPIDOMWindow* aWindow,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnFailure);
nsresult EnumerateDevices(nsPIDOMWindow* aWindow, dom::Promise& aPromise);
void OnNavigation(uint64_t aWindowID);
MediaEnginePrefs mPrefs;
private:
StreamListeners* AddWindowID(uint64_t aWindowId);
WindowTable *GetActiveWindows() {
NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
return &mActiveWindows;

View File

@ -146,6 +146,7 @@ EXPORTS.mozilla.dom += [
'AudioTrack.h',
'AudioTrackList.h',
'GetUserMediaRequest.h',
'MediaDeviceInfo.h',
'MediaDevices.h',
'MediaStreamError.h',
'MediaStreamTrack.h',
@ -183,6 +184,7 @@ UNIFIED_SOURCES += [
'MediaDecoder.cpp',
'MediaDecoderReader.cpp',
'MediaDecoderStateMachine.cpp',
'MediaDeviceInfo.cpp',
'MediaDevices.cpp',
'MediaManager.cpp',
'MediaRecorder.cpp',

View File

@ -644,6 +644,8 @@ var interfaceNamesInGlobalScope =
"LocalMediaStream",
// IMPORTANT: Do not change this list without review from a DOM peer!
"Location",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MediaDeviceInfo",
// IMPORTANT: Do not change this list without review from a DOM peer!
"MediaDevices",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -0,0 +1,22 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* http://dev.w3.org/2011/webrtc/editor/getusermedia.html
*/
enum MediaDeviceKind {
"audioinput",
"audiooutput",
"videoinput"
};
[Func="Navigator::HasUserMediaSupport"]
interface MediaDeviceInfo {
readonly attribute DOMString deviceId;
readonly attribute MediaDeviceKind kind;
readonly attribute DOMString label;
readonly attribute DOMString groupId;
};

View File

@ -12,12 +12,12 @@
[Func="Navigator::HasUserMediaSupport"]
interface MediaDevices : EventTarget {
// attribute EventHandler ondevicechange;
//
// void enumerateDevices (MediaDeviceInfoCallback resultCallback);
//
// attribute EventHandler ondevicechange;
// static Dictionary getSupportedConstraints (DOMString kind);
[Throws, Func="Navigator::HasUserMediaSupport"]
[Throws]
Promise<sequence<MediaDeviceInfo>> enumerateDevices();
[Throws]
Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
};

View File

@ -262,6 +262,7 @@ WEBIDL_FILES = [
'ListBoxObject.webidl',
'LocalMediaStream.webidl',
'Location.webidl',
'MediaDeviceInfo.webidl',
'MediaDevices.webidl',
'MediaElementAudioSourceNode.webidl',
'MediaError.webidl',