mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1181896 - make gUM fail w/OverconstrainedError and candidate argument r=jesup
This commit is contained in:
parent
1fedf527e6
commit
6dccc3ae57
@ -949,10 +949,26 @@ GetSources(MediaEngine *engine, dom::MediaSourceEnum aSrcType,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class DeviceType>
|
||||||
|
static bool
|
||||||
|
AreUnfitSettings(const MediaTrackConstraints &aConstraints,
|
||||||
|
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||||
|
{
|
||||||
|
nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
|
||||||
|
aggregateConstraints.AppendElement(&aConstraints);
|
||||||
|
|
||||||
|
for (auto& source : aSources) {
|
||||||
|
if (source->GetBestFitnessDistance(aggregateConstraints) != UINT32_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Apply constrains to a supplied list of sources (removes items from the list)
|
// Apply constrains to a supplied list of sources (removes items from the list)
|
||||||
|
|
||||||
template<class DeviceType>
|
template<class DeviceType>
|
||||||
static void
|
static const char*
|
||||||
SelectSettings(const MediaTrackConstraints &aConstraints,
|
SelectSettings(const MediaTrackConstraints &aConstraints,
|
||||||
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
nsTArray<nsRefPtr<DeviceType>>& aSources)
|
||||||
{
|
{
|
||||||
@ -963,6 +979,7 @@ SelectSettings(const MediaTrackConstraints &aConstraints,
|
|||||||
// Stack constraintSets that pass, starting with the required one, because the
|
// Stack constraintSets that pass, starting with the required one, because the
|
||||||
// whole stack must be re-satisfied each time a capability-set is ruled out
|
// whole stack must be re-satisfied each time a capability-set is ruled out
|
||||||
// (this avoids storing state or pushing algorithm into the lower-level code).
|
// (this avoids storing state or pushing algorithm into the lower-level code).
|
||||||
|
nsTArray<nsRefPtr<DeviceType>> unsatisfactory;
|
||||||
nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
|
nsTArray<const MediaTrackConstraintSet*> aggregateConstraints;
|
||||||
aggregateConstraints.AppendElement(&c);
|
aggregateConstraints.AppendElement(&c);
|
||||||
|
|
||||||
@ -971,6 +988,7 @@ SelectSettings(const MediaTrackConstraints &aConstraints,
|
|||||||
for (uint32_t i = 0; i < aSources.Length();) {
|
for (uint32_t i = 0; i < aSources.Length();) {
|
||||||
uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
|
uint32_t distance = aSources[i]->GetBestFitnessDistance(aggregateConstraints);
|
||||||
if (distance == UINT32_MAX) {
|
if (distance == UINT32_MAX) {
|
||||||
|
unsatisfactory.AppendElement(aSources[i]);
|
||||||
aSources.RemoveElementAt(i);
|
aSources.RemoveElementAt(i);
|
||||||
} else {
|
} else {
|
||||||
ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
|
ordered.insert(std::pair<uint32_t, nsRefPtr<DeviceType>>(distance,
|
||||||
@ -978,6 +996,49 @@ SelectSettings(const MediaTrackConstraints &aConstraints,
|
|||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!aSources.Length()) {
|
||||||
|
// None selected. The spec says to report a constraint that satisfies NONE
|
||||||
|
// of the sources. Unfortunately, this is a bit laborious to find out, and
|
||||||
|
// requires updating as new constraints are added!
|
||||||
|
|
||||||
|
if (c.mDeviceId.IsConstrainDOMStringParameters()) {
|
||||||
|
MediaTrackConstraints fresh;
|
||||||
|
fresh.mDeviceId = c.mDeviceId;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "deviceId";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mWidth.IsConstrainLongRange()) {
|
||||||
|
MediaTrackConstraints fresh;
|
||||||
|
fresh.mWidth = c.mWidth;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "width";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mHeight.IsConstrainLongRange()) {
|
||||||
|
MediaTrackConstraints fresh;
|
||||||
|
fresh.mHeight = c.mHeight;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "height";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mFrameRate.IsConstrainDoubleRange()) {
|
||||||
|
MediaTrackConstraints fresh;
|
||||||
|
fresh.mFrameRate = c.mFrameRate;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "frameRate";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (c.mFacingMode.IsConstrainDOMStringParameters()) {
|
||||||
|
MediaTrackConstraints fresh;
|
||||||
|
fresh.mFacingMode = c.mFacingMode;
|
||||||
|
if (AreUnfitSettings(fresh, unsatisfactory)) {
|
||||||
|
return "facingMode";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
// Order devices by shortest distance
|
// Order devices by shortest distance
|
||||||
for (auto& ordinal : ordered) {
|
for (auto& ordinal : ordered) {
|
||||||
aSources.RemoveElement(ordinal.second);
|
aSources.RemoveElement(ordinal.second);
|
||||||
@ -1006,9 +1067,10 @@ SelectSettings(const MediaTrackConstraints &aConstraints,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static const char*
|
||||||
SelectSettings(MediaStreamConstraints &aConstraints,
|
SelectSettings(MediaStreamConstraints &aConstraints,
|
||||||
nsTArray<nsRefPtr<MediaDevice>>& aSources)
|
nsTArray<nsRefPtr<MediaDevice>>& aSources)
|
||||||
{
|
{
|
||||||
@ -1016,7 +1078,6 @@ SelectSettings(MediaStreamConstraints &aConstraints,
|
|||||||
// a candidate set is overconstrained (zero members), we must split up the
|
// a candidate set is overconstrained (zero members), we must split up the
|
||||||
// list into videos and audios, and put it back together again at the end.
|
// list into videos and audios, and put it back together again at the end.
|
||||||
|
|
||||||
bool overconstrained = false;
|
|
||||||
nsTArray<nsRefPtr<VideoDevice>> videos;
|
nsTArray<nsRefPtr<VideoDevice>> videos;
|
||||||
nsTArray<nsRefPtr<AudioDevice>> audios;
|
nsTArray<nsRefPtr<AudioDevice>> audios;
|
||||||
|
|
||||||
@ -1032,25 +1093,21 @@ SelectSettings(MediaStreamConstraints &aConstraints,
|
|||||||
aSources.Clear();
|
aSources.Clear();
|
||||||
MOZ_ASSERT(!aSources.Length());
|
MOZ_ASSERT(!aSources.Length());
|
||||||
|
|
||||||
|
const char* badConstraint = nullptr;
|
||||||
|
|
||||||
if (IsOn(aConstraints.mVideo)) {
|
if (IsOn(aConstraints.mVideo)) {
|
||||||
SelectSettings(GetInvariant(aConstraints.mVideo), videos);
|
badConstraint = SelectSettings(GetInvariant(aConstraints.mVideo), videos);
|
||||||
if (!videos.Length()) {
|
|
||||||
overconstrained = true;
|
|
||||||
}
|
|
||||||
for (auto& video : videos) {
|
for (auto& video : videos) {
|
||||||
aSources.AppendElement(video);
|
aSources.AppendElement(video);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (IsOn(aConstraints.mAudio)) {
|
if (audios.Length() && IsOn(aConstraints.mAudio)) {
|
||||||
SelectSettings(GetInvariant(aConstraints.mAudio), audios);
|
badConstraint = SelectSettings(GetInvariant(aConstraints.mAudio), audios);
|
||||||
if (!audios.Length()) {
|
|
||||||
overconstrained = true;
|
|
||||||
}
|
|
||||||
for (auto& audio : audios) {
|
for (auto& audio : audios) {
|
||||||
aSources.AppendElement(audio);
|
aSources.AppendElement(audio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return !overconstrained;
|
return badConstraint;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1697,7 +1754,7 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
auto& vc = c.mVideo.GetAsMediaTrackConstraints();
|
auto& vc = c.mVideo.GetAsMediaTrackConstraints();
|
||||||
videoType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
videoType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
||||||
vc.mMediaSource,
|
vc.mMediaSource,
|
||||||
videoType);
|
dom::MediaSourceEnum::Other);
|
||||||
switch (videoType) {
|
switch (videoType) {
|
||||||
case dom::MediaSourceEnum::Camera:
|
case dom::MediaSourceEnum::Camera:
|
||||||
break;
|
break;
|
||||||
@ -1740,7 +1797,10 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
case dom::MediaSourceEnum::Other:
|
case dom::MediaSourceEnum::Other:
|
||||||
default: {
|
default: {
|
||||||
nsRefPtr<MediaStreamError> error =
|
nsRefPtr<MediaStreamError> error =
|
||||||
new MediaStreamError(aWindow, NS_LITERAL_STRING("NotFoundError"));
|
new MediaStreamError(aWindow,
|
||||||
|
NS_LITERAL_STRING("OverconstrainedError"),
|
||||||
|
NS_LITERAL_STRING(""),
|
||||||
|
NS_LITERAL_STRING("mediaSource"));
|
||||||
onFailure->OnError(error);
|
onFailure->OnError(error);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -1785,7 +1845,7 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
auto& ac = c.mAudio.GetAsMediaTrackConstraints();
|
auto& ac = c.mAudio.GetAsMediaTrackConstraints();
|
||||||
audioType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
audioType = StringToEnum(dom::MediaSourceEnumValues::strings,
|
||||||
ac.mMediaSource,
|
ac.mMediaSource,
|
||||||
audioType);
|
dom::MediaSourceEnum::Other);
|
||||||
// Work around WebIDL default since spec uses same dictionary w/audio & video.
|
// Work around WebIDL default since spec uses same dictionary w/audio & video.
|
||||||
if (audioType == dom::MediaSourceEnum::Camera) {
|
if (audioType == dom::MediaSourceEnum::Camera) {
|
||||||
audioType = dom::MediaSourceEnum::Microphone;
|
audioType = dom::MediaSourceEnum::Microphone;
|
||||||
@ -1812,7 +1872,10 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
case dom::MediaSourceEnum::Other:
|
case dom::MediaSourceEnum::Other:
|
||||||
default: {
|
default: {
|
||||||
nsRefPtr<MediaStreamError> error =
|
nsRefPtr<MediaStreamError> error =
|
||||||
new MediaStreamError(aWindow, NS_LITERAL_STRING("NotFoundError"));
|
new MediaStreamError(aWindow,
|
||||||
|
NS_LITERAL_STRING("OverconstrainedError"),
|
||||||
|
NS_LITERAL_STRING(""),
|
||||||
|
NS_LITERAL_STRING("mediaSource"));
|
||||||
onFailure->OnError(error);
|
onFailure->OnError(error);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@ -1905,8 +1968,19 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply any constraints. This modifies the list.
|
// Apply any constraints. This modifies the list.
|
||||||
|
const char* badConstraint = SelectSettings(c, *devices);
|
||||||
if (!SelectSettings(c, *devices)) {
|
if (badConstraint) {
|
||||||
|
nsString constraint;
|
||||||
|
constraint.AssignASCII(badConstraint);
|
||||||
|
nsRefPtr<MediaStreamError> error =
|
||||||
|
new MediaStreamError(window,
|
||||||
|
NS_LITERAL_STRING("OverconstrainedError"),
|
||||||
|
NS_LITERAL_STRING(""),
|
||||||
|
constraint);
|
||||||
|
onFailure->OnError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!devices->Length()) {
|
||||||
nsRefPtr<MediaStreamError> error =
|
nsRefPtr<MediaStreamError> error =
|
||||||
new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
|
new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
|
||||||
onFailure->OnError(error);
|
onFailure->OnError(error);
|
||||||
|
@ -28,8 +28,9 @@ BaseMediaMgrError::BaseMediaMgrError(const nsAString& aName,
|
|||||||
} else if (mName.EqualsLiteral("InternalError")) {
|
} else if (mName.EqualsLiteral("InternalError")) {
|
||||||
mMessage.AssignLiteral("Internal error.");
|
mMessage.AssignLiteral("Internal error.");
|
||||||
} else if (mName.EqualsLiteral("NotSupportedError")) {
|
} else if (mName.EqualsLiteral("NotSupportedError")) {
|
||||||
mMessage.AssignLiteral("Constraints with no audio or video in it are not "
|
mMessage.AssignLiteral("The operation is not supported.");
|
||||||
"supported");
|
} else if (mName.EqualsLiteral("OverconstrainedError")) {
|
||||||
|
mMessage.AssignLiteral("Constraints could be not satisfied.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,13 @@ function mustSucceed(msg, f) {
|
|||||||
e => is(e.name, null, msg + " must succeed: " + e.message));
|
e => is(e.name, null, msg + " must succeed: " + e.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
function mustFailWith(msg, reason, f) {
|
function mustFailWith(msg, reason, constraint, f) {
|
||||||
return f().then(() => ok(false, msg + " must fail"),
|
return f().then(() => ok(false, msg + " must fail"), e => {
|
||||||
e => is(e.name, reason, msg + " must fail: " + e.message));
|
is(e.name, reason, msg + " must fail: " + e.message);
|
||||||
|
if (constraint) {
|
||||||
|
is(e.constraint, constraint, msg + " must fail w/correct constraint.");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var pushPrefs = (...p) => new Promise(r => SpecialPowers.pushPrefEnv({set: p}, r));
|
var pushPrefs = (...p) => new Promise(r => SpecialPowers.pushPrefEnv({set: p}, r));
|
||||||
@ -49,13 +53,15 @@ runTest(() =>
|
|||||||
audio: { deviceId: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" },
|
audio: { deviceId: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" },
|
||||||
fake: true,
|
fake: true,
|
||||||
})))
|
})))
|
||||||
.then(() => mustFailWith("unknown exact deviceId on video", "NotFoundError",
|
.then(() => mustFailWith("unknown exact deviceId on video",
|
||||||
() => navigator.mediaDevices.getUserMedia({
|
"OverconstrainedError", "deviceId",
|
||||||
|
() => navigator.mediaDevices.getUserMedia({
|
||||||
video: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
video: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
||||||
fake: true,
|
fake: true,
|
||||||
})))
|
})))
|
||||||
.then(() => mustFailWith("unknown exact deviceId on audio", "NotFoundError",
|
.then(() => mustFailWith("unknown exact deviceId on audio",
|
||||||
() => navigator.mediaDevices.getUserMedia({
|
"OverconstrainedError", "deviceId",
|
||||||
|
() => navigator.mediaDevices.getUserMedia({
|
||||||
audio: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
audio: { deviceId: { exact: "unknown9qHr8B0JIbcHlbl9xR+jMbZZ8WyoPfpCXPfc=" } },
|
||||||
fake: true,
|
fake: true,
|
||||||
})))
|
})))
|
||||||
|
@ -39,9 +39,14 @@ var tests = [
|
|||||||
{ message: "browser screensharing requires permission",
|
{ message: "browser screensharing requires permission",
|
||||||
constraints: { video: { mediaSource: 'browser' } },
|
constraints: { video: { mediaSource: 'browser' } },
|
||||||
error: "PermissionDeniedError" },
|
error: "PermissionDeniedError" },
|
||||||
{ message: "unknown mediaSource fails",
|
{ message: "unknown mediaSource in video fails",
|
||||||
constraints: { video: { mediaSource: 'uncle' } },
|
constraints: { video: { mediaSource: 'uncle' } },
|
||||||
error: "NotFoundError" },
|
error: "OverconstrainedError",
|
||||||
|
constraint: "mediaSource" },
|
||||||
|
{ message: "unknown mediaSource in audio fails",
|
||||||
|
constraints: { audio: { mediaSource: 'uncle' } },
|
||||||
|
error: "OverconstrainedError",
|
||||||
|
constraint: "mediaSource" },
|
||||||
{ message: "emtpy constraint fails",
|
{ message: "emtpy constraint fails",
|
||||||
constraints: { },
|
constraints: { },
|
||||||
error: "NotSupportedError" },
|
error: "NotSupportedError" },
|
||||||
@ -106,8 +111,13 @@ runTest(function() {
|
|||||||
|
|
||||||
return tests.reduce((p, test) =>
|
return tests.reduce((p, test) =>
|
||||||
p.then(() => navigator.mediaDevices.getUserMedia(test.constraints))
|
p.then(() => navigator.mediaDevices.getUserMedia(test.constraints))
|
||||||
.then(() => is(null, test.error, test.message),
|
.then(() => is(null, test.error, test.message), e => {
|
||||||
e => is(e.name, test.error, test.message + ": " + e.message)), p);
|
is(e.name, test.error, test.message + ": " + e.message);
|
||||||
|
if (test.constraint) {
|
||||||
|
is(e.constraint, test.constraint,
|
||||||
|
test.message + " w/correct constraint.");
|
||||||
|
}
|
||||||
|
}), p);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user