diff options
Diffstat (limited to 'application/basilisk/modules/webrtcUI.jsm')
-rw-r--r-- | application/basilisk/modules/webrtcUI.jsm | 1087 |
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; - } -} |