mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1712898 add a basic prompt for selectAudioOutput() r=jib,flod,pbz
This contains the code paths that are similar to the prompt for getUserMedia(). Permanent permissions and grace period are not included here and their model may differ from that of getUserMedia(). See https://bugzilla.mozilla.org/show_bug.cgi?id=1712892 Differential Revision: https://phabricator.services.mozilla.com/D115994
This commit is contained in:
parent
b52cf6bbe2
commit
78defe09b9
@ -266,22 +266,22 @@ function handleGUMRequest(aSubject, aTopic, aData) {
|
||||
GlobalMuteListener.init();
|
||||
|
||||
let constraints = aSubject.getConstraints();
|
||||
let secure = aSubject.isSecure;
|
||||
let isHandlingUserInput = aSubject.isHandlingUserInput;
|
||||
let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID);
|
||||
|
||||
prompt(
|
||||
aSubject.type,
|
||||
contentWindow,
|
||||
aSubject.windowID,
|
||||
aSubject.callID,
|
||||
constraints,
|
||||
aSubject.devices,
|
||||
secure,
|
||||
isHandlingUserInput
|
||||
aSubject.isSecure,
|
||||
aSubject.isHandlingUserInput
|
||||
);
|
||||
}
|
||||
|
||||
function prompt(
|
||||
aRequestType,
|
||||
aContentWindow,
|
||||
aWindowID,
|
||||
aCallID,
|
||||
@ -292,6 +292,7 @@ function prompt(
|
||||
) {
|
||||
let audioInputDevices = [];
|
||||
let videoInputDevices = [];
|
||||
let audioOutputDevices = [];
|
||||
let devices = [];
|
||||
|
||||
// MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
|
||||
@ -303,18 +304,19 @@ function prompt(
|
||||
audio && typeof audio != "boolean" && audio.mediaSource != "microphone";
|
||||
for (let device of aDevices) {
|
||||
device = device.QueryInterface(Ci.nsIMediaDevice);
|
||||
let deviceObject = {
|
||||
name: device.rawName, // unfiltered device name to show to the user
|
||||
deviceIndex: devices.length,
|
||||
id: device.rawId,
|
||||
mediaSource: device.mediaSource,
|
||||
};
|
||||
switch (device.type) {
|
||||
case "audioinput":
|
||||
// Check that if we got a microphone, we have not requested an audio
|
||||
// capture, and if we have requested an audio capture, we are not
|
||||
// getting a microphone instead.
|
||||
if (audio && (device.mediaSource == "microphone") != sharingAudio) {
|
||||
audioInputDevices.push({
|
||||
name: device.rawName, // unfiltered device name to show to the user
|
||||
deviceIndex: devices.length,
|
||||
id: device.rawId,
|
||||
mediaSource: device.mediaSource,
|
||||
});
|
||||
audioInputDevices.push(deviceObject);
|
||||
devices.push(device);
|
||||
}
|
||||
break;
|
||||
@ -322,12 +324,6 @@ function prompt(
|
||||
// Verify that if we got a camera, we haven't requested a screen share,
|
||||
// or that if we requested a screen share we aren't getting a camera.
|
||||
if (video && (device.mediaSource == "camera") != sharingScreen) {
|
||||
let deviceObject = {
|
||||
name: device.rawName, // unfiltered device name to show to the user
|
||||
deviceIndex: devices.length,
|
||||
id: device.rawId,
|
||||
mediaSource: device.mediaSource,
|
||||
};
|
||||
if (device.scary) {
|
||||
deviceObject.scary = true;
|
||||
}
|
||||
@ -335,6 +331,12 @@ function prompt(
|
||||
devices.push(device);
|
||||
}
|
||||
break;
|
||||
case "audiooutput":
|
||||
if (aRequestType == "selectaudiooutput") {
|
||||
audioOutputDevices.push(deviceObject);
|
||||
devices.push(device);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -345,6 +347,9 @@ function prompt(
|
||||
if (audioInputDevices.length) {
|
||||
requestTypes.push(sharingAudio ? "AudioCapture" : "Microphone");
|
||||
}
|
||||
if (audioOutputDevices.length) {
|
||||
requestTypes.push("Speaker");
|
||||
}
|
||||
|
||||
if (!requestTypes.length) {
|
||||
// Device enumeration is done ahead of handleGUMRequest, so we're not
|
||||
@ -401,6 +406,7 @@ function prompt(
|
||||
sharingAudio,
|
||||
audioInputDevices,
|
||||
videoInputDevices,
|
||||
audioOutputDevices,
|
||||
};
|
||||
|
||||
let actor = getActorForWindow(aContentWindow);
|
||||
|
@ -389,7 +389,12 @@ class WebRTCParent extends JSWindowActorParent {
|
||||
return false;
|
||||
}
|
||||
|
||||
let { audioInputDevices, videoInputDevices, sharingScreen } = aRequest;
|
||||
let {
|
||||
audioInputDevices,
|
||||
videoInputDevices,
|
||||
audioOutputDevices,
|
||||
sharingScreen,
|
||||
} = aRequest;
|
||||
|
||||
let micAllowed =
|
||||
SitePermissions.getForPrincipal(aPrincipal, "microphone").state ==
|
||||
@ -471,7 +476,8 @@ class WebRTCParent extends JSWindowActorParent {
|
||||
}
|
||||
if (
|
||||
(!audioInputDevices.length || micAllowed || activeMic) &&
|
||||
(!videoInputDevices.length || camAllowed || activeCamera)
|
||||
(!videoInputDevices.length || camAllowed || activeCamera) &&
|
||||
!audioOutputDevices.length
|
||||
) {
|
||||
let allowedDevices = [];
|
||||
if (videoInputDevices.length) {
|
||||
@ -522,6 +528,7 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
let {
|
||||
audioInputDevices,
|
||||
videoInputDevices,
|
||||
audioOutputDevices,
|
||||
sharingScreen,
|
||||
sharingAudio,
|
||||
requestTypes,
|
||||
@ -566,19 +573,16 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
|
||||
// If the user has already denied access once in this tab,
|
||||
// deny again without even showing the notification icon.
|
||||
if (
|
||||
(audioInputDevices.length &&
|
||||
SitePermissions.getForPrincipal(principal, "microphone", aBrowser)
|
||||
.state == SitePermissions.BLOCK) ||
|
||||
(videoInputDevices.length &&
|
||||
SitePermissions.getForPrincipal(
|
||||
principal,
|
||||
sharingScreen ? "screen" : "camera",
|
||||
aBrowser
|
||||
).state == SitePermissions.BLOCK)
|
||||
) {
|
||||
aActor.denyRequest(aRequest);
|
||||
return;
|
||||
for (const type of requestTypes) {
|
||||
const permissionID =
|
||||
type == "AudioCapture" ? "microphone" : type.toLowerCase();
|
||||
if (
|
||||
SitePermissions.getForPrincipal(principal, permissionID, aBrowser)
|
||||
.state == SitePermissions.BLOCK
|
||||
) {
|
||||
aActor.denyRequest(aRequest);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the browser to refresh the identity block display in case there
|
||||
@ -606,6 +610,7 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
"getUserMedia.shareMicrophoneUnsafeDelegations2.message",
|
||||
"getUserMedia.shareScreenUnsafeDelegation2.message",
|
||||
"getUserMedia.shareAudioCaptureUnsafeDelegation2.message",
|
||||
"selectAudioOutput.shareSpeakerUnsafeDelegation.message",
|
||||
// Combinations of the above request types last.
|
||||
"getUserMedia.shareCameraAndMicrophoneUnsafeDelegation2.message",
|
||||
"getUserMedia.shareCameraAndAudioCaptureUnsafeDelegation2.message",
|
||||
@ -619,6 +624,7 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
"getUserMedia.shareMicrophone3.message",
|
||||
"getUserMedia.shareScreen4.message",
|
||||
"getUserMedia.shareAudioCapture3.message",
|
||||
"selectAudioOutput.shareSpeaker.message",
|
||||
// Combinations of the above request types last.
|
||||
"getUserMedia.shareCameraAndMicrophone3.message",
|
||||
"getUserMedia.shareCameraAndAudioCapture3.message",
|
||||
@ -1102,12 +1108,18 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
!sharingScreen || !videoInputDevices.length;
|
||||
doc.getElementById("webRTC-selectMicrophone").hidden =
|
||||
!audioInputDevices.length || sharingAudio;
|
||||
doc.getElementById(
|
||||
"webRTC-selectSpeaker"
|
||||
).hidden = !audioOutputDevices.length;
|
||||
|
||||
let camMenupopup = doc.getElementById("webRTC-selectCamera-menupopup");
|
||||
let windowMenupopup = doc.getElementById("webRTC-selectWindow-menupopup");
|
||||
let micMenupopup = doc.getElementById(
|
||||
"webRTC-selectMicrophone-menupopup"
|
||||
);
|
||||
let speakerMenupopup = doc.getElementById(
|
||||
"webRTC-selectSpeaker-menupopup"
|
||||
);
|
||||
let describedByIDs = ["webRTC-shareDevices-notification-description"];
|
||||
let describedBySuffix = "icon";
|
||||
|
||||
@ -1133,6 +1145,13 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
let labelID = "webRTC-selectSpeaker-single-device-label";
|
||||
listDevices(speakerMenupopup, audioOutputDevices, labelID);
|
||||
if (audioInputDevices.length == 1) {
|
||||
describedByIDs.push("webRTC-selectSpeaker-icon");
|
||||
describedByIDs.push(labelID);
|
||||
}
|
||||
|
||||
// PopupNotifications knows to clear the aria-describedby attribute
|
||||
// when hiding, so we don't have to worry about cleaning it up ourselves.
|
||||
chromeDoc.defaultView.PopupNotifications.panel.setAttribute(
|
||||
@ -1207,6 +1226,15 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
allowedDevices.push(0);
|
||||
}
|
||||
}
|
||||
if (audioOutputDevices.length) {
|
||||
let audioDeviceIndex = doc.getElementById(
|
||||
"webRTC-selectSpeaker-menulist"
|
||||
).value;
|
||||
let allowSpeaker = audioDeviceIndex != "-1";
|
||||
if (allowSpeaker) {
|
||||
allowedDevices.push(audioDeviceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
if (!allowedDevices.length) {
|
||||
aActor.denyRequest(aRequest);
|
||||
@ -1257,6 +1285,12 @@ function prompt(aActor, aBrowser, aRequest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// "Always allow this speaker" not yet supported for
|
||||
// selectAudioOutput(). Bug 1712892
|
||||
if (audioOutputDevices.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,16 @@
|
||||
<label id="webRTC-selectMicrophone-single-device-label" class="webRTC-selectDevice-label"></label>
|
||||
</html:div>
|
||||
</popupnotificationcontent>
|
||||
|
||||
<popupnotificationcontent id="webRTC-selectSpeaker" orient="vertical">
|
||||
<html:div class="webRTC-selectDevice-selector-container">
|
||||
<xul:image id="webRTC-selectSpeaker-icon" data-l10n-id="popup-select-speaker-icon" class="webRTC-selectDevice-icon"/>
|
||||
<menulist id="webRTC-selectSpeaker-menulist" aria-labelledby="webRTC-selectSpeaker-icon" size="large">
|
||||
<menupopup id="webRTC-selectSpeaker-menupopup"/>
|
||||
</menulist>
|
||||
<label id="webRTC-selectSpeaker-single-device-label" class="webRTC-selectDevice-label"></label>
|
||||
</html:div>
|
||||
</popupnotificationcontent>
|
||||
</popupnotification>
|
||||
|
||||
<popupnotification id="servicesInstall-notification" hidden="true">
|
||||
|
@ -846,13 +846,13 @@ async function reloadAndAssertClosedStreams() {
|
||||
*/
|
||||
function checkDeviceSelectors(aExpectedTypes, aWindow = window) {
|
||||
for (const type of aExpectedTypes) {
|
||||
if (!["microphone", "camera", "screen"].includes(type)) {
|
||||
if (!["microphone", "camera", "screen", "speaker"].includes(type)) {
|
||||
throw new Error(`Bad device type name ${type}`);
|
||||
}
|
||||
}
|
||||
let document = aWindow.document;
|
||||
|
||||
for (let type of ["Microphone", "Camera"]) {
|
||||
for (let type of ["Microphone", "Camera", "Speaker"]) {
|
||||
let selector = document.getElementById(`webRTC-select${type}`);
|
||||
if (!aExpectedTypes.includes(type.toLowerCase())) {
|
||||
ok(selector.hidden, `${type} selector hidden`);
|
||||
|
@ -388,6 +388,8 @@ popup-select-microphone-device =
|
||||
.accesskey = M
|
||||
popup-select-microphone-icon =
|
||||
.tooltiptext = Microphone
|
||||
popup-select-speaker-icon =
|
||||
.tooltiptext = Speakers
|
||||
popup-all-windows-shared = All visible windows on your screen will be shared.
|
||||
|
||||
popup-screen-sharing-block =
|
||||
|
@ -709,6 +709,11 @@ getUserMedia.shareCameraAndAudioCapture3.message = Allow %S to use your camera a
|
||||
getUserMedia.shareScreenAndMicrophone4.message = Allow %S to use your microphone and see your screen?
|
||||
getUserMedia.shareScreenAndAudioCapture4.message = Allow %S to listen to this tab’s audio and see your screen?
|
||||
getUserMedia.shareAudioCapture3.message = Allow %S to listen to this tab’s audio?
|
||||
# LOCALIZATION NOTE (selectAudioOutput.shareSpeaker.message):
|
||||
# "Speakers" is used in a general sense that might include headphones or
|
||||
# another audio output connection.
|
||||
# %S is the website origin (e.g. www.mozilla.org)
|
||||
selectAudioOutput.shareSpeaker.message = Allow %S to use other speakers?
|
||||
|
||||
# LOCALIZATION NOTE (getUserMedia.shareCameraUnsafeDelegation2.message,
|
||||
# getUserMedia.shareMicrophoneUnsafeDelegation2.message,
|
||||
@ -726,6 +731,12 @@ getUserMedia.shareCameraAndMicrophoneUnsafeDelegation2.message = Allow %1$S to g
|
||||
getUserMedia.shareCameraAndAudioCaptureUnsafeDelegation2.message = Allow %1$S to give %2$S access to your camera and listen to this tab’s audio?
|
||||
getUserMedia.shareScreenAndMicrophoneUnsafeDelegation2.message = Allow %1$S to give %2$S access to your microphone and see your screen?
|
||||
getUserMedia.shareScreenAndAudioCaptureUnsafeDelegation2.message = Allow %1$S to give %2$S permission to listen to this tab’s audio and see your screen?
|
||||
# LOCALIZATION NOTE ():
|
||||
# "Speakers" is used in a general sense that might include headphones or
|
||||
# another audio output connection.
|
||||
# %1$S is the first party origin.
|
||||
# %2$S is the third party origin.
|
||||
selectAudioOutput.shareSpeakerUnsafeDelegation.message = Allow %1$S to give %2$S access to other speakers?
|
||||
|
||||
# LOCALIZATION NOTE (getUserMedia.shareScreenWarning.message): NB: inserted via innerHTML, so please don't use <, > or & in this string.
|
||||
getUserMedia.shareScreenWarning2.message = Only share screens with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data.
|
||||
|
@ -799,6 +799,10 @@ popupnotificationcontent {
|
||||
list-style-image: url("chrome://browser/skin/notification-icons/microphone.svg");
|
||||
}
|
||||
|
||||
#webRTC-selectSpeaker-icon {
|
||||
list-style-image: url("chrome://global/skin/media/audio.svg");
|
||||
}
|
||||
|
||||
.popup-notification-body :is(description, label, checkbox, input) {
|
||||
margin-inline: 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user