mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 804611 - Add a way to grant/deny getUserMedia permissions persistently, r=jesup,dolske, ui-r=Boriss.
This commit is contained in:
parent
fb5ab6a760
commit
b4f865560b
@ -57,6 +57,7 @@ support-files =
|
||||
file_bug970276_favicon2.ico
|
||||
file_dom_notifications.html
|
||||
file_fullscreen-window-open.html
|
||||
get_user_media.html
|
||||
head.js
|
||||
healthreport_testRemoteCommands.html
|
||||
moz.png
|
||||
@ -262,6 +263,7 @@ skip-if = true # browser_drag.js is disabled, as it needs to be updated for the
|
||||
[browser_findbarClose.js]
|
||||
[browser_fullscreen-window-open.js]
|
||||
[browser_gestureSupport.js]
|
||||
[browser_get_user_media.js]
|
||||
[browser_getshortcutoruri.js]
|
||||
[browser_hide_removing.js]
|
||||
[browser_homeDrop.js]
|
||||
|
742
browser/base/content/test/general/browser_get_user_media.js
Normal file
742
browser/base/content/test/general/browser_get_user_media.js
Normal file
@ -0,0 +1,742 @@
|
||||
const kObservedTopics = [
|
||||
"getUserMedia:response:allow",
|
||||
"getUserMedia:revoke",
|
||||
"getUserMedia:response:deny",
|
||||
"getUserMedia:request",
|
||||
"recording-device-events",
|
||||
"recording-window-ended"
|
||||
];
|
||||
|
||||
const PREF_PERMISSION_FAKE = "media.navigator.permission.fake";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
|
||||
"@mozilla.org/mediaManagerService;1",
|
||||
"nsIMediaManagerService");
|
||||
|
||||
var gObservedTopics = {};
|
||||
function observer(aSubject, aTopic, aData) {
|
||||
if (!(aTopic in gObservedTopics))
|
||||
gObservedTopics[aTopic] = 1;
|
||||
else
|
||||
++gObservedTopics[aTopic];
|
||||
}
|
||||
|
||||
function promiseNotification(aTopic, aAction) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
Services.obs.addObserver(function observer() {
|
||||
ok(true, "got " + aTopic + " notification");
|
||||
Services.obs.removeObserver(observer, aTopic);
|
||||
|
||||
if (kObservedTopics.indexOf(aTopic) != -1) {
|
||||
if (!(aTopic in gObservedTopics))
|
||||
gObservedTopics[aTopic] = -1;
|
||||
else
|
||||
--gObservedTopics[aTopic];
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
}, aTopic, false);
|
||||
|
||||
if (aAction)
|
||||
aAction();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function expectNotification(aTopic) {
|
||||
is(gObservedTopics[aTopic], 1, "expected notification " + aTopic);
|
||||
if (aTopic in gObservedTopics)
|
||||
--gObservedTopics[aTopic];
|
||||
}
|
||||
|
||||
function expectNoNotifications() {
|
||||
for (let topic in gObservedTopics) {
|
||||
if (gObservedTopics[topic])
|
||||
is(gObservedTopics[topic], 0, topic + " notification unexpected");
|
||||
}
|
||||
gObservedTopics = {}
|
||||
}
|
||||
|
||||
function promiseMessage(aMessage, aAction) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
content.addEventListener("message", function messageListener(event) {
|
||||
content.removeEventListener("message", messageListener);
|
||||
is(event.data, aMessage, "received " + aMessage);
|
||||
if (event.data == aMessage)
|
||||
deferred.resolve();
|
||||
else
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
if (aAction)
|
||||
aAction();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promisePopupNotification(aName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
waitForCondition(() => PopupNotifications.getNotification(aName),
|
||||
() => {
|
||||
ok(!!PopupNotifications.getNotification(aName),
|
||||
aName + " notification appeared");
|
||||
deferred.resolve();
|
||||
}, "timeout waiting for popup notification " + aName);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseNoPopupNotification(aName) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
waitForCondition(() => !PopupNotifications.getNotification(aName),
|
||||
() => {
|
||||
ok(!PopupNotifications.getNotification(aName),
|
||||
aName + " notification removed");
|
||||
deferred.resolve();
|
||||
}, "timeout waiting for popup notification " + aName + " to disappear");
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
const kActionAlways = 1;
|
||||
const kActionDeny = 2;
|
||||
const kActionNever = 3;
|
||||
|
||||
function activateSecondaryAction(aAction) {
|
||||
let notification = PopupNotifications.panel.firstChild;
|
||||
notification.button.focus();
|
||||
let popup = notification.menupopup;
|
||||
popup.addEventListener("popupshown", function () {
|
||||
popup.removeEventListener("popupshown", arguments.callee, false);
|
||||
|
||||
// Press 'down' as many time as needed to select the requested action.
|
||||
while (aAction--)
|
||||
EventUtils.synthesizeKey("VK_DOWN", {});
|
||||
|
||||
// Activate
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
}, false);
|
||||
|
||||
// One down event to open the popup
|
||||
EventUtils.synthesizeKey("VK_DOWN",
|
||||
{ altKey: !navigator.platform.contains("Mac") });
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gBrowser.removeCurrentTab();
|
||||
kObservedTopics.forEach(topic => {
|
||||
Services.obs.removeObserver(observer, topic);
|
||||
});
|
||||
Services.prefs.clearUserPref(PREF_PERMISSION_FAKE);
|
||||
});
|
||||
|
||||
function getMediaCaptureState() {
|
||||
let hasVideo = {};
|
||||
let hasAudio = {};
|
||||
MediaManagerService.mediaCaptureWindowState(content, hasVideo, hasAudio);
|
||||
if (hasVideo.value && hasAudio.value)
|
||||
return "CameraAndMicrophone";
|
||||
if (hasVideo.value)
|
||||
return "Camera";
|
||||
if (hasAudio.value)
|
||||
return "Microphone";
|
||||
return "none";
|
||||
}
|
||||
|
||||
function closeStream(aAlreadyClosed) {
|
||||
expectNoNotifications();
|
||||
|
||||
info("closing the stream");
|
||||
content.wrappedJSObject.closeStream();
|
||||
|
||||
if (!aAlreadyClosed)
|
||||
yield promiseNotification("recording-device-events");
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
if (!aAlreadyClosed)
|
||||
expectNotification("recording-window-ended");
|
||||
|
||||
let statusButton = document.getElementById("webrtc-status-button");
|
||||
ok(statusButton.hidden, "WebRTC status button hidden");
|
||||
}
|
||||
|
||||
function checkDeviceSelectors(aAudio, aVideo) {
|
||||
let micSelector = document.getElementById("webRTC-selectMicrophone");
|
||||
if (aAudio)
|
||||
ok(!micSelector.hidden, "microphone selector visible");
|
||||
else
|
||||
ok(micSelector.hidden, "microphone selector hidden");
|
||||
|
||||
let cameraSelector = document.getElementById("webRTC-selectCamera");
|
||||
if (aVideo)
|
||||
ok(!cameraSelector.hidden, "camera selector visible");
|
||||
else
|
||||
ok(cameraSelector.hidden, "camera selector hidden");
|
||||
}
|
||||
|
||||
function checkSharingUI() {
|
||||
yield promisePopupNotification("webRTC-sharingDevices");
|
||||
let statusButton = document.getElementById("webrtc-status-button");
|
||||
ok(!statusButton.hidden, "WebRTC status button visible");
|
||||
}
|
||||
|
||||
function checkNotSharing() {
|
||||
is(getMediaCaptureState(), "none", "expected nothing to be shared");
|
||||
|
||||
ok(!PopupNotifications.getNotification("webRTC-sharingDevices"),
|
||||
"no webRTC-sharingDevices popup notification");
|
||||
|
||||
let statusButton = document.getElementById("webrtc-status-button");
|
||||
ok(statusButton.hidden, "WebRTC status button hidden");
|
||||
}
|
||||
|
||||
let gTests = [
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video",
|
||||
run: function checkAudioVideo() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectNotification("getUserMedia:response:allow");
|
||||
expectNotification("recording-device-events");
|
||||
is(getMediaCaptureState(), "CameraAndMicrophone",
|
||||
"expected camera and microphone to be shared");
|
||||
|
||||
yield checkSharingUI();
|
||||
yield closeStream();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio only",
|
||||
run: function checkAudioOnly() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectNotification("getUserMedia:response:allow");
|
||||
expectNotification("recording-device-events");
|
||||
is(getMediaCaptureState(), "Microphone", "expected microphone to be shared");
|
||||
|
||||
yield checkSharingUI();
|
||||
yield closeStream();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia video only",
|
||||
run: function checkVideoOnly() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(false, true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(false, true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectNotification("getUserMedia:response:allow");
|
||||
expectNotification("recording-device-events");
|
||||
is(getMediaCaptureState(), "Camera", "expected camera to be shared");
|
||||
|
||||
yield checkSharingUI();
|
||||
yield closeStream();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video, user disables video",
|
||||
run: function checkDisableVideo() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
// disable the camera
|
||||
document.getElementById("webRTC-selectCamera-menulist").value = -1;
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
|
||||
// reset the menuitem to have no impact on the following tests.
|
||||
document.getElementById("webRTC-selectCamera-menulist").value = 0;
|
||||
|
||||
expectNotification("getUserMedia:response:allow");
|
||||
expectNotification("recording-device-events");
|
||||
is(getMediaCaptureState(), "Microphone",
|
||||
"expected microphone to be shared");
|
||||
|
||||
yield checkSharingUI();
|
||||
yield closeStream();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video, user disables audio",
|
||||
run: function checkDisableAudio() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
// disable the microphone
|
||||
document.getElementById("webRTC-selectMicrophone-menulist").value = -1;
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
|
||||
// reset the menuitem to have no impact on the following tests.
|
||||
document.getElementById("webRTC-selectMicrophone-menulist").value = 0;
|
||||
|
||||
expectNotification("getUserMedia:response:allow");
|
||||
expectNotification("recording-device-events");
|
||||
is(getMediaCaptureState(), "Camera",
|
||||
"expected microphone to be shared");
|
||||
|
||||
yield checkSharingUI();
|
||||
yield closeStream();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video, user disables both audio and video",
|
||||
run: function checkDisableAudioVideo() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
// disable the camera and microphone
|
||||
document.getElementById("webRTC-selectCamera-menulist").value = -1;
|
||||
document.getElementById("webRTC-selectMicrophone-menulist").value = -1;
|
||||
|
||||
yield promiseMessage("error: PERMISSION_DENIED", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
|
||||
// reset the menuitems to have no impact on the following tests.
|
||||
document.getElementById("webRTC-selectCamera-menulist").value = 0;
|
||||
document.getElementById("webRTC-selectMicrophone-menulist").value = 0;
|
||||
|
||||
expectNotification("getUserMedia:response:deny");
|
||||
expectNotification("recording-window-ended");
|
||||
checkNotSharing();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video, user clicks \"Don't Share\"",
|
||||
run: function checkDontShare() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
yield promiseMessage("error: PERMISSION_DENIED", () => {
|
||||
activateSecondaryAction(kActionDeny);
|
||||
});
|
||||
|
||||
expectNotification("getUserMedia:response:deny");
|
||||
expectNotification("recording-window-ended");
|
||||
checkNotSharing();
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia audio+video: stop sharing",
|
||||
run: function checkStopSharing() {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
info("requesting devices");
|
||||
content.wrappedJSObject.requestDevice(true, true);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
checkDeviceSelectors(true, true);
|
||||
|
||||
yield promiseMessage("ok", () => {
|
||||
PopupNotifications.panel.firstChild.button.click();
|
||||
});
|
||||
expectNotification("getUserMedia:response:allow");
|
||||
expectNotification("recording-device-events");
|
||||
is(getMediaCaptureState(), "CameraAndMicrophone",
|
||||
"expected camera and microphone to be shared");
|
||||
|
||||
yield checkSharingUI();
|
||||
|
||||
PopupNotifications.getNotification("webRTC-sharingDevices").reshow();
|
||||
activateSecondaryAction(kActionDeny);
|
||||
|
||||
yield promiseNotification("recording-device-events");
|
||||
expectNotification("getUserMedia:revoke");
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
|
||||
if (gObservedTopics["recording-device-events"] == 1) {
|
||||
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
|
||||
gObservedTopics["recording-device-events"] = 0;
|
||||
}
|
||||
|
||||
expectNoNotifications();
|
||||
checkNotSharing();
|
||||
|
||||
// the stream is already closed, but this will do some cleanup anyway
|
||||
yield closeStream(true);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia prompt: Always/Never Share",
|
||||
run: function checkRememberCheckbox() {
|
||||
function checkPerm(aRequestAudio, aRequestVideo, aAllowAudio, aAllowVideo,
|
||||
aExpectedAudioPerm, aExpectedVideoPerm, aNever) {
|
||||
yield promiseNotification("getUserMedia:request", () => {
|
||||
content.wrappedJSObject.requestDevice(aRequestAudio, aRequestVideo);
|
||||
});
|
||||
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
|
||||
let elt = id => document.getElementById(id);
|
||||
|
||||
let noAudio = aAllowAudio === undefined;
|
||||
is(elt("webRTC-selectMicrophone").hidden, noAudio,
|
||||
"microphone selector expected to be " + (noAudio ? "hidden" : "visible"));
|
||||
if (!noAudio)
|
||||
elt("webRTC-selectMicrophone-menulist").value = (aAllowAudio || aNever) ? 0 : -1;
|
||||
|
||||
let noVideo = aAllowVideo === undefined;
|
||||
is(elt("webRTC-selectCamera").hidden, noVideo,
|
||||
"camera selector expected to be " + (noVideo ? "hidden" : "visible"));
|
||||
if (!noVideo)
|
||||
elt("webRTC-selectCamera-menulist").value = (aAllowVideo || aNever) ? 0 : -1;
|
||||
|
||||
let expectedMessage =
|
||||
(aAllowVideo || aAllowAudio) ? "ok" : "error: PERMISSION_DENIED";
|
||||
yield promiseMessage(expectedMessage, () => {
|
||||
activateSecondaryAction(aNever ? kActionNever : kActionAlways);
|
||||
});
|
||||
let expected = [];
|
||||
if (expectedMessage == "ok") {
|
||||
expectNotification("getUserMedia:response:allow");
|
||||
expectNotification("recording-device-events");
|
||||
if (aAllowVideo)
|
||||
expected.push("Camera");
|
||||
if (aAllowAudio)
|
||||
expected.push("Microphone");
|
||||
expected = expected.join("And");
|
||||
}
|
||||
else {
|
||||
expectNotification("getUserMedia:response:deny");
|
||||
expectNotification("recording-window-ended");
|
||||
expected = "none";
|
||||
}
|
||||
is(getMediaCaptureState(), expected,
|
||||
"expected " + expected + " to be shared");
|
||||
|
||||
function checkDevicePermissions(aDevice, aExpected) {
|
||||
let Perms = Services.perms;
|
||||
let uri = content.document.documentURIObject;
|
||||
let devicePerms = Perms.testExactPermission(uri, aDevice);
|
||||
if (aExpected === undefined)
|
||||
is(devicePerms, Perms.UNKNOWN_ACTION, "no " + aDevice + " persistent permissions");
|
||||
else {
|
||||
is(devicePerms, aExpected ? Perms.ALLOW_ACTION : Perms.DENY_ACTION,
|
||||
aDevice + " persistently " + (aExpected ? "allowed" : "denied"));
|
||||
}
|
||||
Perms.remove(uri.host, aDevice);
|
||||
}
|
||||
checkDevicePermissions("microphone", aExpectedAudioPerm);
|
||||
checkDevicePermissions("camera", aExpectedVideoPerm);
|
||||
|
||||
if (expectedMessage == "ok")
|
||||
yield closeStream();
|
||||
}
|
||||
|
||||
// 3 cases where the user accepts the device prompt.
|
||||
info("audio+video, user grants, expect both perms set to allow");
|
||||
yield checkPerm(true, true, true, true, true, true);
|
||||
info("audio only, user grants, check audio perm set to allow, video perm not set");
|
||||
yield checkPerm(true, false, true, undefined, true, undefined);
|
||||
info("video only, user grants, check video perm set to allow, audio perm not set");
|
||||
yield checkPerm(false, true, undefined, true, undefined, true);
|
||||
|
||||
// 3 cases where the user rejects the device request.
|
||||
// First test these cases by setting the device to 'No Audio'/'No Video'
|
||||
info("audio+video, user denies, expect both perms set to deny");
|
||||
yield checkPerm(true, true, false, false, false, false);
|
||||
info("audio only, user denies, expect audio perm set to deny, video not set");
|
||||
yield checkPerm(true, false, false, undefined, false, undefined);
|
||||
info("video only, user denies, expect video perm set to deny, audio perm not set");
|
||||
yield checkPerm(false, true, undefined, false, undefined, false);
|
||||
// Now test these 3 cases again by using the 'Never Share' action.
|
||||
info("audio+video, user denies, expect both perms set to deny");
|
||||
yield checkPerm(true, true, false, false, false, false, true);
|
||||
info("audio only, user denies, expect audio perm set to deny, video not set");
|
||||
yield checkPerm(true, false, false, undefined, false, undefined, true);
|
||||
info("video only, user denies, expect video perm set to deny, audio perm not set");
|
||||
yield checkPerm(false, true, undefined, false, undefined, false, true);
|
||||
|
||||
// 2 cases where the user allows half of what's requested.
|
||||
info("audio+video, user denies video, grants audio, " +
|
||||
"expect video perm set to deny, audio perm set to allow.");
|
||||
yield checkPerm(true, true, true, false, true, false);
|
||||
info("audio+video, user denies audio, grants video, " +
|
||||
"expect video perm set to allow, audio perm set to deny.");
|
||||
yield checkPerm(true, true, false, true, false, true);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "getUserMedia without prompt: use persistent permissions",
|
||||
run: function checkUsePersistentPermissions() {
|
||||
function usePerm(aAllowAudio, aAllowVideo, aRequestAudio, aRequestVideo,
|
||||
aExpectStream) {
|
||||
let Perms = Services.perms;
|
||||
let uri = content.document.documentURIObject;
|
||||
if (aAllowAudio !== undefined) {
|
||||
Perms.add(uri, "microphone", aAllowAudio ? Perms.ALLOW_ACTION
|
||||
: Perms.DENY_ACTION);
|
||||
}
|
||||
if (aAllowVideo !== undefined) {
|
||||
Perms.add(uri, "camera", aAllowVideo ? Perms.ALLOW_ACTION
|
||||
: Perms.DENY_ACTION);
|
||||
}
|
||||
|
||||
let gum = function() {
|
||||
content.wrappedJSObject.requestDevice(aRequestAudio, aRequestVideo);
|
||||
};
|
||||
|
||||
if (aExpectStream === undefined) {
|
||||
// Check that we get a prompt.
|
||||
yield promiseNotification("getUserMedia:request", gum);
|
||||
yield promisePopupNotification("webRTC-shareDevices");
|
||||
|
||||
// Deny the request to cleanup...
|
||||
yield promiseMessage("error: PERMISSION_DENIED", () => {
|
||||
activateSecondaryAction(kActionDeny);
|
||||
});
|
||||
expectNotification("getUserMedia:response:deny");
|
||||
expectNotification("recording-window-ended");
|
||||
}
|
||||
else {
|
||||
let allow = (aAllowVideo && aRequestVideo) || (aAllowAudio && aRequestAudio);
|
||||
let expectedMessage = allow ? "ok" : "error: PERMISSION_DENIED";
|
||||
yield promiseMessage(expectedMessage, gum);
|
||||
|
||||
if (expectedMessage == "ok") {
|
||||
expectNotification("recording-device-events");
|
||||
|
||||
// Check what's actually shared.
|
||||
let expected = [];
|
||||
if (aAllowVideo && aRequestVideo)
|
||||
expected.push("Camera");
|
||||
if (aAllowAudio && aRequestAudio)
|
||||
expected.push("Microphone");
|
||||
expected = expected.join("And");
|
||||
is(getMediaCaptureState(), expected,
|
||||
"expected " + expected + " to be shared");
|
||||
|
||||
yield closeStream();
|
||||
}
|
||||
else {
|
||||
expectNotification("recording-window-ended");
|
||||
}
|
||||
}
|
||||
|
||||
Perms.remove(uri.host, "camera");
|
||||
Perms.remove(uri.host, "microphone");
|
||||
}
|
||||
|
||||
// Set both permissions identically
|
||||
info("allow audio+video, request audio+video, expect ok (audio+video)");
|
||||
yield usePerm(true, true, true, true, true);
|
||||
info("deny audio+video, request audio+video, expect denied");
|
||||
yield usePerm(false, false, true, true, false);
|
||||
|
||||
// Allow audio, deny video.
|
||||
info("allow audio, deny video, request audio+video, expect ok (audio)");
|
||||
yield usePerm(true, false, true, true, true);
|
||||
info("allow audio, deny video, request audio, expect ok (audio)");
|
||||
yield usePerm(true, false, true, false, true);
|
||||
info("allow audio, deny video, request video, expect denied");
|
||||
yield usePerm(true, false, false, true, false);
|
||||
|
||||
// Deny audio, allow video.
|
||||
info("deny audio, allow video, request audio+video, expect ok (video)");
|
||||
yield usePerm(false, true, true, true, true);
|
||||
info("deny audio, allow video, request audio, expect denied");
|
||||
yield usePerm(false, true, true, false, true);
|
||||
info("deny audio, allow video, request video, expect ok (video)");
|
||||
yield usePerm(false, true, false, true, false);
|
||||
|
||||
// Allow audio, video not set.
|
||||
info("allow audio, request audio+video, expect prompt");
|
||||
yield usePerm(true, undefined, true, true, undefined);
|
||||
info("allow audio, request audio, expect ok (audio)");
|
||||
yield usePerm(true, undefined, true, false, true);
|
||||
info("allow audio, request video, expect prompt");
|
||||
yield usePerm(true, undefined, false, true, undefined);
|
||||
|
||||
// Deny audio, video not set.
|
||||
info("deny audio, request audio+video, expect prompt");
|
||||
yield usePerm(false, undefined, true, true, undefined);
|
||||
info("deny audio, request audio, expect denied");
|
||||
yield usePerm(false, undefined, true, false, false);
|
||||
info("deny audio, request video, expect prompt");
|
||||
yield usePerm(false, undefined, false, true, undefined);
|
||||
|
||||
// Allow video, video not set.
|
||||
info("allow video, request audio+video, expect prompt");
|
||||
yield usePerm(undefined, true, true, true, undefined);
|
||||
info("allow video, request audio, expect prompt");
|
||||
yield usePerm(undefined, true, true, false, undefined);
|
||||
info("allow video, request video, expect ok (video)");
|
||||
yield usePerm(undefined, true, false, true, true);
|
||||
|
||||
// Deny video, video not set.
|
||||
info("deny video, request audio+video, expect prompt");
|
||||
yield usePerm(undefined, false, true, true, undefined);
|
||||
info("deny video, request audio, expect prompt");
|
||||
yield usePerm(undefined, false, true, false, undefined);
|
||||
info("deny video, request video, expect denied");
|
||||
yield usePerm(undefined, false, false, true, false);
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Stop Sharing removes persistent permissions",
|
||||
run: function checkStopSharingRemovesPersistentPermissions() {
|
||||
function stopAndCheckPerm(aRequestAudio, aRequestVideo) {
|
||||
let Perms = Services.perms;
|
||||
let uri = content.document.documentURIObject;
|
||||
|
||||
// Initially set both permissions to 'allow'.
|
||||
Perms.add(uri, "microphone", Perms.ALLOW_ACTION);
|
||||
Perms.add(uri, "camera", Perms.ALLOW_ACTION);
|
||||
|
||||
// Start sharing what's been requested.
|
||||
yield promiseMessage("ok", () => {
|
||||
content.wrappedJSObject.requestDevice(aRequestAudio, aRequestVideo);
|
||||
});
|
||||
expectNotification("recording-device-events");
|
||||
yield checkSharingUI();
|
||||
|
||||
// Stop sharing.
|
||||
PopupNotifications.getNotification("webRTC-sharingDevices").reshow();
|
||||
activateSecondaryAction(kActionDeny);
|
||||
|
||||
yield promiseNotification("recording-device-events");
|
||||
expectNotification("getUserMedia:revoke");
|
||||
|
||||
yield promiseNoPopupNotification("webRTC-sharingDevices");
|
||||
|
||||
if (gObservedTopics["recording-device-events"] == 1) {
|
||||
todo(false, "Got the 'recording-device-events' notification twice, likely because of bug 962719");
|
||||
gObservedTopics["recording-device-events"] = 0;
|
||||
}
|
||||
|
||||
// Check that permissions have been removed as expected.
|
||||
let audioPerm = Perms.testExactPermission(uri, "microphone");
|
||||
if (aRequestAudio)
|
||||
is(audioPerm, Perms.UNKNOWN_ACTION, "microphone permissions removed");
|
||||
else
|
||||
is(audioPerm, Perms.ALLOW_ACTION, "microphone permissions untouched");
|
||||
|
||||
let videoPerm = Perms.testExactPermission(uri, "camera");
|
||||
if (aRequestVideo)
|
||||
is(videoPerm, Perms.UNKNOWN_ACTION, "camera permissions removed");
|
||||
else
|
||||
is(videoPerm, Perms.ALLOW_ACTION, "camera permissions untouched");
|
||||
|
||||
// Cleanup.
|
||||
yield closeStream(true);
|
||||
|
||||
Perms.remove(uri.host, "camera");
|
||||
Perms.remove(uri.host, "microphone");
|
||||
}
|
||||
|
||||
info("request audio+video, stop sharing resets both");
|
||||
yield stopAndCheckPerm(true, true);
|
||||
info("request audio, stop sharing resets audio only");
|
||||
yield stopAndCheckPerm(true, false);
|
||||
info("request video, stop sharing resets video only");
|
||||
yield stopAndCheckPerm(false, true);
|
||||
}
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab;
|
||||
tab.linkedBrowser.addEventListener("load", function onload() {
|
||||
tab.linkedBrowser.removeEventListener("load", onload, true);
|
||||
|
||||
kObservedTopics.forEach(topic => {
|
||||
Services.obs.addObserver(observer, topic, false);
|
||||
});
|
||||
Services.prefs.setBoolPref(PREF_PERMISSION_FAKE, true);
|
||||
|
||||
Task.spawn(function () {
|
||||
for (let test of gTests) {
|
||||
info(test.desc);
|
||||
yield test.run();
|
||||
|
||||
// Cleanup before the next test
|
||||
expectNoNotifications();
|
||||
}
|
||||
}).then(finish, ex => {
|
||||
ok(false, "Unexpected Exception: " + ex);
|
||||
finish();
|
||||
});
|
||||
}, true);
|
||||
let rootDir = getRootDirectory(gTestPath)
|
||||
rootDir = rootDir.replace("chrome://mochitests/content/",
|
||||
"http://127.0.0.1:8888/");
|
||||
content.location = rootDir + "get_user_media.html";
|
||||
}
|
||||
|
||||
|
||||
function wait(time) {
|
||||
let deferred = Promise.defer();
|
||||
setTimeout(deferred.resolve, time);
|
||||
return deferred.promise;
|
||||
}
|
32
browser/base/content/test/general/get_user_media.html
Normal file
32
browser/base/content/test/general/get_user_media.html
Normal file
@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><meta charset="UTF-8"></head>
|
||||
<body>
|
||||
<div id="message"></div>
|
||||
<script>
|
||||
function message(m) {
|
||||
document.getElementById("message").innerHTML = m;
|
||||
window.parent.postMessage(m, "*");
|
||||
}
|
||||
|
||||
var gStream;
|
||||
|
||||
function requestDevice(aAudio, aVideo) {
|
||||
window.navigator.mozGetUserMedia({video: aVideo, audio: aAudio, fake: true},
|
||||
function(stream) {
|
||||
gStream = stream;
|
||||
message("ok");
|
||||
}, function(err) { message("error: " + err); });
|
||||
}
|
||||
message("pending");
|
||||
|
||||
function closeStream() {
|
||||
if (!gStream)
|
||||
return;
|
||||
gStream.stop();
|
||||
gStream = null;
|
||||
message("closed");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -38,7 +38,7 @@ let gVisitStmt = gPlacesDatabase.createAsyncStatement(
|
||||
* Permission types that should be tested with testExactPermission, as opposed
|
||||
* to testPermission. This is based on what consumers use to test these permissions.
|
||||
*/
|
||||
let TEST_EXACT_PERM_TYPES = ["geo"];
|
||||
let TEST_EXACT_PERM_TYPES = ["geo", "camera", "microphone"];
|
||||
|
||||
/**
|
||||
* Site object represents a single site, uniquely identified by a host.
|
||||
@ -330,8 +330,11 @@ let PermissionDefaults = {
|
||||
set fullscreen(aValue) {
|
||||
let value = (aValue != this.DENY);
|
||||
Services.prefs.setBoolPref("full-screen-api.enabled", value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get camera() this.UNKNOWN,
|
||||
get microphone() this.UNKNOWN
|
||||
};
|
||||
|
||||
/**
|
||||
* AboutPermissions manages the about:permissions page.
|
||||
@ -339,7 +342,7 @@ let PermissionDefaults = {
|
||||
let AboutPermissions = {
|
||||
/**
|
||||
* Number of sites to return from the places database.
|
||||
*/
|
||||
*/
|
||||
PLACES_SITES_LIMIT: 50,
|
||||
|
||||
/**
|
||||
@ -369,17 +372,18 @@ let AboutPermissions = {
|
||||
*
|
||||
* Potential future additions: "sts/use", "sts/subd"
|
||||
*/
|
||||
_supportedPermissions: ["password", "cookie", "geo", "indexedDB", "popup", "fullscreen"],
|
||||
_supportedPermissions: ["password", "cookie", "geo", "indexedDB", "popup",
|
||||
"fullscreen", "camera", "microphone"],
|
||||
|
||||
/**
|
||||
* Permissions that don't have a global "Allow" option.
|
||||
*/
|
||||
_noGlobalAllow: ["geo", "indexedDB", "fullscreen"],
|
||||
_noGlobalAllow: ["geo", "indexedDB", "fullscreen", "camera", "microphone"],
|
||||
|
||||
/**
|
||||
* Permissions that don't have a global "Deny" option.
|
||||
*/
|
||||
_noGlobalDeny: [],
|
||||
_noGlobalDeny: ["camera", "microphone"],
|
||||
|
||||
_stringBundle: Services.strings.
|
||||
createBundle("chrome://browser/locale/preferences/aboutPermissions.properties"),
|
||||
@ -407,7 +411,7 @@ let AboutPermissions = {
|
||||
Services.obs.addObserver(this, "passwordmgr-storage-changed", false);
|
||||
Services.obs.addObserver(this, "cookie-changed", false);
|
||||
Services.obs.addObserver(this, "browser:purge-domain-data", false);
|
||||
|
||||
|
||||
this._observersInitialized = true;
|
||||
Services.obs.notifyObservers(null, "browser-permissions-preinit", null);
|
||||
},
|
||||
@ -542,7 +546,7 @@ let AboutPermissions = {
|
||||
let uri = NetUtil.newURI(aLogin.hostname);
|
||||
this.addHost(uri.host);
|
||||
} catch (e) {
|
||||
// newURI will throw for add-ons logins stored in chrome:// URIs
|
||||
// newURI will throw for add-ons logins stored in chrome:// URIs
|
||||
}
|
||||
itemCnt++;
|
||||
}, this);
|
||||
@ -557,7 +561,7 @@ let AboutPermissions = {
|
||||
let uri = NetUtil.newURI(aHostname);
|
||||
this.addHost(uri.host);
|
||||
} catch (e) {
|
||||
// newURI will throw for add-ons logins stored in chrome:// URIs
|
||||
// newURI will throw for add-ons logins stored in chrome:// URIs
|
||||
}
|
||||
itemCnt++;
|
||||
}, this);
|
||||
@ -778,7 +782,7 @@ let AboutPermissions = {
|
||||
let visitLabel = PluralForm.get(aCount, visitForm)
|
||||
.replace("#1", aCount);
|
||||
document.getElementById("site-visit-count").value = visitLabel;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
updatePasswordsCount: function() {
|
||||
|
@ -113,6 +113,48 @@
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<!-- Camera -->
|
||||
<hbox id="camera-pref-item"
|
||||
class="pref-item" align="top">
|
||||
<image class="pref-icon" type="camera"/>
|
||||
<vbox>
|
||||
<label class="pref-title" value="&camera.label;"/>
|
||||
<hbox align="center">
|
||||
<menulist id="camera-menulist"
|
||||
class="pref-menulist"
|
||||
type="camera"
|
||||
oncommand="AboutPermissions.onPermissionCommand(event);">
|
||||
<menupopup>
|
||||
<menuitem id="camera-0" value="0" label="&permission.alwaysAsk;"/>
|
||||
<menuitem id="camera-1" value="1" label="&permission.allow;"/>
|
||||
<menuitem id="camera-2" value="2" label="&permission.block;"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<!-- Microphone -->
|
||||
<hbox id="microphone-pref-item"
|
||||
class="pref-item" align="top">
|
||||
<image class="pref-icon" type="microphone"/>
|
||||
<vbox>
|
||||
<label class="pref-title" value="µphone.label;"/>
|
||||
<hbox align="center">
|
||||
<menulist id="microphone-menulist"
|
||||
class="pref-menulist"
|
||||
type="microphone"
|
||||
oncommand="AboutPermissions.onPermissionCommand(event);">
|
||||
<menupopup>
|
||||
<menuitem id="microphone-0" value="0" label="&permission.alwaysAsk;"/>
|
||||
<menuitem id="microphone-1" value="1" label="&permission.allow;"/>
|
||||
<menuitem id="microphone-2" value="2" label="&permission.block;"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<!-- Cookies -->
|
||||
<hbox id="cookie-pref-item"
|
||||
class="pref-item" align="top">
|
||||
|
@ -27,6 +27,8 @@ const TEST_PERMS = {
|
||||
"indexedDB": PERM_UNKNOWN,
|
||||
"popup": PERM_DENY,
|
||||
"fullscreen" : PERM_UNKNOWN,
|
||||
"camera": PERM_UNKNOWN,
|
||||
"microphone": PERM_UNKNOWN
|
||||
};
|
||||
|
||||
const NO_GLOBAL_ALLOW = [
|
||||
@ -36,7 +38,7 @@ const NO_GLOBAL_ALLOW = [
|
||||
];
|
||||
|
||||
// number of managed permissions in the interface
|
||||
const TEST_PERMS_COUNT = 6;
|
||||
const TEST_PERMS_COUNT = 8;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
@ -164,7 +166,7 @@ var tests = [
|
||||
function test_all_sites_permission() {
|
||||
// apply the old default of allowing all cookies
|
||||
Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
|
||||
|
||||
|
||||
// there should be no user-set pref for cookie behavior
|
||||
is(Services.prefs.getIntPref("network.cookie.cookieBehavior"), PERM_UNKNOWN,
|
||||
"network.cookie.cookieBehavior is expected default");
|
||||
@ -189,12 +191,12 @@ var tests = [
|
||||
// make sure "Manage All Passwords..." button opens the correct dialog
|
||||
addWindowListener("chrome://passwordmgr/content/passwordManager.xul", runNextTest);
|
||||
gBrowser.contentDocument.getElementById("passwords-manage-all-button").doCommand();
|
||||
|
||||
|
||||
},
|
||||
|
||||
function test_manage_all_cookies() {
|
||||
// make sure "Manage All Cookies..." button opens the correct dialog
|
||||
addWindowListener("chrome://browser/content/preferences/cookies.xul", runNextTest);
|
||||
addWindowListener("chrome://browser/content/preferences/cookies.xul", runNextTest);
|
||||
gBrowser.contentDocument.getElementById("cookies-manage-all-button").doCommand();
|
||||
},
|
||||
|
||||
|
@ -497,8 +497,12 @@ getUserMedia.noVideo.label = No Video
|
||||
getUserMedia.noAudio.label = No Audio
|
||||
getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected Devices
|
||||
getUserMedia.shareSelectedDevices.accesskey = S
|
||||
getUserMedia.always.label = Always Share
|
||||
getUserMedia.always.accesskey = A
|
||||
getUserMedia.denyRequest.label = Don't Share
|
||||
getUserMedia.denyRequest.accesskey = D
|
||||
getUserMedia.never.label = Never Share
|
||||
getUserMedia.never.accesskey = N
|
||||
getUserMedia.sharingCamera.message2 = You are currently sharing your camera with this page.
|
||||
getUserMedia.sharingMicrophone.message2 = You are currently sharing your microphone with this page.
|
||||
getUserMedia.sharingCameraAndMicrophone.message2 = You are currently sharing your camera and microphone with this page.
|
||||
|
@ -42,3 +42,5 @@
|
||||
<!ENTITY popup.label "Open Pop-up Windows">
|
||||
|
||||
<!ENTITY fullscreen.label "Fullscreen">
|
||||
<!ENTITY camera.label "Use the Camera">
|
||||
<!ENTITY microphone.label "Use the Microphone">
|
||||
|
@ -10,6 +10,8 @@ alwaysAsk = Always Ask
|
||||
permission.cookie.label = Set Cookies
|
||||
permission.desktop-notification.label = Show Notifications
|
||||
permission.image.label = Load Images
|
||||
permission.camera.label = Use the Camera
|
||||
permission.microphone.label = Use the Microphone
|
||||
permission.install.label = Install Add-ons
|
||||
permission.popup.label = Open Pop-up Windows
|
||||
permission.geo.label = Access Your Location
|
||||
|
@ -186,6 +186,9 @@ let gPermissionObject = {
|
||||
|
||||
"desktop-notification": {},
|
||||
|
||||
"camera": {},
|
||||
"microphone": {},
|
||||
|
||||
"popup": {
|
||||
getDefault: function () {
|
||||
return Services.prefs.getBoolPref("dom.disable_open_during_load") ?
|
||||
|
@ -120,13 +120,13 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
|
||||
return;
|
||||
}
|
||||
|
||||
let host = aContentWindow.document.documentURIObject.host;
|
||||
let uri = aContentWindow.document.documentURIObject;
|
||||
let browser = getBrowserForWindow(aContentWindow);
|
||||
let chromeDoc = browser.ownerDocument;
|
||||
let chromeWin = chromeDoc.defaultView;
|
||||
let stringBundle = chromeWin.gNavigatorBundle;
|
||||
let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message",
|
||||
[ host ]);
|
||||
[ uri.host ]);
|
||||
|
||||
let mainAction = {
|
||||
label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1,
|
||||
@ -138,13 +138,34 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
|
||||
callback: function() {}
|
||||
};
|
||||
|
||||
let secondaryActions = [{
|
||||
label: stringBundle.getString("getUserMedia.denyRequest.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
|
||||
callback: function () {
|
||||
denyRequest(aCallID);
|
||||
let secondaryActions = [
|
||||
{
|
||||
label: stringBundle.getString("getUserMedia.always.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
|
||||
callback: function () {
|
||||
mainAction.callback(true);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: stringBundle.getString("getUserMedia.denyRequest.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
|
||||
callback: function () {
|
||||
denyRequest(aCallID);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: stringBundle.getString("getUserMedia.never.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
|
||||
callback: function () {
|
||||
denyRequest(aCallID);
|
||||
let perms = Services.perms;
|
||||
if (audioDevices.length)
|
||||
perms.add(uri, "microphone", perms.DENY_ACTION);
|
||||
if (videoDevices.length)
|
||||
perms.add(uri, "camera", perms.DENY_ACTION);
|
||||
}
|
||||
}
|
||||
}];
|
||||
];
|
||||
|
||||
let options = {
|
||||
eventCallback: function(aTopic, aNewBrowser) {
|
||||
@ -188,18 +209,29 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
|
||||
addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
|
||||
}
|
||||
|
||||
this.mainAction.callback = function() {
|
||||
this.mainAction.callback = function(aRemember) {
|
||||
let allowedDevices = Cc["@mozilla.org/supports-array;1"]
|
||||
.createInstance(Ci.nsISupportsArray);
|
||||
let perms = Services.perms;
|
||||
if (videoDevices.length) {
|
||||
let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value;
|
||||
if (videoDeviceIndex != "-1")
|
||||
let allowCamera = videoDeviceIndex != "-1";
|
||||
if (allowCamera)
|
||||
allowedDevices.AppendElement(videoDevices[videoDeviceIndex]);
|
||||
if (aRemember) {
|
||||
perms.add(uri, "camera",
|
||||
allowCamera ? perms.ALLOW_ACTION : perms.DENY_ACTION);
|
||||
}
|
||||
}
|
||||
if (audioDevices.length) {
|
||||
let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value;
|
||||
if (audioDeviceIndex != "-1")
|
||||
let allowMic = audioDeviceIndex != "-1";
|
||||
if (allowMic)
|
||||
allowedDevices.AppendElement(audioDevices[audioDeviceIndex]);
|
||||
if (aRemember) {
|
||||
perms.add(uri, "microphone",
|
||||
allowMic ? perms.ALLOW_ACTION : perms.DENY_ACTION);
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedDevices.Count() == 0) {
|
||||
@ -252,6 +284,7 @@ function showBrowserSpecificIndicator(aBrowser) {
|
||||
|
||||
let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
|
||||
|
||||
let uri = aBrowser.contentWindow.document.documentURIObject;
|
||||
let windowId = aBrowser.contentWindow
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
@ -266,6 +299,14 @@ function showBrowserSpecificIndicator(aBrowser) {
|
||||
label: stringBundle.getString("getUserMedia.stopSharing.label"),
|
||||
accessKey: stringBundle.getString("getUserMedia.stopSharing.accesskey"),
|
||||
callback: function () {
|
||||
let perms = Services.perms;
|
||||
if (hasVideo.value &&
|
||||
perms.testExactPermission(uri, "camera") == perms.ALLOW_ACTION)
|
||||
perms.remove(uri.host, "camera");
|
||||
if (hasAudio.value &&
|
||||
perms.testExactPermission(uri, "microphone") == perms.ALLOW_ACTION)
|
||||
perms.remove(uri.host, "microphone");
|
||||
|
||||
Services.obs.notifyObservers(null, "getUserMedia:revoke", windowId);
|
||||
}
|
||||
}];
|
||||
|
@ -97,6 +97,12 @@
|
||||
.pref-icon[type="fullscreen"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
.pref-icon[type="camera"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
.pref-icon[type="microphone"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
|
||||
.pref-title {
|
||||
font-size: 125%;
|
||||
|
@ -107,6 +107,12 @@
|
||||
.pref-icon[type="fullscreen"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
.pref-icon[type="camera"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
.pref-icon[type="microphone"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.pref-icon[type="geo"] {
|
||||
|
@ -100,6 +100,12 @@
|
||||
.pref-icon[type="fullscreen"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
.pref-icon[type="camera"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
.pref-icon[type="microphone"] {
|
||||
list-style-image: url(chrome://global/skin/icons/question-64.png);
|
||||
}
|
||||
|
||||
.pref-title {
|
||||
font-size: 125%;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsIScriptGlobalObject.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIPopupWindowManager.h"
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsIDocShell.h"
|
||||
@ -33,8 +34,6 @@
|
||||
#include "nsDOMFile.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
/* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
|
||||
#include "MediaEngineDefault.h"
|
||||
#if defined(MOZ_WEBRTC)
|
||||
@ -907,6 +906,13 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SetContraints(const MediaStreamConstraintsInternal& aConstraints)
|
||||
{
|
||||
mConstraints = aConstraints;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SetAudioDevice(MediaDevice* aAudioDevice)
|
||||
{
|
||||
@ -1075,7 +1081,11 @@ public:
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
|
||||
MediaEngine *backend = mManager->GetBackend(mWindowId);
|
||||
nsRefPtr<MediaEngine> backend;
|
||||
if (mConstraints.mFake)
|
||||
backend = new MediaEngineDefault();
|
||||
else
|
||||
backend = mManager->GetBackend(mWindowId);
|
||||
|
||||
ScopedDeletePtr<SourceSet> final (GetSources(backend, mConstraints.mVideom,
|
||||
&MediaEngine::EnumerateVideoDevices,
|
||||
@ -1415,14 +1425,59 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
|
||||
}
|
||||
#endif
|
||||
// XXX No full support for picture in Desktop yet (needs proper UI)
|
||||
if (aPrivileged || c.mFake) {
|
||||
if (aPrivileged ||
|
||||
(c.mFake && !Preferences::GetBool("media.navigator.permission.fake"))) {
|
||||
runnable->Arm();
|
||||
mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
// Check if this site has persistent permissions.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIPermissionManager> permManager =
|
||||
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
uint32_t audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
if (c.mAudio) {
|
||||
rv = permManager->TestExactPermissionFromPrincipal(
|
||||
aWindow->GetExtantDoc()->NodePrincipal(), "microphone", &audioPerm);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (audioPerm == nsIPermissionManager::PROMPT_ACTION) {
|
||||
audioPerm = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
if (c.mVideo) {
|
||||
rv = permManager->TestExactPermissionFromPrincipal(
|
||||
aWindow->GetExtantDoc()->NodePrincipal(), "camera", &videoPerm);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (videoPerm == nsIPermissionManager::PROMPT_ACTION) {
|
||||
videoPerm = nsIPermissionManager::UNKNOWN_ACTION;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!c.mAudio || audioPerm) && (!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;
|
||||
runnable->SetContraints(c);
|
||||
}
|
||||
if (c.mVideo && videoPerm == nsIPermissionManager::DENY_ACTION) {
|
||||
c.mVideo = false;
|
||||
runnable->SetContraints(c);
|
||||
}
|
||||
|
||||
runnable->Arm();
|
||||
if (!c.mAudio && !c.mVideo) {
|
||||
return runnable->Denied(NS_LITERAL_STRING("PERMISSION_DENIED"));
|
||||
}
|
||||
|
||||
return mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
// Ask for user permission, and dispatch runnable (or not) when a response
|
||||
// is received via an observer notification. Each call is paired with its
|
||||
// runnable by a GUID.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidgen =
|
||||
do_GetService("@mozilla.org/uuid-generator;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "nsIDOMNavigatorUserMedia.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/dom/MediaStreamTrackBinding.h"
|
||||
#include "prlog.h"
|
||||
@ -102,12 +103,16 @@ public:
|
||||
bool CapturingVideo()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mVideoSource && !mVideoSource->IsFake() && !mStopped;
|
||||
return mVideoSource && !mStopped &&
|
||||
(!mVideoSource->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
bool CapturingAudio()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
return mAudioSource && !mAudioSource->IsFake() && !mStopped;
|
||||
return mAudioSource && !mStopped &&
|
||||
(!mAudioSource->IsFake() ||
|
||||
Preferences::GetBool("media.navigator.permission.fake"));
|
||||
}
|
||||
|
||||
void SetStopped()
|
||||
|
Loading…
Reference in New Issue
Block a user