123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- /* 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/. */
- "use strict";
- this.EXPORTED_SYMBOLS = ["webrtcUI"];
- const Cu = Components.utils;
- const Cc = Components.classes;
- const Ci = Components.interfaces;
- Cu.import("resource://gre/modules/Services.jsm");
- Cu.import("resource://gre/modules/XPCOMUtils.jsm");
- XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
- "resource://gre/modules/PluralForm.jsm");
- XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
- "@mozilla.org/mediaManagerService;1",
- "nsIMediaManagerService");
- this.webrtcUI = {
- init: function () {
- Services.obs.addObserver(handleRequest, "getUserMedia:request", false);
- Services.obs.addObserver(updateIndicators, "recording-device-events", false);
- Services.obs.addObserver(removeBrowserSpecificIndicator, "recording-window-ended", false);
- },
- uninit: function () {
- Services.obs.removeObserver(handleRequest, "getUserMedia:request");
- Services.obs.removeObserver(updateIndicators, "recording-device-events");
- Services.obs.removeObserver(removeBrowserSpecificIndicator, "recording-window-ended");
- },
- 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 browser = getBrowserForWindow(contentWindow);
- let browserWindow = browser.ownerDocument.defaultView;
- let tab = browserWindow.gBrowser &&
- browserWindow.gBrowser._getTabForContentWindow(contentWindow.top);
- activeStreams.push({
- uri: contentWindow.location.href,
- tab: tab,
- browser: browser
- });
- }
- return activeStreams;
- }
- }
- function getBrowserForWindowId(aWindowID) {
- return getBrowserForWindow(Services.wm.getOuterWindowWithId(aWindowID));
- }
- function getBrowserForWindow(aContentWindow) {
- return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShell)
- .chromeEventHandler;
- }
- function handleRequest(aSubject, aTopic, aData) {
- let {windowID: windowID, callID: callID} = JSON.parse(aData);
- let params = aSubject.QueryInterface(Ci.nsIMediaStreamOptions);
- Services.wm.getMostRecentWindow(null).navigator.mozGetUserMediaDevices(
- function (devices) {
- prompt(windowID, callID, params.audio, params.video || params.picture, devices);
- },
- function (error) {
- // bug 827146 -- In the future, the UI should catch NO_DEVICES_FOUND
- // and allow the user to plug in a device, instead of immediately failing.
- denyRequest(callID, error);
- }
- );
- }
- function denyRequest(aCallID, aError) {
- let msg = null;
- if (aError) {
- msg = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
- msg.data = aError;
- }
- Services.obs.notifyObservers(msg, "getUserMedia:response:deny", aCallID);
- }
- function prompt(aWindowID, aCallID, aAudioRequested, aVideoRequested, aDevices) {
- let audioDevices = [];
- let videoDevices = [];
- for (let device of aDevices) {
- device = device.QueryInterface(Ci.nsIMediaDevice);
- switch (device.type) {
- case "audio":
- if (aAudioRequested)
- audioDevices.push(device);
- break;
- case "video":
- if (aVideoRequested)
- 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 {
- denyRequest(aCallID, "NO_DEVICES_FOUND");
- return;
- }
- let contentWindow = Services.wm.getOuterWindowWithId(aWindowID);
- let host = contentWindow.document.documentURIObject.host;
- let browser = getBrowserForWindow(contentWindow);
- let chromeDoc = browser.ownerDocument;
- let chromeWin = chromeDoc.defaultView;
- let stringBundle = chromeWin.gNavigatorBundle;
- let message = stringBundle.getFormattedString("getUserMedia.share" + requestType + ".message",
- [ host ]);
- let mainAction = {
- label: PluralForm.get(requestType == "CameraAndMicrophone" ? 2 : 1,
- stringBundle.getString("getUserMedia.shareSelectedDevices.label")),
- accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"),
- // The real callback will be set during the "showing" event. The
- // empty function here is so that PopupNotifications.show doesn't
- // reject the action.
- callback: function() {}
- };
- let secondaryActions = [{
- label: stringBundle.getString("getUserMedia.denyRequest.label"),
- accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
- callback: function () {
- denyRequest(aCallID);
- }
- }];
- let options = {
- eventCallback: function(aTopic, aNewBrowser) {
- if (aTopic == "swapping")
- return true;
- if (aTopic != "showing")
- return false;
- let chromeDoc = this.browser.ownerDocument;
- function listDevices(menupopup, devices) {
- while (menupopup.lastChild)
- menupopup.removeChild(menupopup.lastChild);
- let deviceIndex = 0;
- for (let device of devices) {
- addDeviceToList(menupopup, device.name, deviceIndex);
- deviceIndex++;
- }
- }
- function addDeviceToList(menupopup, deviceName, deviceIndex) {
- let menuitem = chromeDoc.createElement("menuitem");
- menuitem.setAttribute("value", deviceIndex);
- menuitem.setAttribute("label", deviceName);
- menuitem.setAttribute("tooltiptext", deviceName);
- menupopup.appendChild(menuitem);
- }
- chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length;
- chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length;
- let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup");
- let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup");
- listDevices(camMenupopup, videoDevices);
- listDevices(micMenupopup, audioDevices);
- if (requestType == "CameraAndMicrophone") {
- let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
- addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
- addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
- }
- this.mainAction.callback = function() {
- let allowedDevices = Cc["@mozilla.org/supports-array;1"]
- .createInstance(Ci.nsISupportsArray);
- if (videoDevices.length) {
- let videoDeviceIndex = chromeDoc.getElementById("webRTC-selectCamera-menulist").value;
- if (videoDeviceIndex != "-1")
- allowedDevices.AppendElement(videoDevices[videoDeviceIndex]);
- }
- if (audioDevices.length) {
- let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value;
- if (audioDeviceIndex != "-1")
- allowedDevices.AppendElement(audioDevices[audioDeviceIndex]);
- }
- if (allowedDevices.Count() == 0) {
- denyRequest(aCallID);
- return;
- }
- Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID);
- };
- return true;
- }
- };
- chromeWin.PopupNotifications.show(browser, "webRTC-shareDevices", message,
- "webRTC-shareDevices-notification-icon", mainAction,
- secondaryActions, options);
- }
- function updateIndicators() {
- webrtcUI.showGlobalIndicator =
- MediaManagerService.activeMediaCaptureWindows.Count() > 0;
- let e = Services.wm.getEnumerator("navigator:browser");
- while (e.hasMoreElements())
- e.getNext().WebrtcIndicator.updateButton();
- for (let {browser: browser} of webrtcUI.activeStreams)
- showBrowserSpecificIndicator(browser);
- }
- function showBrowserSpecificIndicator(aBrowser) {
- let hasVideo = {};
- let hasAudio = {};
- MediaManagerService.mediaCaptureWindowState(aBrowser.contentWindow,
- hasVideo, hasAudio);
- let captureState;
- if (hasVideo.value && hasAudio.value) {
- captureState = "CameraAndMicrophone";
- } else if (hasVideo.value) {
- captureState = "Camera";
- } else if (hasAudio.value) {
- captureState = "Microphone";
- } else {
- Cu.reportError("showBrowserSpecificIndicator: got neither video nor audio access");
- return;
- }
- let chromeWin = aBrowser.ownerDocument.defaultView;
- let stringBundle = chromeWin.gNavigatorBundle;
- let message = stringBundle.getString("getUserMedia.sharing" + captureState + ".message2");
- let windowId = aBrowser.contentWindow
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils)
- .currentInnerWindowID;
- let mainAction = {
- label: "Continue Sharing", //stringBundle.getString("getUserMedia.continueSharing.label"),
- accessKey: "C", //stringBundle.getString("getUserMedia.continueSharing.accesskey"),
- callback: function () {},
- dismiss: true
- };
- let secondaryActions = [{
- label: "Stop Sharing", //stringBundle.getString("getUserMedia.stopSharing.label"),
- accessKey: "S", //stringBundle.getString("getUserMedia.stopSharing.accesskey"),
- callback: function () {
- Services.obs.notifyObservers(null, "getUserMedia:revoke", windowId);
- }
- }];
- let options = {
- hideNotNow: true,
- dismissed: true,
- eventCallback: function(aTopic) aTopic == "swapping"
- };
- chromeWin.PopupNotifications.show(aBrowser, "webRTC-sharingDevices", message,
- "webRTC-sharingDevices-notification-icon", mainAction,
- secondaryActions, options);
- }
- function removeBrowserSpecificIndicator(aSubject, aTopic, aData) {
- let browser = getBrowserForWindowId(aData);
- let PopupNotifications = browser.ownerDocument.defaultView.PopupNotifications;
- let notification = PopupNotifications &&
- PopupNotifications.getNotification("webRTC-sharingDevices",
- browser);
- if (notification)
- PopupNotifications.remove(notification);
- }
|