summaryrefslogtreecommitdiffstats
path: root/application/basilisk/modules/webrtcUI.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'application/basilisk/modules/webrtcUI.jsm')
-rw-r--r--application/basilisk/modules/webrtcUI.jsm1087
1 files changed, 0 insertions, 1087 deletions
diff --git a/application/basilisk/modules/webrtcUI.jsm b/application/basilisk/modules/webrtcUI.jsm
deleted file mode 100644
index b43d2108f..000000000
--- a/application/basilisk/modules/webrtcUI.jsm
+++ /dev/null
@@ -1,1087 +0,0 @@
-/* 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:///modules/syncedtabs/EventEmitter.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
- "resource://gre/modules/PluralForm.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
- "resource://gre/modules/PrivateBrowsingUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task",
- "resource://gre/modules/Task.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "SitePermissions",
- "resource:///modules/SitePermissions.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
- return Services.strings.createBundle("chrome://branding/locale/brand.properties");
-});
-
-this.webrtcUI = {
- peerConnectionBlockers: new Set(),
- emitter: new EventEmitter(),
-
- init() {
- Services.obs.addObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished", false);
-
- let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
- .getService(Ci.nsIMessageBroadcaster);
- ppmm.addMessageListener("webrtc:UpdatingIndicators", this);
- ppmm.addMessageListener("webrtc:UpdateGlobalIndicators", this);
- ppmm.addMessageListener("child-process-shutdown", this);
-
- let mm = Cc["@mozilla.org/globalmessagemanager;1"]
- .getService(Ci.nsIMessageListenerManager);
- mm.addMessageListener("rtcpeer:Request", this);
- mm.addMessageListener("rtcpeer:CancelRequest", this);
- mm.addMessageListener("webrtc:Request", this);
- mm.addMessageListener("webrtc:StopRecording", this);
- mm.addMessageListener("webrtc:CancelRequest", this);
- mm.addMessageListener("webrtc:UpdateBrowserIndicators", this);
- },
-
- uninit() {
- Services.obs.removeObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished");
-
- let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
- .getService(Ci.nsIMessageBroadcaster);
- ppmm.removeMessageListener("webrtc:UpdatingIndicators", this);
- ppmm.removeMessageListener("webrtc:UpdateGlobalIndicators", this);
-
- let mm = Cc["@mozilla.org/globalmessagemanager;1"]
- .getService(Ci.nsIMessageListenerManager);
- mm.removeMessageListener("rtcpeer:Request", this);
- mm.removeMessageListener("rtcpeer:CancelRequest", this);
- mm.removeMessageListener("webrtc:Request", this);
- mm.removeMessageListener("webrtc:StopRecording", this);
- mm.removeMessageListener("webrtc:CancelRequest", this);
- mm.removeMessageListener("webrtc:UpdateBrowserIndicators", this);
-
- if (gIndicatorWindow) {
- gIndicatorWindow.close();
- gIndicatorWindow = null;
- }
- },
-
- processIndicators: new Map(),
- activePerms: new Map(),
-
- get showGlobalIndicator() {
- for (let [, indicators] of this.processIndicators) {
- if (indicators.showGlobalIndicator)
- return true;
- }
- return false;
- },
-
- get showCameraIndicator() {
- for (let [, indicators] of this.processIndicators) {
- if (indicators.showCameraIndicator)
- return true;
- }
- return false;
- },
-
- get showMicrophoneIndicator() {
- for (let [, indicators] of this.processIndicators) {
- if (indicators.showMicrophoneIndicator)
- return true;
- }
- return false;
- },
-
- get showScreenSharingIndicator() {
- let list = [""];
- for (let [, indicators] of this.processIndicators) {
- if (indicators.showScreenSharingIndicator)
- list.push(indicators.showScreenSharingIndicator);
- }
-
- let precedence =
- ["Screen", "Window", "Application", "Browser", ""];
-
- list.sort((a, b) => {
- return precedence.indexOf(a) -
- precedence.indexOf(b);
-});
-
- return list[0];
- },
-
- _streams: [],
- // The boolean parameters indicate which streams should be included in the result.
- getActiveStreams(aCamera, aMicrophone, aScreen) {
- return webrtcUI._streams.filter(aStream => {
- let state = aStream.state;
- return aCamera && state.camera ||
- aMicrophone && state.microphone ||
- aScreen && state.screen;
- }).map(aStream => {
- let state = aStream.state;
- let types = {camera: state.camera, microphone: state.microphone,
- screen: state.screen};
- let browser = aStream.browser;
- let browserWindow = browser.ownerGlobal;
- let tab = browserWindow.gBrowser &&
- browserWindow.gBrowser.getTabForBrowser(browser);
- return {uri: state.documentURI, tab, browser, types};
- });
- },
-
- swapBrowserForNotification(aOldBrowser, aNewBrowser) {
- for (let stream of this._streams) {
- if (stream.browser == aOldBrowser)
- stream.browser = aNewBrowser;
- }
- },
-
- forgetActivePermissionsFromBrowser(aBrowser) {
- webrtcUI.activePerms.delete(aBrowser.outerWindowID);
- },
-
- forgetStreamsFromBrowser(aBrowser) {
- this._streams = this._streams.filter(stream => stream.browser != aBrowser);
- webrtcUI.forgetActivePermissionsFromBrowser(aBrowser);
- },
-
- showSharingDoorhanger(aActiveStream) {
- let browserWindow = aActiveStream.browser.ownerGlobal;
- if (aActiveStream.tab) {
- browserWindow.gBrowser.selectedTab = aActiveStream.tab;
- } else {
- aActiveStream.browser.focus();
- }
- browserWindow.focus();
- let identityBox = browserWindow.document.getElementById("identity-box");
-#ifdef XP_MACOSX
- if (!Services.focus.activeWindow) {
- browserWindow.addEventListener("activate", function onActivate() {
- browserWindow.removeEventListener("activate", onActivate);
- Services.tm.mainThread.dispatch(function() {
- identityBox.click();
- }, Ci.nsIThread.DISPATCH_NORMAL);
- });
- Cc["@mozilla.org/widget/macdocksupport;1"].getService(Ci.nsIMacDockSupport)
- .activateApplication(true);
- return;
- }
-#endif
- identityBox.click();
- },
-
- updateWarningLabel(aMenuList) {
- let type = aMenuList.selectedItem.getAttribute("devicetype");
- let document = aMenuList.ownerDocument;
- document.getElementById("webRTC-all-windows-shared").hidden = type != "Screen";
- },
-
- // Add-ons can override stock permission behavior by doing:
- //
- // webrtcUI.addPeerConnectionBlocker(function(aParams) {
- // // new permission checking logic
- // }));
- //
- // The blocking function receives an object with origin, callID, and windowID
- // parameters. If it returns the string "deny" or a Promise that resolves
- // to "deny", the connection is immediately blocked. With any other return
- // value (though the string "allow" is suggested for consistency), control
- // is passed to other registered blockers. If no registered blockers block
- // the connection (or of course if there are no registered blockers), then
- // the connection is allowed.
- //
- // Add-ons may also use webrtcUI.on/off to listen to events without
- // blocking anything:
- // peer-request-allowed is emitted when a new peer connection is
- // established (and not blocked).
- // peer-request-blocked is emitted when a peer connection request is
- // blocked by some blocking connection handler.
- // peer-request-cancel is emitted when a peer-request connection request
- // is canceled. (This would typically be used in
- // conjunction with a blocking handler to cancel
- // a user prompt or other work done by the handler)
- addPeerConnectionBlocker(aCallback) {
- this.peerConnectionBlockers.add(aCallback);
- },
-
- removePeerConnectionBlocker(aCallback) {
- this.peerConnectionBlockers.delete(aCallback);
- },
-
- on(...args) {
- return this.emitter.on(...args);
- },
-
- off(...args) {
- return this.emitter.off(...args);
- },
-
- receiveMessage(aMessage) {
- switch (aMessage.name) {
-
- case "rtcpeer:Request": {
- let params = Object.freeze(Object.assign({
- origin: aMessage.target.contentPrincipal.origin
- }, aMessage.data));
-
- let blockers = Array.from(this.peerConnectionBlockers);
-
- Task.spawn(function*() {
- for (let blocker of blockers) {
- try {
- let result = yield blocker(params);
- if (result == "deny") {
- return false;
- }
- } catch (err) {
- Cu.reportError(`error in PeerConnection blocker: ${err.message}`);
- }
- }
- return true;
- }).then(decision => {
- let message;
- if (decision) {
- this.emitter.emit("peer-request-allowed", params);
- message = "rtcpeer:Allow";
- } else {
- this.emitter.emit("peer-request-blocked", params);
- message = "rtcpeer:Deny";
- }
-
- aMessage.target.messageManager.sendAsyncMessage(message, {
- callID: params.callID,
- windowID: params.windowID,
- });
- });
- break;
- }
- case "rtcpeer:CancelRequest": {
- let params = Object.freeze({
- origin: aMessage.target.contentPrincipal.origin,
- callID: aMessage.data
- });
- this.emitter.emit("peer-request-cancel", params);
- break;
- }
- case "webrtc:Request":
- prompt(aMessage.target, aMessage.data);
- break;
- case "webrtc:StopRecording":
- stopRecording(aMessage.target, aMessage.data);
- break;
- case "webrtc:CancelRequest":
- removePrompt(aMessage.target, aMessage.data);
- break;
- case "webrtc:UpdatingIndicators":
- webrtcUI._streams = [];
- break;
- case "webrtc:UpdateGlobalIndicators":
- updateIndicators(aMessage.data, aMessage.target);
- break;
- case "webrtc:UpdateBrowserIndicators":
- let id = aMessage.data.windowId;
- let index;
- for (index = 0; index < webrtcUI._streams.length; ++index) {
- if (webrtcUI._streams[index].state.windowId == id)
- break;
- }
- // If there's no documentURI, the update is actually a removal of the
- // stream, triggered by the recording-window-ended notification.
- if (!aMessage.data.documentURI && index < webrtcUI._streams.length)
- webrtcUI._streams.splice(index, 1);
- else
- webrtcUI._streams[index] = {browser: aMessage.target, state: aMessage.data};
- let tabbrowser = aMessage.target.ownerGlobal.gBrowser;
- if (tabbrowser)
- tabbrowser.setBrowserSharing(aMessage.target, aMessage.data);
- break;
- case "child-process-shutdown":
- webrtcUI.processIndicators.delete(aMessage.target);
- updateIndicators(null, null);
- break;
- }
- }
-};
-
-function getBrowserForWindow(aContentWindow) {
- return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShell)
- .chromeEventHandler;
-}
-
-function denyRequest(aBrowser, aRequest) {
- aBrowser.messageManager.sendAsyncMessage("webrtc:Deny",
- {callID: aRequest.callID,
- windowID: aRequest.windowID});
-}
-
-function getHost(uri, href) {
- let host;
- try {
- if (!uri) {
- uri = Services.io.newURI(href);
- }
- host = uri.host;
- } catch (ex) {}
- if (!host) {
- if (uri && uri.scheme.toLowerCase() == "about") {
- // For about URIs, just use the full spec, without any #hash parts.
- host = uri.specIgnoringRef;
- } else {
- // This is unfortunate, but we should display *something*...
- const kBundleURI = "chrome://browser/locale/browser.properties";
- let bundle = Services.strings.createBundle(kBundleURI);
- host = bundle.GetStringFromName("getUserMedia.sharingMenuUnknownHost");
- }
- }
- return host;
-}
-
-function stopRecording(aBrowser, aRequest) {
- let outerWindowID = aBrowser.outerWindowID;
-
- if (!webrtcUI.activePerms.has(outerWindowID)) {
- return;
- }
-
- if (!aRequest.rawID) {
- webrtcUI.activePerms.delete(outerWindowID);
- } else {
- let set = webrtcUI.activePerms.get(outerWindowID);
- set.delete(aRequest.windowID + aRequest.mediaSource + aRequest.rawID);
- }
-}
-
-function prompt(aBrowser, aRequest) {
- let { audioDevices, videoDevices, sharingScreen, sharingAudio,
- requestTypes } = aRequest;
-
- // If the user has already denied access once in this tab,
- // deny again without even showing the notification icon.
- if ((audioDevices.length && SitePermissions
- .get(null, "microphone", aBrowser).state == SitePermissions.BLOCK) ||
- (videoDevices.length && SitePermissions
- .get(null, sharingScreen ? "screen" : "camera", aBrowser).state == SitePermissions.BLOCK)) {
- denyRequest(aBrowser, aRequest);
- return;
- }
-
- // Tell the browser to refresh the identity block display in case there
- // are expired permission states.
- aBrowser.dispatchEvent(new aBrowser.ownerGlobal
- .CustomEvent("PermissionStateChange"));
-
- let uri = Services.io.newURI(aRequest.documentURI);
- let host = getHost(uri);
- let chromeDoc = aBrowser.ownerDocument;
- let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
-
- // Mind the order, because for simplicity we're iterating over the list using
- // "includes()". This allows the rotation of string identifiers. We list the
- // full identifiers here so they can be cross-referenced more easily.
- let joinedRequestTypes = requestTypes.join("And");
- let stringId = [
- // Individual request types first.
- "getUserMedia.shareCamera2.message",
- "getUserMedia.shareMicrophone2.message",
- "getUserMedia.shareScreen3.message",
- "getUserMedia.shareAudioCapture2.message",
- // Combinations of the above request types last.
- "getUserMedia.shareCameraAndMicrophone2.message",
- "getUserMedia.shareCameraAndAudioCapture2.message",
- "getUserMedia.shareScreenAndMicrophone3.message",
- "getUserMedia.shareScreenAndAudioCapture3.message",
- ].find(id => id.includes(joinedRequestTypes));
-
- let message = stringBundle.getFormattedString(stringId, [host]);
-
- let notification; // Used by action callbacks.
- let mainAction = {
- label: stringBundle.getString("getUserMedia.allow.label"),
- accessKey: stringBundle.getString("getUserMedia.allow.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() {}
- };
-
- let secondaryActions = [
- {
- label: stringBundle.getString("getUserMedia.dontAllow.label"),
- accessKey: stringBundle.getString("getUserMedia.dontAllow.accesskey"),
- callback(aState) {
- denyRequest(notification.browser, aRequest);
- let scope = SitePermissions.SCOPE_TEMPORARY;
- if (aState && aState.checkboxChecked) {
- scope = SitePermissions.SCOPE_PERSISTENT;
- }
- if (audioDevices.length)
- SitePermissions.set(uri, "microphone",
- SitePermissions.BLOCK, scope, notification.browser);
- if (videoDevices.length)
- SitePermissions.set(uri, sharingScreen ? "screen" : "camera",
- SitePermissions.BLOCK, scope, notification.browser);
- }
- }
- ];
-
- let productName = gBrandBundle.GetStringFromName("brandShortName");
-
- let options = {
- persistent: true,
- hideClose: !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton"),
- eventCallback(aTopic, aNewBrowser) {
- if (aTopic == "swapping")
- return true;
-
- let doc = this.browser.ownerDocument;
-
- // Clean-up video streams of screensharing previews.
- if ((aTopic == "dismissed" || aTopic == "removed") &&
- requestTypes.includes("Screen")) {
- let video = doc.getElementById("webRTC-previewVideo");
- video.deviceId = undefined;
- if (video.stream) {
- video.stream.getTracks().forEach(t => t.stop());
- video.stream = null;
- video.src = null;
- doc.getElementById("webRTC-preview").hidden = true;
- }
- let menupopup = doc.getElementById("webRTC-selectWindow-menupopup");
- if (menupopup._commandEventListener) {
- menupopup.removeEventListener("command", menupopup._commandEventListener);
- menupopup._commandEventListener = null;
- }
- }
-
- if (aTopic != "showing")
- return false;
-
- // BLOCK is handled immediately by MediaManager if it has been set
- // persistently in the permission manager. If it has been set on the tab,
- // it is handled synchronously before we add the notification.
- // Handling of ALLOW is delayed until the popupshowing event,
- // to avoid granting permissions automatically to background tabs.
- if (aRequest.secure) {
- let micAllowed =
- SitePermissions.get(uri, "microphone").state == SitePermissions.ALLOW;
- let camAllowed =
- SitePermissions.get(uri, "camera").state == SitePermissions.ALLOW;
-
- let perms = Services.perms;
- let mediaManagerPerm =
- perms.testExactPermission(uri, "MediaManagerVideo");
- if (mediaManagerPerm) {
- perms.remove(uri, "MediaManagerVideo");
- }
-
- // Screen sharing shouldn't follow the camera permissions.
- if (videoDevices.length && sharingScreen)
- camAllowed = false;
-
- let activeCamera;
- let activeMic;
-
- for (let device of videoDevices) {
- let set = webrtcUI.activePerms.get(aBrowser.outerWindowID);
- if (set && set.has(aRequest.windowID + device.mediaSource + device.id)) {
- activeCamera = device;
- break;
- }
- }
-
- for (let device of audioDevices) {
- let set = webrtcUI.activePerms.get(aBrowser.outerWindowID);
- if (set && set.has(aRequest.windowID + device.mediaSource + device.id)) {
- activeMic = device;
- break;
- }
- }
-
- if ((!audioDevices.length || micAllowed || activeMic) &&
- (!videoDevices.length || camAllowed || activeCamera)) {
- let allowedDevices = [];
- if (videoDevices.length) {
- allowedDevices.push((activeCamera || videoDevices[0]).deviceIndex);
- Services.perms.add(uri, "MediaManagerVideo",
- Services.perms.ALLOW_ACTION,
- Services.perms.EXPIRE_SESSION);
- }
- if (audioDevices.length) {
- allowedDevices.push((activeMic || audioDevices[0]).deviceIndex);
- }
-
- // Remember on which URIs we found persistent permissions so that we
- // can remove them if the user clicks 'Stop Sharing'. There's no
- // other way for the stop sharing code to know the hostnames of frames
- // using devices until bug 1066082 is fixed.
- let browser = this.browser;
- browser._devicePermissionURIs = browser._devicePermissionURIs || [];
- browser._devicePermissionURIs.push(uri);
-
- let mm = browser.messageManager;
- mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
- windowID: aRequest.windowID,
- devices: allowedDevices});
- this.remove();
- return true;
- }
- }
-
- function listDevices(menupopup, devices) {
- while (menupopup.lastChild)
- menupopup.removeChild(menupopup.lastChild);
- // Removing the child nodes of the menupopup doesn't clear the value
- // attribute of the menulist. This can have unfortunate side effects
- // when the list is rebuilt with a different content, so we remove
- // the value attribute explicitly.
- menupopup.parentNode.removeAttribute("value");
-
- for (let device of devices)
- addDeviceToList(menupopup, device.name, device.deviceIndex);
- }
-
- function listScreenShareDevices(menupopup, devices) {
- while (menupopup.lastChild)
- menupopup.removeChild(menupopup.lastChild);
-
- let type = devices[0].mediaSource;
- let typeName = type.charAt(0).toUpperCase() + type.substr(1);
-
- let label = doc.getElementById("webRTC-selectWindow-label");
- let gumStringId = "getUserMedia.select" + typeName;
- label.setAttribute("value",
- stringBundle.getString(gumStringId + ".label"));
- label.setAttribute("accesskey",
- stringBundle.getString(gumStringId + ".accesskey"));
-
- // "No <type>" is the default because we can't pick a
- // 'default' window to share.
- addDeviceToList(menupopup,
- stringBundle.getString("getUserMedia.no" + typeName + ".label"),
- "-1");
- menupopup.appendChild(doc.createElement("menuseparator"));
-
- // Build the list of 'devices'.
- let monitorIndex = 1;
- for (let i = 0; i < devices.length; ++i) {
- let device = devices[i];
-
- let name;
- // Building screen list from available screens.
- if (type == "screen") {
- if (device.name == "Primary Monitor") {
- name = stringBundle.getString("getUserMedia.shareEntireScreen.label");
- } else {
- name = stringBundle.getFormattedString("getUserMedia.shareMonitor.label",
- [monitorIndex]);
- ++monitorIndex;
- }
- } else {
- name = device.name;
- if (type == "application") {
- // The application names returned by the platform are of the form:
- // <window count>\x1e<application name>
- let sepIndex = name.indexOf("\x1e");
- let count = name.slice(0, sepIndex);
- let sawcStringId = "getUserMedia.shareApplicationWindowCount.label";
- name = PluralForm.get(parseInt(count), stringBundle.getString(sawcStringId))
- .replace("#1", name.slice(sepIndex + 1))
- .replace("#2", count);
- }
- }
- let item = addDeviceToList(menupopup, name, i, typeName);
- item.deviceId = device.id;
- if (device.scary)
- item.scary = true;
- }
-
- // Always re-select the "No <type>" item.
- doc.getElementById("webRTC-selectWindow-menulist").removeAttribute("value");
- doc.getElementById("webRTC-all-windows-shared").hidden = true;
- menupopup._commandEventListener = event => {
- let video = doc.getElementById("webRTC-previewVideo");
- if (video.stream) {
- video.stream.getTracks().forEach(t => t.stop());
- video.stream = null;
- }
-
- let deviceId = event.target.deviceId;
- if (deviceId == undefined) {
- doc.getElementById("webRTC-preview").hidden = true;
- video.src = null;
- return;
- }
-
- let scary = event.target.scary;
- let warning = doc.getElementById("webRTC-previewWarning");
- warning.hidden = !scary;
- let chromeWin = doc.defaultView;
- if (scary) {
- warning.hidden = false;
- let string;
- let bundle = chromeWin.gNavigatorBundle;
-
- let learnMoreText =
- bundle.getString("getUserMedia.shareScreen.learnMoreLabel");
- let baseURL =
- Services.urlFormatter.formatURLPref("app.support.baseURL");
- let learnMore =
- "<label class='text-link' href='" + baseURL + "screenshare-safety'>" +
- learnMoreText + "</label>";
-
- if (type == "screen") {
- string = bundle.getFormattedString("getUserMedia.shareScreenWarning.message",
- [learnMore]);
- } else {
- let brand =
- doc.getElementById("bundle_brand").getString("brandShortName");
- string = bundle.getFormattedString("getUserMedia.shareBasiliskWarning.message",
- [brand, learnMore]);
- }
- warning.innerHTML = string;
- }
-
- let perms = Services.perms;
- let chromeUri = Services.io.newURI(doc.documentURI);
- perms.add(chromeUri, "MediaManagerVideo", perms.ALLOW_ACTION,
- perms.EXPIRE_SESSION);
-
- video.deviceId = deviceId;
- let constraints = { video: { mediaSource: type, deviceId: {exact: deviceId } } };
- chromeWin.navigator.mediaDevices.getUserMedia(constraints).then(stream => {
- if (video.deviceId != deviceId) {
- // The user has selected a different device or closed the panel
- // before getUserMedia finished.
- stream.getTracks().forEach(t => t.stop());
- return;
- }
- video.src = chromeWin.URL.createObjectURL(stream);
- video.stream = stream;
- doc.getElementById("webRTC-preview").hidden = false;
- video.onloadedmetadata = function(e) {
- video.play();
- };
- });
- };
- menupopup.addEventListener("command", menupopup._commandEventListener);
- }
-
- function addDeviceToList(menupopup, deviceName, deviceIndex, type) {
- let menuitem = doc.createElement("menuitem");
- menuitem.setAttribute("value", deviceIndex);
- menuitem.setAttribute("label", deviceName);
- menuitem.setAttribute("tooltiptext", deviceName);
- if (type)
- menuitem.setAttribute("devicetype", type);
- menupopup.appendChild(menuitem);
- return menuitem;
- }
-
- doc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
- doc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
- doc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length || sharingAudio;
-
- let camMenupopup = doc.getElementById("webRTC-selectCamera-menupopup");
- let windowMenupopup = doc.getElementById("webRTC-selectWindow-menupopup");
- let micMenupopup = doc.getElementById("webRTC-selectMicrophone-menupopup");
- if (sharingScreen)
- listScreenShareDevices(windowMenupopup, videoDevices);
- else
- listDevices(camMenupopup, videoDevices);
-
- if (!sharingAudio)
- listDevices(micMenupopup, audioDevices);
-
- this.mainAction.callback = function(aState) {
- let remember = aState && aState.checkboxChecked;
- let allowedDevices = [];
- let perms = Services.perms;
- if (videoDevices.length) {
- let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
- let videoDeviceIndex = doc.getElementById(listId).value;
- let allowVideoDevice = videoDeviceIndex != "-1";
- if (allowVideoDevice) {
- allowedDevices.push(videoDeviceIndex);
- // Session permission will be removed after use
- // (it's really one-shot, not for the entire session)
- perms.add(uri, "MediaManagerVideo", perms.ALLOW_ACTION,
- perms.EXPIRE_SESSION);
- if (!webrtcUI.activePerms.has(aBrowser.outerWindowID)) {
- webrtcUI.activePerms.set(aBrowser.outerWindowID, new Set());
- }
-
- for (let device of videoDevices) {
- if (device.deviceIndex == videoDeviceIndex) {
- webrtcUI.activePerms.get(aBrowser.outerWindowID)
- .add(aRequest.windowID + device.mediaSource + device.id);
- break;
- }
- }
- if (remember)
- SitePermissions.set(uri, "camera", SitePermissions.ALLOW);
- }
- }
- if (audioDevices.length) {
- if (!sharingAudio) {
- let audioDeviceIndex = doc.getElementById("webRTC-selectMicrophone-menulist").value;
- let allowMic = audioDeviceIndex != "-1";
- if (allowMic) {
- allowedDevices.push(audioDeviceIndex);
- if (!webrtcUI.activePerms.has(aBrowser.outerWindowID)) {
- webrtcUI.activePerms.set(aBrowser.outerWindowID, new Set());
- }
-
- for (let device of audioDevices) {
- if (device.deviceIndex == audioDeviceIndex) {
- webrtcUI.activePerms.get(aBrowser.outerWindowID)
- .add(aRequest.windowID + device.mediaSource + device.id);
- break;
- }
- }
- if (remember)
- SitePermissions.set(uri, "microphone", SitePermissions.ALLOW);
- }
- } else {
- // Only one device possible for audio capture.
- allowedDevices.push(0);
- }
- }
-
- if (!allowedDevices.length) {
- denyRequest(notification.browser, aRequest);
- return;
- }
-
- if (remember) {
- // Remember on which URIs we set persistent permissions so that we
- // can remove them if the user clicks 'Stop Sharing'.
- aBrowser._devicePermissionURIs = aBrowser._devicePermissionURIs || [];
- aBrowser._devicePermissionURIs.push(uri);
- }
-
- let mm = notification.browser.messageManager;
- mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
- windowID: aRequest.windowID,
- devices: allowedDevices});
- };
- return false;
- }
- };
-
- // Don't offer "always remember" action in PB mode.
- if (!PrivateBrowsingUtils.isBrowserPrivate(aBrowser)) {
-
- // Disable the permanent 'Allow' action if the connection isn't secure, or for
- // screen/audio sharing (because we can't guess which window the user wants to
- // share without prompting).
- let reasonForNoPermanentAllow = "";
- if (sharingScreen) {
- reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.screen2";
- } else if (sharingAudio) {
- reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.audio";
- } else if (!aRequest.secure) {
- reasonForNoPermanentAllow = "getUserMedia.reasonForNoPermanentAllow.insecure";
- }
-
- options.checkbox = {
- label: stringBundle.getString("getUserMedia.remember"),
- checkedState: reasonForNoPermanentAllow ? {
- disableMainAction: true,
- warningLabel: stringBundle.getFormattedString(reasonForNoPermanentAllow,
- [productName])
- } : undefined,
- };
- }
-
- let iconType = "Devices";
- if (requestTypes.length == 1 && (requestTypes[0] == "Microphone" ||
- requestTypes[0] == "AudioCapture"))
- iconType = "Microphone";
- if (requestTypes.includes("Screen"))
- iconType = "Screen";
- let anchorId = "webRTC-share" + iconType + "-notification-icon";
-
- let iconClass = iconType.toLowerCase();
- if (iconClass == "devices")
- iconClass = "camera";
- options.popupIconClass = iconClass + "-icon";
-
- notification =
- chromeDoc.defaultView
- .PopupNotifications
- .show(aBrowser, "webRTC-shareDevices", message,
- anchorId, mainAction, secondaryActions,
- options);
- notification.callID = aRequest.callID;
-}
-
-function removePrompt(aBrowser, aCallId) {
- let chromeWin = aBrowser.ownerGlobal;
- let notification =
- chromeWin.PopupNotifications.getNotification("webRTC-shareDevices", aBrowser);
- if (notification && notification.callID == aCallId)
- notification.remove();
-}
-
-function getGlobalIndicator() {
-#ifndef XP_MACOSX
- const INDICATOR_CHROME_URI = "chrome://browser/content/webrtcIndicator.xul";
- const features = "chrome,dialog=yes,titlebar=no,popup=yes";
-
- return Services.ww.openWindow(null, INDICATOR_CHROME_URI, "_blank", features, []);
-#endif
-
- let indicator = {
- _camera: null,
- _microphone: null,
- _screen: null,
-
- _hiddenDoc: Cc["@mozilla.org/appshell/appShellService;1"]
- .getService(Ci.nsIAppShellService)
- .hiddenDOMWindow.document,
- _statusBar: Cc["@mozilla.org/widget/macsystemstatusbar;1"]
- .getService(Ci.nsISystemStatusBar),
-
- _command(aEvent) {
- webrtcUI.showSharingDoorhanger(aEvent.target.stream);
- },
-
- _popupShowing(aEvent) {
- let type = this.getAttribute("type");
- let activeStreams;
- if (type == "Camera") {
- activeStreams = webrtcUI.getActiveStreams(true, false, false);
- } else if (type == "Microphone") {
- activeStreams = webrtcUI.getActiveStreams(false, true, false);
- } else if (type == "Screen") {
- activeStreams = webrtcUI.getActiveStreams(false, false, true);
- type = webrtcUI.showScreenSharingIndicator;
- }
-
- let bundle =
- Services.strings.createBundle("chrome://browser/locale/webrtcIndicator.properties");
-
- if (activeStreams.length == 1) {
- let stream = activeStreams[0];
-
- let menuitem = this.ownerDocument.createElement("menuitem");
- let labelId = "webrtcIndicator.sharing" + type + "With.menuitem";
- let label = stream.browser.contentTitle || stream.uri;
- menuitem.setAttribute("label", bundle.formatStringFromName(labelId, [label], 1));
- menuitem.setAttribute("disabled", "true");
- this.appendChild(menuitem);
-
- menuitem = this.ownerDocument.createElement("menuitem");
- menuitem.setAttribute("label",
- bundle.GetStringFromName("webrtcIndicator.controlSharing.menuitem"));
- menuitem.stream = stream;
- menuitem.addEventListener("command", indicator._command);
-
- this.appendChild(menuitem);
- return true;
- }
-
- // We show a different menu when there are several active streams.
- let menuitem = this.ownerDocument.createElement("menuitem");
- let labelId = "webrtcIndicator.sharing" + type + "WithNTabs.menuitem";
- let count = activeStreams.length;
- let label = PluralForm.get(count, bundle.GetStringFromName(labelId)).replace("#1", count);
- menuitem.setAttribute("label", label);
- menuitem.setAttribute("disabled", "true");
- this.appendChild(menuitem);
-
- for (let stream of activeStreams) {
- let item = this.ownerDocument.createElement("menuitem");
- labelId = "webrtcIndicator.controlSharingOn.menuitem";
- label = stream.browser.contentTitle || stream.uri;
- item.setAttribute("label", bundle.formatStringFromName(labelId, [label], 1));
- item.stream = stream;
- item.addEventListener("command", indicator._command);
- this.appendChild(item);
- }
-
- return true;
- },
-
- _popupHiding(aEvent) {
- while (this.firstChild)
- this.firstChild.remove();
- },
-
- _setIndicatorState(aName, aState) {
- let field = "_" + aName.toLowerCase();
- if (aState && !this[field]) {
- let menu = this._hiddenDoc.createElement("menu");
- menu.setAttribute("id", "webRTC-sharing" + aName + "-menu");
-
- // The CSS will only be applied if the menu is actually inserted in the DOM.
- this._hiddenDoc.documentElement.appendChild(menu);
-
- this._statusBar.addItem(menu);
-
- let menupopup = this._hiddenDoc.createElement("menupopup");
- menupopup.setAttribute("type", aName);
- menupopup.addEventListener("popupshowing", this._popupShowing);
- menupopup.addEventListener("popuphiding", this._popupHiding);
- menupopup.addEventListener("command", this._command);
- menu.appendChild(menupopup);
-
- this[field] = menu;
- } else if (this[field] && !aState) {
- this._statusBar.removeItem(this[field]);
- this[field].remove();
- this[field] = null
- }
- },
- updateIndicatorState() {
- this._setIndicatorState("Camera", webrtcUI.showCameraIndicator);
- this._setIndicatorState("Microphone", webrtcUI.showMicrophoneIndicator);
- this._setIndicatorState("Screen", webrtcUI.showScreenSharingIndicator);
- },
- close() {
- this._setIndicatorState("Camera", false);
- this._setIndicatorState("Microphone", false);
- this._setIndicatorState("Screen", false);
- }
- };
-
- indicator.updateIndicatorState();
- return indicator;
-}
-
-function onTabSharingMenuPopupShowing(e) {
- let streams = webrtcUI.getActiveStreams(true, true, true);
- for (let streamInfo of streams) {
- let stringName = "getUserMedia.sharingMenu";
- let types = streamInfo.types;
- if (types.camera)
- stringName += "Camera";
- if (types.microphone)
- stringName += "Microphone";
- if (types.screen)
- stringName += types.screen;
-
- let doc = e.target.ownerDocument;
- let bundle = doc.defaultView.gNavigatorBundle;
-
- let origin = getHost(null, streamInfo.uri);
- let menuitem = doc.createElement("menuitem");
- menuitem.setAttribute("label", bundle.getFormattedString(stringName, [origin]));
- menuitem.stream = streamInfo;
- menuitem.addEventListener("command", onTabSharingMenuPopupCommand);
- e.target.appendChild(menuitem);
- }
-}
-
-function onTabSharingMenuPopupHiding(e) {
- while (this.lastChild)
- this.lastChild.remove();
-}
-
-function onTabSharingMenuPopupCommand(e) {
- webrtcUI.showSharingDoorhanger(e.target.stream);
-}
-
-function showOrCreateMenuForWindow(aWindow) {
- let document = aWindow.document;
- let menu = document.getElementById("tabSharingMenu");
- if (!menu) {
- let stringBundle = aWindow.gNavigatorBundle;
- menu = document.createElement("menu");
- menu.id = "tabSharingMenu";
- let labelStringId = "getUserMedia.sharingMenu.label";
- menu.setAttribute("label", stringBundle.getString(labelStringId));
-
- let container, insertionPoint;
-#ifdef XP_MACOSX
- container = document.getElementById("windowPopup");
- insertionPoint = document.getElementById("sep-window-list");
- let separator = document.createElement("menuseparator");
- separator.id = "tabSharingSeparator";
- container.insertBefore(separator, insertionPoint);
-#else
- let accesskeyStringId = "getUserMedia.sharingMenu.accesskey";
- menu.setAttribute("accesskey", stringBundle.getString(accesskeyStringId));
- container = document.getElementById("main-menubar");
- insertionPoint = document.getElementById("helpMenu");
-#endif
- let popup = document.createElement("menupopup");
- popup.id = "tabSharingMenuPopup";
- popup.addEventListener("popupshowing", onTabSharingMenuPopupShowing);
- popup.addEventListener("popuphiding", onTabSharingMenuPopupHiding);
- menu.appendChild(popup);
- container.insertBefore(menu, insertionPoint);
- } else {
- menu.hidden = false;
-#ifdef XP_MACOSX
- document.getElementById("tabSharingSeparator").hidden = false;
-#endif
- }
-}
-
-function maybeAddMenuIndicator(window) {
- if (webrtcUI.showGlobalIndicator) {
- showOrCreateMenuForWindow(window);
- }
-}
-
-var gIndicatorWindow = null;
-
-function updateIndicators(data, target) {
- if (data) {
- // the global indicators specific to this process
- let indicators;
- if (webrtcUI.processIndicators.has(target)) {
- indicators = webrtcUI.processIndicators.get(target);
- } else {
- indicators = {};
- webrtcUI.processIndicators.set(target, indicators);
- }
-
- indicators.showGlobalIndicator = data.showGlobalIndicator;
- indicators.showCameraIndicator = data.showCameraIndicator;
- indicators.showMicrophoneIndicator = data.showMicrophoneIndicator;
- indicators.showScreenSharingIndicator = data.showScreenSharingIndicator;
- }
-
- let browserWindowEnum = Services.wm.getEnumerator("navigator:browser");
- while (browserWindowEnum.hasMoreElements()) {
- let chromeWin = browserWindowEnum.getNext();
- if (webrtcUI.showGlobalIndicator) {
- showOrCreateMenuForWindow(chromeWin);
- } else {
- let doc = chromeWin.document;
- let existingMenu = doc.getElementById("tabSharingMenu");
- if (existingMenu) {
- existingMenu.hidden = true;
- }
-#ifdef XP_MACOSX
- let separator = doc.getElementById("tabSharingSeparator");
- if (separator) {
- separator.hidden = true;
- }
-#endif
- }
- }
-
- if (webrtcUI.showGlobalIndicator) {
- if (!gIndicatorWindow)
- gIndicatorWindow = getGlobalIndicator();
- else
- gIndicatorWindow.updateIndicatorState();
- } else if (gIndicatorWindow) {
- gIndicatorWindow.close();
- gIndicatorWindow = null;
- }
-}