Bug 799417 - Implement global indicator for sites having camera/microphone access. r=gavin

This commit is contained in:
Dão Gottwald 2012-12-28 10:25:59 +01:00
parent 4bb31ec532
commit 968c440b09
9 changed files with 147 additions and 16 deletions

View File

@ -0,0 +1,52 @@
# -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
let WebrtcIndicator = {
init: function () {
let temp = {};
Cu.import("resource:///modules/webrtcUI.jsm", temp);
this.UIModule = temp.webrtcUI;
this.updateButton();
},
get button() {
delete this.button;
return this.button = document.getElementById("webrtc-status-button");
},
updateButton: function () {
this.button.hidden = !this.UIModule.showGlobalIndicator;
},
fillPopup: function (aPopup) {
this._menuitemData = new WeakMap;
for (let streamData of this.UIModule.activeStreams) {
let menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", streamData.uri);
menuitem.setAttribute("tooltiptext", streamData.uri);
this._menuitemData.set(menuitem, streamData);
aPopup.appendChild(menuitem);
}
},
clearPopup: function (aPopup) {
while (aPopup.lastChild)
aPopup.removeChild(aPopup.lastChild);
},
menuCommand: function (aMenuitem) {
let streamData = this._menuitemData.get(aMenuitem);
if (!streamData)
return;
let tab = streamData.tab;
let browserWindow = tab.ownerDocument.defaultView;
browserWindow.gBrowser.selectedTab = tab;
browserWindow.focus();
}
}

View File

@ -155,6 +155,7 @@ let gInitialPages = [
#include browser-tabPreviews.js
#include browser-tabview.js
#include browser-thumbnails.js
#include browser-webrtcUI.js
#ifdef MOZ_SERVICES_SYNC
#include browser-syncui.js
@ -1254,6 +1255,7 @@ var gBrowserInit = {
gFormSubmitObserver.init();
SocialUI.init();
AddonManager.addAddonListener(AddonsMgrListener);
WebrtcIndicator.init();
gBrowser.addEventListener("pageshow", function(event) {
// Filter out events that are not about the document load we are interested in

View File

@ -528,7 +528,7 @@
toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
fullscreentoolbar="true" mode="icons" customizable="true"
iconsize="large"
defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,downloads-button,home-button,bookmarks-menu-button-container,window-controls"
defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,webrtc-status-button,downloads-button,home-button,bookmarks-menu-button-container,window-controls"
context="toolbar-context-menu">
<toolbaritem id="unified-back-forward-button" class="chromeclass-toolbar-additional"
@ -585,6 +585,8 @@
<image id="webapps-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="blocked-plugins-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
</box>
<!-- Use onclick instead of normal popup= syntax since the popup
code fires onmousedown, and hence eats our favicon drag events.
@ -660,6 +662,16 @@
<searchbar id="searchbar" flex="1"/>
</toolbaritem>
<toolbarbutton id="webrtc-status-button"
class="toolbarbutton-1 chromeclass-toolbar-additional"
type="menu"
hidden="true"
orient="horizontal">
<menupopup onpopupshowing="WebrtcIndicator.fillPopup(this);"
onpopuphiding="WebrtcIndicator.clearPopup(this);"
oncommand="WebrtcIndicator.menuCommand(event.target);"/>
</toolbarbutton>
<toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
persist="class" removable="true"
label="&homeButton.label;"

View File

@ -441,8 +441,7 @@ 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.shareMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
# LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message, getUserMedia.sharingCamera.message, getUserMedia.sharingMicrophone.message, getUserMedia.sharingCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
# LOCALIZATION NOTE (getUserMedia.shareSelectedDevices.label):
# Semi-colon list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
@ -454,3 +453,6 @@ getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected D
getUserMedia.shareSelectedDevices.accesskey = S
getUserMedia.denyRequest.label = Don't Share
getUserMedia.denyRequest.accesskey = D
getUserMedia.sharingCamera.message = You are currently sharing your camera with %S.
getUserMedia.sharingMicrophone.message = You are currently sharing your microphone with %S.
getUserMedia.sharingCameraAndMicrophone.message = You are currently sharing your camera and microphone with %S.

View File

@ -12,13 +12,45 @@ const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PluralForm.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
"@mozilla.org/mediaManagerService;1",
"nsIMediaManagerService");
this.webrtcUI = {
init: function () {
Services.obs.addObserver(handleRequest, "getUserMedia:request", false);
Services.obs.addObserver(updateGlobalIndicator, "recording-device-events", false);
},
uninit: function () {
Services.obs.removeObserver(handleRequest, "getUserMedia:request");
Services.obs.removeObserver(updateGlobalIndicator, "recording-device-events");
},
showGlobalIndicator: false,
get activeStreams() {
let contentWindowSupportsArray = MediaManagerService.activeMediaCaptureWindows;
let count = contentWindowSupportsArray.Count();
let activeStreams = [];
for (let i = 0; i < count; i++) {
let contentWindow = contentWindowSupportsArray.GetElementAt(i);
let browserWindow = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler.ownerDocument.defaultView;
let tab = browserWindow.gBrowser &&
browserWindow.gBrowser._getTabForContentWindow(contentWindow.top);
if (tab) {
activeStreams.push({
uri: contentWindow.location.href,
tab: tab
});
}
}
return activeStreams;
}
}
@ -65,11 +97,11 @@ function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) {
let requestType;
if (audioDevices.length && videoDevices.length)
requestType = "shareCameraAndMicrophone";
requestType = "CameraAndMicrophone";
else if (audioDevices.length)
requestType = "shareMicrophone";
requestType = "Microphone";
else if (videoDevices.length)
requestType = "shareCamera";
requestType = "Camera";
else
return;
@ -77,7 +109,7 @@ function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) {
let chromeDoc = aBrowser.ownerDocument;
let chromeWin = chromeDoc.defaultView;
let stringBundle = chromeWin.gNavigatorBundle;
let message = stringBundle.getFormattedString("getUserMedia." + requestType + ".message",
let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message",
[ host ]);
function listDevices(menupopup, devices) {
@ -101,7 +133,7 @@ function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) {
listDevices(chromeDoc.getElementById("webRTC-selectMicrophone-menupopup"), audioDevices);
let mainAction = {
label: PluralForm.get(requestType == "shareCameraAndMicrophone" ? 2 : 1,
label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1,
stringBundle.getString("getUserMedia.shareSelectedDevices.label")),
accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"),
callback: function () {
@ -116,6 +148,18 @@ function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) {
allowedDevices.AppendElement(audioDevices[audioDeviceIndex]);
}
Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID);
// Show browser-specific indicator for the active camera/mic access.
let message = stringBundle.getFormattedString("getUserMedia.sharing" + requestType + ".message",
[ host ]);
let mainAction = null;
let secondaryActions = null;
let options = {
dismissed: true
};
chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
"webRTC-sharingDevices-notification-icon", mainAction,
secondaryActions, options);
}
};
@ -127,10 +171,18 @@ function prompt(aBrowser, aCallID, aAudioRequested, aVideoRequested, aDevices) {
}
}];
let options = {
};
let options = null;
chromeWin.PopupNotifications.show(aBrowser, "webRTC-shareDevices", message,
"webRTC-notification-icon", mainAction,
"webRTC-shareDevices-notification-icon", mainAction,
secondaryActions, options);
}
function updateGlobalIndicator() {
webrtcUI.showGlobalIndicator =
MediaManagerService.activeMediaCaptureWindows.Count() > 0;
let e = Services.wm.getEnumerator("navigator:browser");
while (e.hasMoreElements())
e.getNext().WebrtcIndicator.updateButton();
}

