michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: "use strict"; michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm"); michael@0: michael@0: var WebrtcUI = { michael@0: _notificationId: null, michael@0: michael@0: observe: function(aSubject, aTopic, aData) { michael@0: if (aTopic === "getUserMedia:request") { michael@0: this.handleRequest(aSubject, aTopic, aData); michael@0: } else if (aTopic === "recording-device-events") { michael@0: switch (aData) { michael@0: case "shutdown": michael@0: case "starting": michael@0: this.notify(); michael@0: break; michael@0: } michael@0: } michael@0: }, michael@0: michael@0: notify: function() { michael@0: let windows = MediaManagerService.activeMediaCaptureWindows; michael@0: let count = windows.Count(); michael@0: let msg = {}; michael@0: if (count == 0) { michael@0: if (this._notificationId) { michael@0: Notifications.cancel(this._notificationId); michael@0: this._notificationId = null; michael@0: } michael@0: } else { michael@0: let notificationOptions = { michael@0: title: Strings.brand.GetStringFromName("brandShortName"), michael@0: when: null, // hide the date row michael@0: light: [0xFF9500FF, 1000, 1000], michael@0: ongoing: true michael@0: }; michael@0: michael@0: let cameraActive = false; michael@0: let audioActive = false; michael@0: for (let i = 0; i < count; i++) { michael@0: let win = windows.GetElementAt(i); michael@0: let hasAudio = {}; michael@0: let hasVideo = {}; michael@0: MediaManagerService.mediaCaptureWindowState(win, hasVideo, hasAudio); michael@0: if (hasVideo.value) cameraActive = true; michael@0: if (hasAudio.value) audioActive = true; michael@0: } michael@0: michael@0: if (cameraActive && audioActive) { michael@0: notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingCameraAndMicrophone.message2"); michael@0: notificationOptions.icon = "drawable:alert_mic_camera"; michael@0: } else if (cameraActive) { michael@0: notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingCamera.message2"); michael@0: notificationOptions.icon = "drawable:alert_camera"; michael@0: } else if (audioActive) { michael@0: notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingMicrophone.message2"); michael@0: notificationOptions.icon = "drawable:alert_mic"; michael@0: } else { michael@0: // somethings wrong. lets throw michael@0: throw "Couldn't find any cameras or microphones being used" michael@0: } michael@0: michael@0: if (this._notificationId) michael@0: Notifications.update(this._notificationId, notificationOptions); michael@0: else michael@0: this._notificationId = Notifications.create(notificationOptions); michael@0: if (count > 1) michael@0: msg.count = count; michael@0: } michael@0: }, michael@0: michael@0: handleRequest: function handleRequest(aSubject, aTopic, aData) { michael@0: let constraints = aSubject.getConstraints(); michael@0: let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID); michael@0: michael@0: contentWindow.navigator.mozGetUserMediaDevices( michael@0: constraints, michael@0: function (devices) { michael@0: WebrtcUI.prompt(contentWindow, aSubject.callID, constraints.audio, michael@0: constraints.video, devices); michael@0: }, michael@0: function (error) { michael@0: Cu.reportError(error); michael@0: }, michael@0: aSubject.innerWindowID); michael@0: }, michael@0: michael@0: getDeviceButtons: function(audioDevices, videoDevices, aCallID) { michael@0: return [{ michael@0: label: Strings.browser.GetStringFromName("getUserMedia.denyRequest.label"), michael@0: callback: function() { michael@0: Services.obs.notifyObservers(null, "getUserMedia:response:deny", aCallID); michael@0: } michael@0: }, michael@0: { michael@0: label: Strings.browser.GetStringFromName("getUserMedia.shareRequest.label"), michael@0: callback: function(checked /* ignored */, inputs) { michael@0: let allowedDevices = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); michael@0: michael@0: let audioId = 0; michael@0: if (inputs && inputs.audioDevice != undefined) michael@0: audioId = inputs.audioDevice; michael@0: if (audioDevices[audioId]) michael@0: allowedDevices.AppendElement(audioDevices[audioId]); michael@0: michael@0: let videoId = 0; michael@0: if (inputs && inputs.videoDevice != undefined) michael@0: videoId = inputs.videoDevice; michael@0: if (videoDevices[videoId]) michael@0: allowedDevices.AppendElement(videoDevices[videoId]); michael@0: michael@0: Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID); michael@0: } michael@0: }]; michael@0: }, michael@0: michael@0: // Get a list of string names for devices. Ensures that none of the strings are blank michael@0: _getList: function(aDevices, aType) { michael@0: let defaultCount = 0; michael@0: return aDevices.map(function(device) { michael@0: // if this is a Camera input, convert the name to something readable michael@0: let res = /Camera\ \d+,\ Facing (front|back)/.exec(device.name); michael@0: if (res) michael@0: return Strings.browser.GetStringFromName("getUserMedia." + aType + "." + res[1] + "Camera"); michael@0: michael@0: if (device.name.startsWith("&") && device.name.endsWith(";")) michael@0: return Strings.browser.GetStringFromName(device.name.substring(1, device.name.length -1)); michael@0: michael@0: if (device.name.trim() == "") { michael@0: defaultCount++; michael@0: return Strings.browser.formatStringFromName("getUserMedia." + aType + ".default", [defaultCount], 1); michael@0: } michael@0: return device.name michael@0: }, this); michael@0: }, michael@0: michael@0: _addDevicesToOptions: function(aDevices, aType, aOptions, extraOptions) { michael@0: if (aDevices.length) { michael@0: michael@0: // Filter out empty items from the list michael@0: let list = this._getList(aDevices, aType); michael@0: if (extraOptions) michael@0: list = list.concat(extraOptions); michael@0: michael@0: if (list.length > 0) { michael@0: aOptions.inputs.push({ michael@0: id: aType, michael@0: type: "menulist", michael@0: label: Strings.browser.GetStringFromName("getUserMedia." + aType + ".prompt"), michael@0: values: list michael@0: }); michael@0: michael@0: } michael@0: } michael@0: }, michael@0: michael@0: prompt: function prompt(aContentWindow, aCallID, aAudioRequested, michael@0: aVideoRequested, aDevices) { michael@0: let audioDevices = []; michael@0: let videoDevices = []; michael@0: for (let device of aDevices) { michael@0: device = device.QueryInterface(Ci.nsIMediaDevice); michael@0: switch (device.type) { michael@0: case "audio": michael@0: if (aAudioRequested) michael@0: audioDevices.push(device); michael@0: break; michael@0: case "video": michael@0: if (aVideoRequested) michael@0: videoDevices.push(device); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: let requestType; michael@0: if (audioDevices.length && videoDevices.length) michael@0: requestType = "CameraAndMicrophone"; michael@0: else if (audioDevices.length) michael@0: requestType = "Microphone"; michael@0: else if (videoDevices.length) michael@0: requestType = "Camera"; michael@0: else michael@0: return; michael@0: michael@0: let host = aContentWindow.document.documentURIObject.host; michael@0: let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host; michael@0: let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1); michael@0: michael@0: let options = { inputs: [] }; michael@0: // if the users only option would be to select "No Audio" or "No Video" michael@0: // i.e. we're only showing audio or only video and there is only one device for that type michael@0: // don't bother showing a menulist to select from michael@0: var extraItems = null; michael@0: if (videoDevices.length > 1 || audioDevices.length > 0) { michael@0: // Only show the No Video option if there are also Audio devices to choose from michael@0: if (audioDevices.length > 0) michael@0: extraItems = [ Strings.browser.GetStringFromName("getUserMedia.videoDevice.none") ]; michael@0: this._addDevicesToOptions(videoDevices, "videoDevice", options, extraItems); michael@0: } michael@0: michael@0: if (audioDevices.length > 1 || videoDevices.length > 0) { michael@0: // Only show the No Audio option if there are also Video devices to choose from michael@0: if (videoDevices.length > 0) michael@0: extraItems = [ Strings.browser.GetStringFromName("getUserMedia.audioDevice.none") ]; michael@0: this._addDevicesToOptions(audioDevices, "audioDevice", options, extraItems); michael@0: } michael@0: michael@0: let buttons = this.getDeviceButtons(audioDevices, videoDevices, aCallID); michael@0: michael@0: NativeWindow.doorhanger.show(message, "webrtc-request", buttons, BrowserApp.selectedTab.id, options); michael@0: } michael@0: }