Bug 916012 - Clean up gUM to use webidl unions and filter by media-type. r=bz, r=bwc

This commit is contained in:
Jan-Ivar Bruaroey 2014-04-04 05:54:25 -04:00
parent 4b3c68d60c
commit 37917f58c1
8 changed files with 100 additions and 109 deletions

View File

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
Bug 991256 requires a clobber because it renames generated mobile/android/base/widget files.
Bug 916012 moves definition from one WEBIDL_FILE to another (Bug 979886)

View File

@ -4,7 +4,7 @@
#include "base/basictypes.h"
#include "GetUserMediaRequest.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/dom/GetUserMediaRequestBinding.h"
#include "nsIScriptGlobalObject.h"
#include "nsPIDOMWindow.h"
@ -20,7 +20,7 @@ GetUserMediaRequest::GetUserMediaRequest(
: mInnerWindowID(aInnerWindow->WindowID())
, mOuterWindowID(aInnerWindow->GetOuterWindow()->WindowID())
, mCallID(aCallID)
, mConstraints(aConstraints)
, mConstraints(new MediaStreamConstraintsInternal(aConstraints))
{
SetIsDOMBinding();
}
@ -62,7 +62,7 @@ uint64_t GetUserMediaRequest::InnerWindowID()
void
GetUserMediaRequest::GetConstraints(MediaStreamConstraintsInternal &result)
{
result = mConstraints;
result = *mConstraints;
}
} // namespace dom

View File

@ -10,7 +10,6 @@
#include "nsAutoPtr.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
@ -40,7 +39,7 @@ public:
private:
uint64_t mInnerWindowID, mOuterWindowID;
const nsString mCallID;
MediaStreamConstraintsInternal mConstraints;
nsAutoPtr<MediaStreamConstraintsInternal> mConstraints;
};
} // namespace dom

View File

