Bug 1037405 - implement the doorhanger prompting the user to share a screen/window, r=Gijs.

--HG--
extra : rebase_source : 394a06b14a7561ea1f43d3c7a8ce52ad4a4fe447
This commit is contained in:
Florian Quèze 2014-07-07 09:08:00 +02:00
parent 4e970c9538
commit 7960aa9c7b
4 changed files with 112 additions and 34 deletions

View File

@ -18,6 +18,17 @@
<menupopup id="webRTC-selectCamera-menupopup"/>
</menulist>
</popupnotificationcontent>
<popupnotificationcontent id="webRTC-selectWindowOrScreen" orient="vertical">
<separator class="thin"/>
<label value="&getUserMedia.selectWindowOrScreen.label;"
accesskey="&getUserMedia.selectWindowOrScreen.accesskey;"
control="webRTC-selectWindow-menulist"/>
<menulist id="webRTC-selectWindow-menulist">
<menupopup id="webRTC-selectWindow-menupopup"/>
</menulist>
</popupnotificationcontent>
<popupnotificationcontent id="webRTC-selectMicrophone" orient="vertical">
<separator class="thin"/>
<label value="&getUserMedia.selectMicrophone.label;"

View File

@ -709,6 +709,8 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY getUserMedia.selectCamera.label "Camera to share:">
<!ENTITY getUserMedia.selectCamera.accesskey "C">
<!ENTITY getUserMedia.selectWindowOrScreen.label "Window or screen to share:">
<!ENTITY getUserMedia.selectWindowOrScreen.accesskey "W">
<!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
<!ENTITY getUserMedia.selectMicrophone.accesskey "M">

View File

@ -484,16 +484,23 @@ identity.loggedIn.description = Signed in as: %S
identity.loggedIn.signOut.label = Sign Out
identity.loggedIn.signOut.accessKey = O
# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message,
# getUserMedia.shareScreen.message, getUserMedia.shareCameraAndMicrophone.message,
# getUserMedia.shareScreenAndMicrophone.message):
# %S is the website origin (e.g. www.mozilla.org)
getUserMedia.shareCamera.message = Would you like to share your camera with %S?
getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
getUserMedia.shareScreen.message = Would you like to share your screen with %S?
getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
getUserMedia.shareScreenAndMicrophone.message = Would you like to share your microphone and screen with %S?
getUserMedia.noVideo.label = No Video
getUserMedia.noWindowOrScreen.label = No Window or Screen
getUserMedia.noAudio.label = No Audio
getUserMedia.shareEntireScreen.label = Entire screen
# LOCALIZATION NOTE (getUserMedia.shareSelectedDevices.label):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# The number of devices can be either one or two.
getUserMedia.shareCamera.message = Would you like to share your camera with %S?
getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
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

View File

@ -94,31 +94,36 @@ function denyRequest(aCallID, aError) {
Services.obs.notifyObservers(msg, "getUserMedia:response:deny", aCallID);
}
function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevices, aSecure) {
function prompt(aContentWindow, aCallID, aAudio, aVideo, aDevices, aSecure) {
let audioDevices = [];
let videoDevices = [];
// MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
let sharingScreen = aVideo && typeof(aVideo) != "boolean" &&
aVideo.mediaSource != "camera";
for (let device of aDevices) {
device = device.QueryInterface(Ci.nsIMediaDevice);
switch (device.type) {
case "audio":
if (aAudioRequested)
if (aAudio)
audioDevices.push(device);
break;
case "video":
if (aVideoRequested)
// 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 (aVideo && (device.mediaSource == "camera") != sharingScreen)
videoDevices.push(device);
break;
}
}
let requestType;
if (audioDevices.length && videoDevices.length)
requestType = "CameraAndMicrophone";
else if (audioDevices.length)
requestType = "Microphone";
else if (videoDevices.length)
requestType = "Camera";
else {
let requestTypes = [];
if (videoDevices.length)
requestTypes.push(sharingScreen ? "Screen" : "Camera");
if (audioDevices.length)
requestTypes.push("Microphone");
if (!requestTypes.length) {
denyRequest(aCallID, "NO_DEVICES_FOUND");
return;
}
@ -128,11 +133,11 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
let chromeDoc = browser.ownerDocument;
let chromeWin = chromeDoc.defaultView;
let stringBundle = chromeWin.gNavigatorBundle;
let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message",
[ uri.host ]);
let stringId = "getUserMedia.share" + requestTypes.join("And") + ".message";
let message = stringBundle.getFormattedString(stringId, [uri.host]);
let mainAction = {
label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1,
label: PluralForm.get(requestTypes.length,
stringBundle.getString("getUserMedia.shareSelectedDevices.label")),
accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"),
// The real callback will be set during the "showing" event. The
@ -148,8 +153,11 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
callback: function () {
denyRequest(aCallID);
}
},
{
}
];
if (!sharingScreen) { // Bug 1037438: implement 'never' for screen sharing.
secondaryActions.push({
label: stringBundle.getString("getUserMedia.never.label"),
accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
callback: function () {
@ -162,11 +170,13 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
if (videoDevices.length)
perms.add(uri, "camera", perms.DENY_ACTION);
}
}
];
});
}
if (aSecure) {
// Don't show the 'Always' action if the connection isn't secure.
if (aSecure && !sharingScreen) {
// Don't show the 'Always' action if the connection isn't secure, or for
// screen sharing (because we can't guess which window the user wants to
// share without prompting).
secondaryActions.unshift({
label: stringBundle.getString("getUserMedia.always.label"),
accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
@ -185,7 +195,9 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
if (aTopic == "shown") {
let PopupNotifications = chromeDoc.defaultView.PopupNotifications;
let popupId = requestType == "Microphone" ? "Microphone" : "Devices";
let popupId = "Devices";
if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
popupId = "Microphone";
PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-share" + popupId);
}
@ -206,6 +218,10 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
if (camPerm == perms.PROMPT_ACTION)
camPerm = perms.UNKNOWN_ACTION;
// Screen sharing shouldn't follow the camera permissions.
if (videoDevices.length && sharingScreen)
camPerm = perms.UNKNOWN_ACTION;
// We don't check that permissions are set to ALLOW_ACTION in this
// test; only that they are set. This is because if audio is allowed
// and video is denied persistently, we don't want to show the prompt,
@ -235,6 +251,40 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
}
}
function listScreenShareDevices(menupopup, devices) {
while (menupopup.lastChild)
menupopup.removeChild(menupopup.lastChild);
// "No Window or Screen" is the default because we can't pick a
// 'default' window to share.
addDeviceToList(menupopup,
stringBundle.getString("getUserMedia.noWindowOrScreen.label"),
"-1");
// Then add the 'Entire screen' item if mozGetUserMediaDevices returned it.
for (let i = 0; i < devices.length; ++i) {
if (devices[i].mediaSource == "screen") {
menupopup.appendChild(chromeDoc.createElement("menuseparator"));
addDeviceToList(menupopup,
stringBundle.getString("getUserMedia.shareEntireScreen.label"),
i);
break;
}
}
// Finally add all the window names.
let separatorNeeded = true;
for (let i = 0; i < devices.length; ++i) {
if (devices[i].mediaSource == "window") {
if (separatorNeeded) {
menupopup.appendChild(chromeDoc.createElement("menuseparator"));
separatorNeeded = false;
}
addDeviceToList(menupopup, devices[i].name, i);
}
}
}
function addDeviceToList(menupopup, deviceName, deviceIndex) {
let menuitem = chromeDoc.createElement("menuitem");
menuitem.setAttribute("value", deviceIndex);
@ -243,16 +293,22 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
menupopup.appendChild(menuitem);
}
chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length;
chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
chromeDoc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length;
let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup");
let windowMenupopup = chromeDoc.getElementById("webRTC-selectWindow-menupopup");
let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup");
listDevices(camMenupopup, videoDevices);
if (sharingScreen)
listScreenShareDevices(windowMenupopup, videoDevices);
else
listDevices(camMenupopup, videoDevices);
listDevices(micMenupopup, audioDevices);
if (requestType == "CameraAndMicrophone") {
if (requestTypes.length == 2) {
let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
if (!sharingScreen)
addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
}
@ -261,7 +317,8 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
.createInstance(Ci.nsISupportsArray);
let perms = Services.perms;
if (videoDevices.length) {
let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value;
let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
let videoDeviceIndex = chromeDoc.getElementById(listId).value;
let allowCamera = videoDeviceIndex != "-1";
if (allowCamera)
allowedDevices.AppendElement(videoDevices[videoDeviceIndex]);
@ -292,8 +349,9 @@ function prompt(aContentWindow, aCallID, aAudioRequested, aVideoRequested, aDevi
}
};
let anchorId = requestType == "Microphone" ? "webRTC-shareMicrophone-notification-icon"
: "webRTC-shareDevices-notification-icon";
let anchorId = "webRTC-shareDevices-notification-icon";
if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
anchorId = "webRTC-shareMicrophone-notification-icon";
chromeWin.PopupNotifications.show(browser, "webRTC-shareDevices", message,
anchorId, mainAction, secondaryActions, options);
}