View File

@ -1,3 +1,3 @@
%filter substitution
%define primaryToolbarButtons #back-button, #forward-button, #reload-button, #stop-button, #home-button, #print-button, #downloads-button, #downloads-indicator, #history-button, #bookmarks-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-in-button, #sync-button, #feed-button, #alltabs-button, #tabview-button
%define primaryToolbarButtons #back-button, #forward-button, #reload-button, #stop-button, #home-button, #print-button, #downloads-button, #downloads-indicator, #history-button, #bookmarks-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #cut-button, #copy-button, #paste-button, #fullscreen-button, #zoom-out-button, #zoom-in-button, #sync-button, #feed-button, #alltabs-button, #tabview-button, #webrtc-status-button

View File

@ -649,6 +649,7 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
-moz-image-region: rect(0px 24px 24px 0px);
}
#webrtc-status-button /* temporary placeholder (bug 824825) */,
#history-button {
-moz-image-region: rect(0px 48px 24px 24px);
}
@ -810,6 +811,7 @@ toolbar[iconsize="small"] #downloads-button {
-moz-image-region: rect(0px 16px 16px 0px);
}
toolbar[iconsize="small"] #webrtc-status-button /* temporary placeholder (bug 824825) */,
toolbar[iconsize="small"] #history-button {
-moz-image-region: rect(0px 32px 16px 16px);
}
@ -1200,6 +1202,7 @@ toolbar[iconsize="small"] #feed-button {
list-style-image: url(chrome://browser/skin/webapps-64.png);
}
.popup-notification-icon[popupid="webRTC-sharingDevices"],
.popup-notification-icon[popupid="webRTC-shareDevices"] {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}
@ -1288,7 +1291,8 @@ toolbar[iconsize="small"] #feed-button {
}
}
#webRTC-notification-icon {
#webRTC-sharingDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16.png);
}

View File

@ -860,6 +860,7 @@ toolbar[mode="icons"] #forward-button:-moz-lwtheme {
/* history sidebar button */
#webrtc-status-button /* temporary placeholder (bug 824825) */,
#history-button {
-moz-image-region: rect(0, 160px, 20px, 140px);
}
@ -3079,11 +3080,13 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
}
}
#webRTC-notification-icon {
#webRTC-sharingDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16.png);
}
@media (min-resolution: 2dppx) {
#webRTC-notification-icon {
#webRTC-sharingDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16@2x.png);
}
}
@ -3164,6 +3167,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
list-style-image: url(chrome://browser/skin/webapps-64.png);
}
.popup-notification-icon[popupid="webRTC-sharingDevices"],
.popup-notification-icon[popupid="webRTC-shareDevices"] {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}

View File

@ -1030,6 +1030,7 @@ toolbar[mode=full] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
/* history sidebar button */
#webrtc-status-button /* temporary placeholder (bug 824825) */,
#history-button {
-moz-image-region: rect(0, 126px, 18px, 108px);
}
@ -2331,6 +2332,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
list-style-image: url(chrome://browser/skin/webapps-64.png);
}
.popup-notification-icon[popupid="webRTC-sharingDevices"],
.popup-notification-icon[popupid="webRTC-shareDevices"] {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-64.png);
}
@ -2417,7 +2419,8 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
}
}
#webRTC-notification-icon {
#webRTC-sharingDevices-notification-icon,
#webRTC-shareDevices-notification-icon {
list-style-image: url(chrome://browser/skin/webRTC-shareDevice-16.png);
}