@ -24,6 +24,7 @@
#include "nsISupportsPrimitives.h"
#include "nsIInterfaceRequestorUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/GetUserMediaRequestBinding.h"
@ -79,6 +80,7 @@ using dom::MediaTrackConstraintSet; // Mandatory or optional constraints
using dom::MediaTrackConstraints; // Raw mMandatory (as JSObject)
using dom::GetUserMediaRequest;
using dom::Sequence;
using dom::OwningBooleanOrMediaTrackConstraintsInternal;
// Used to compare raw MediaTrackConstraintSet against normalized dictionary
// version to detect member differences, e.g. unsupported constraints.
@ -120,29 +122,6 @@ static nsresult CompareDictionaries(JSContext* aCx, JSObject *aA,
return NS_OK;
}
// Look for and return any unknown mandatory constraint. Done by comparing
// a raw MediaTrackConstraints against a normalized copy, both passed in.
static nsresult ValidateTrackConstraints(
JSContext *aCx, JSObject *aRaw,
const MediaTrackConstraintsInternal &aNormalized,
nsString *aOutUnknownConstraint)
{
// First find raw mMandatory member (use MediaTrackConstraints as helper)
JS::Rooted<JS::Value> rawval(aCx, JS::ObjectValue(*aRaw));
dom::RootedDictionary<MediaTrackConstraints> track(aCx);
bool success = track.Init(aCx, rawval);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
if (track.mMandatory.WasPassed()) {
nsresult rv = CompareDictionaries(aCx, track.mMandatory.Value(),
aNormalized.mMandatory,
aOutUnknownConstraint);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
ErrorCallbackRunnable::ErrorCallbackRunnable(
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aSuccess,
nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aError,
@ -692,6 +671,18 @@ private:
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
};
static bool
IsOn(const dom::OwningBooleanOrMediaTrackConstraintsInternal &aUnion) {
return !aUnion.IsBoolean() || aUnion.GetAsBoolean();
}
static JSObject *
GetMandatoryJSObj(const dom::OwningBooleanOrMediaTrackConstraints &aUnion) {
return (aUnion.IsMediaTrackConstraints() &&
aUnion.GetAsMediaTrackConstraints().mMandatory.WasPassed()) ?
aUnion.GetAsMediaTrackConstraints().mMandatory.Value() : nullptr;
}
/**
* Helper functions that implement the constraints algorithm from
* http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
@ -728,10 +719,16 @@ typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
template<class SourceType>
static SourceSet *
GetSources(MediaEngine *engine,
const MediaTrackConstraintsInternal &aConstraints,
const OwningBooleanOrMediaTrackConstraintsInternal &aConstraints,
void (MediaEngine::* aEnumerate)(nsTArray<nsRefPtr<SourceType> >*),
char* media_device_name = nullptr)
{
ScopedDeletePtr<SourceSet> result(new SourceSet);
if (!IsOn(aConstraints)) {
return result.forget();
}
const SourceType * const type = nullptr;
nsString deviceName;
// First collect sources
@ -762,12 +759,19 @@ static SourceSet *
}
}
if (aConstraints.IsBoolean()) {
MOZ_ASSERT(aConstraints.GetAsBoolean());
result->MoveElementsFrom(candidateSet);
return result.forget();
}
auto& constraints = aConstraints.GetAsMediaTrackConstraintsInternal();
// Then apply mandatory constraints
// Note: Iterator must be signed as it can dip below zero
for (int i = 0; i < int(candidateSet.Length()); i++) {
// Overloading instead of template specialization keeps things local
if (!SatisfyConstraint(type, aConstraints.mMandatory, *candidateSet[i])) {
if (!SatisfyConstraint(type, constraints.mMandatory, *candidateSet[i])) {
candidateSet.RemoveElementAt(i--);
}
}
@ -788,8 +792,8 @@ static SourceSet *
SourceSet tailSet;
if (aConstraints.mOptional.WasPassed()) {
const Sequence<MediaTrackConstraintSet> &array = aConstraints.mOptional.Value();
if (constraints.mOptional.WasPassed()) {
const auto &array = constraints.mOptional.Value();
for (int i = 0; i < int(array.Length()); i++) {
SourceSet rejects;
// Note: Iterator must be signed as it can dip below zero
@ -803,10 +807,9 @@ static SourceSet *
}
}
SourceSet *result = new SourceSet;
result->MoveElementsFrom(candidateSet);
result->MoveElementsFrom(tailSet);
return result;
return result.forget();
}
/**
@ -896,7 +899,8 @@ public:
}
// It is an error if audio or video are requested along with picture.
if (mConstraints.mPicture && (mConstraints.mAudio || mConstraints.mVideo)) {
if (mConstraints.mPicture &&
(IsOn(mConstraints.mAudio) || IsOn(mConstraints.mVideo))) {
Fail(NS_LITERAL_STRING("NOT_SUPPORTED_ERR"));
return NS_OK;
}
@ -907,9 +911,9 @@ public:
}
// There's a bug in the permission code that can leave us with mAudio but no audio device
ProcessGetUserMedia(((mConstraints.mAudio && mAudioDevice) ?
ProcessGetUserMedia(((IsOn(mConstraints.mAudio) && mAudioDevice) ?
mAudioDevice->GetSource() : nullptr),
((mConstraints.mVideo && mVideoDevice) ?
((IsOn(mConstraints.mVideo) && mVideoDevice) ?
mVideoDevice->GetSource() : nullptr));
return NS_OK;
}
@ -975,9 +979,9 @@ public:
{
MOZ_ASSERT(mSuccess);
MOZ_ASSERT(mError);
if (mConstraints.mPicture || mConstraints.mVideo) {
if (mConstraints.mPicture || IsOn(mConstraints.mVideo)) {
ScopedDeletePtr<SourceSet> sources (GetSources(backend,
mConstraints.mVideom, &MediaEngine::EnumerateVideoDevices));
mConstraints.mVideo, &MediaEngine::EnumerateVideoDevices));
if (!sources->Length()) {
Fail(NS_LITERAL_STRING("NO_DEVICES_FOUND"));
@ -988,9 +992,9 @@ public:
LOG(("Selected video device"));
}
if (mConstraints.mAudio) {
if (IsOn(mConstraints.mAudio)) {
ScopedDeletePtr<SourceSet> sources (GetSources(backend,
mConstraints.mAudiom, &MediaEngine::EnumerateAudioDevices));
mConstraints.mAudio, &MediaEngine::EnumerateAudioDevices));
if (!sources->Length()) {
Fail(NS_LITERAL_STRING("NO_DEVICES_FOUND"));
@ -1126,11 +1130,11 @@ public:
else
backend = mManager->GetBackend(mWindowId);
ScopedDeletePtr<SourceSet> final (GetSources(backend, mConstraints.mVideom,
ScopedDeletePtr<SourceSet> final (GetSources(backend, mConstraints.mVideo,
&MediaEngine::EnumerateVideoDevices,
mLoopbackVideoDevice));
{
ScopedDeletePtr<SourceSet> s (GetSources(backend, mConstraints.mAudiom,
ScopedDeletePtr<SourceSet> s (GetSources(backend, mConstraints.mAudio,
&MediaEngine::EnumerateAudioDevices,
mLoopbackAudioDevice));
final->MoveElementsFrom(*s);
@ -1305,52 +1309,40 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
Maybe<JSAutoCompartment> ac;
if (aRawConstraints.mAudio.IsObject() || aRawConstraints.mVideo.IsObject()) {
ac.construct(aCx, (aRawConstraints.mVideo.IsObject()?
aRawConstraints.mVideo.GetAsObject() :
aRawConstraints.mAudio.GetAsObject()));
JS::Rooted<JSObject*> audioObj (aCx, GetMandatoryJSObj(aRawConstraints.mAudio));
JS::Rooted<JSObject*> videoObj (aCx, GetMandatoryJSObj(aRawConstraints.mVideo));
if (audioObj || videoObj) {
ac.construct(aCx, audioObj? audioObj : videoObj);
}
// aRawConstraints has JSObjects in it, so process it by copying it into
// aRawConstraints may have JSObject in mMandatory, so copy everything into
// MediaStreamConstraintsInternal which does not.
dom::RootedDictionary<MediaStreamConstraintsInternal> c(aCx);
JS::Rooted<JS::Value> temp(aCx);
// This isn't the fastest way to copy a MediaStreamConstraints into a
// MediaStreamConstraintsInternal, but requires less code maintenance than an
// explicit member-by-member copy, and should be safe given the circumstances.
aRawConstraints.ToObject(aCx, &temp);
bool success = c.Init(aCx, temp);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
// TODO: Simplify this part once Bug 767924 is fixed.
// Since we cannot yet use unions on non-objects, we process the raw object
// into discrete members for internal use until Bug 767924 is fixed
// Validate mandatory constraints by detecting any unknown constraints.
// Done by comparing the raw MediaTrackConstraints against the normalized copy
nsresult rv;
nsString unknownConstraintFound;
if (aRawConstraints.mAudio.IsObject()) {
JS::Rooted<JS::Value> temp(aCx,
JS::ObjectValue(*aRawConstraints.mAudio.GetAsObject()));
bool success = c.mAudiom.Init(aCx, temp);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
rv = ValidateTrackConstraints(aCx, aRawConstraints.mAudio.GetAsObject(),
c.mAudiom, &unknownConstraintFound);
if (audioObj) {
nsresult rv = CompareDictionaries(aCx, audioObj,
c.mAudio.GetAsMediaTrackConstraintsInternal().mMandatory,
&unknownConstraintFound);
NS_ENSURE_SUCCESS(rv, rv);
c.mAudio = true;
} else {
c.mAudio = aRawConstraints.mAudio.GetAsBoolean();
}
if (aRawConstraints.mVideo.IsObject()) {
JS::Rooted<JS::Value> temp(aCx,
JS::ObjectValue(*aRawConstraints.mVideo.GetAsObject()));
bool success = c.mVideom.Init(aCx, temp);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
rv = ValidateTrackConstraints(aCx, aRawConstraints.mVideo.GetAsObject(),
c.mVideom, &unknownConstraintFound);
if (videoObj) {
nsresult rv = CompareDictionaries(aCx, videoObj,
c.mVideo.GetAsMediaTrackConstraintsInternal().mMandatory,
&unknownConstraintFound);
NS_ENSURE_SUCCESS(rv, rv);
c.mVideo = true;
} else {
c.mVideo = aRawConstraints.mVideo.GetAsBoolean();
}
c.mPicture = aRawConstraints.mPicture;
c.mFake = aRawConstraints.mFake;
/**
* If we were asked to get a picture, before getting a snapshot, we check if
@ -1434,7 +1426,7 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
aPrivileged = true;
}
if (!Preferences::GetBool("media.navigator.video.enabled", true)) {
c.mVideo = false;
c.mVideo.SetAsBoolean() = false;
}
// Pass callbacks and MediaStreamListener along to GetUserMediaRunnable.
@ -1474,7 +1466,7 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
NS_ENSURE_SUCCESS(rv, rv);
uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
if (c.mAudio) {
if (IsOn(c.mAudio)) {
rv = permManager->TestExactPermissionFromPrincipal(
aWindow->GetExtantDoc()->NodePrincipal(), "microphone", &audioPerm);
NS_ENSURE_SUCCESS(rv, rv);
@ -1484,7 +1476,7 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
}
uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
if (c.mVideo) {
if (IsOn(c.mVideo)) {
rv = permManager->TestExactPermissionFromPrincipal(
aWindow->GetExtantDoc()->NodePrincipal(), "camera", &videoPerm);
NS_ENSURE_SUCCESS(rv, rv);
@ -1493,18 +1485,18 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
}
}
if ((!c.mAudio || audioPerm) && (!c.mVideo || videoPerm)) {
if ((!IsOn(c.mAudio) || audioPerm) && (!IsOn(c.mVideo) || videoPerm)) {
// All permissions we were about to request already have a saved value.
if (c.mAudio && audioPerm == nsIPermissionManager::DENY_ACTION) {
c.mAudio = false;
if (IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) {
c.mAudio.SetAsBoolean() = false;
runnable->SetContraints(c);
}
if (c.mVideo && videoPerm == nsIPermissionManager::DENY_ACTION) {
c.mVideo = false;
if (IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION) {
c.mVideo.SetAsBoolean() = false;
runnable->SetContraints(c);
}
if (!c.mAudio && !c.mVideo) {
if (!IsOn(c.mAudio) && !IsOn(c.mVideo)) {
return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
}

View File

@ -21,6 +21,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "prlog.h"
#include "DOMMediaStream.h"

View File

@ -179,8 +179,8 @@ MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest
dom::MediaStreamConstraintsInternal constraints;
mRequest->GetConstraints(constraints);
mAudio = constraints.mAudio;
mVideo = constraints.mVideo;
mAudio = !constraints.mAudio.IsBoolean() || constraints.mAudio.GetAsBoolean();
mVideo = !constraints.mVideo.IsBoolean() || constraints.mVideo.GetAsBoolean();
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
nsCOMPtr<nsIMediaDevice> device(aDevices[i]);

View File

@ -10,6 +10,23 @@
* liability, trademark and document use rules apply.
*/
// These dictionaries need to be in a separate file from their
// MediaTrackConstraints* counterparts due to a webidl compiler limitation.
dictionary MediaStreamConstraints {
(boolean or MediaTrackConstraints) audio = false;
(boolean or MediaTrackConstraints) video = false;
boolean picture = false;
boolean fake = false;
};
dictionary MediaStreamConstraintsInternal {
(boolean or MediaTrackConstraintsInternal) audio;
(boolean or MediaTrackConstraintsInternal) video;
boolean picture = false;
boolean fake = false;
};
interface MediaStream {
// readonly attribute DOMString id;
sequence<AudioStreamTrack> getAudioTracks ();

View File

@ -29,31 +29,13 @@ dictionary MediaTrackConstraintSet {
// typedef MediaTrackConstraintSet MediaTrackConstraint; // TODO: Bug 913053
dictionary MediaStreamConstraints {
(boolean or object) audio = false; // TODO: Once Bug 767924 fixed change to
(boolean or object) video = false; // (boolean or MediaTrackConstraints)
boolean picture = false;
boolean fake = false;
};
// Internal dictionary to process the raw objects in (boolean or object)
// workaround above. Since we cannot yet use unions on non-objects, we process
// the data into discrete members for internal use until Bug 767924 is fixed:
dictionary MediaStreamConstraintsInternal {
boolean audio = false;
boolean video = false;
boolean picture = false;
boolean fake = false;
MediaTrackConstraintsInternal audiom;
MediaTrackConstraintsInternal videom;
};
dictionary MediaTrackConstraints {
object mandatory; // so we can see unknown + unsupported constraints
sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
};
// Internal dictionary holds result of processing raw MediaTrackConstraints above
dictionary MediaTrackConstraintsInternal {
MediaTrackConstraintSet mandatory; // holds only supported constraints
sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint