|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 "use strict"; |
|
5 |
|
6 XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm"); |
|
7 |
|
8 var WebrtcUI = { |
|
9 _notificationId: null, |
|
10 |
|
11 observe: function(aSubject, aTopic, aData) { |
|
12 if (aTopic === "getUserMedia:request") { |
|
13 this.handleRequest(aSubject, aTopic, aData); |
|
14 } else if (aTopic === "recording-device-events") { |
|
15 switch (aData) { |
|
16 case "shutdown": |
|
17 case "starting": |
|
18 this.notify(); |
|
19 break; |
|
20 } |
|
21 } |
|
22 }, |
|
23 |
|
24 notify: function() { |
|
25 let windows = MediaManagerService.activeMediaCaptureWindows; |
|
26 let count = windows.Count(); |
|
27 let msg = {}; |
|
28 if (count == 0) { |
|
29 if (this._notificationId) { |
|
30 Notifications.cancel(this._notificationId); |
|
31 this._notificationId = null; |
|
32 } |
|
33 } else { |
|
34 let notificationOptions = { |
|
35 title: Strings.brand.GetStringFromName("brandShortName"), |
|
36 when: null, // hide the date row |
|
37 light: [0xFF9500FF, 1000, 1000], |
|
38 ongoing: true |
|
39 }; |
|
40 |
|
41 let cameraActive = false; |
|
42 let audioActive = false; |
|
43 for (let i = 0; i < count; i++) { |
|
44 let win = windows.GetElementAt(i); |
|
45 let hasAudio = {}; |
|
46 let hasVideo = {}; |
|
47 MediaManagerService.mediaCaptureWindowState(win, hasVideo, hasAudio); |
|
48 if (hasVideo.value) cameraActive = true; |
|
49 if (hasAudio.value) audioActive = true; |
|
50 } |
|
51 |
|
52 if (cameraActive && audioActive) { |
|
53 notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingCameraAndMicrophone.message2"); |
|
54 notificationOptions.icon = "drawable:alert_mic_camera"; |
|
55 } else if (cameraActive) { |
|
56 notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingCamera.message2"); |
|
57 notificationOptions.icon = "drawable:alert_camera"; |
|
58 } else if (audioActive) { |
|
59 notificationOptions.message = Strings.browser.GetStringFromName("getUserMedia.sharingMicrophone.message2"); |
|
60 notificationOptions.icon = "drawable:alert_mic"; |
|
61 } else { |
|
62 // somethings wrong. lets throw |
|
63 throw "Couldn't find any cameras or microphones being used" |
|
64 } |
|
65 |
|
66 if (this._notificationId) |
|
67 Notifications.update(this._notificationId, notificationOptions); |
|
68 else |
|
69 this._notificationId = Notifications.create(notificationOptions); |
|
70 if (count > 1) |
|
71 msg.count = count; |
|
72 } |
|
73 }, |
|
74 |
|
75 handleRequest: function handleRequest(aSubject, aTopic, aData) { |
|
76 let constraints = aSubject.getConstraints(); |
|
77 let contentWindow = Services.wm.getOuterWindowWithId(aSubject.windowID); |
|
78 |
|
79 contentWindow.navigator.mozGetUserMediaDevices( |
|
80 constraints, |
|
81 function (devices) { |
|
82 WebrtcUI.prompt(contentWindow, aSubject.callID, constraints.audio, |
|
83 constraints.video, devices); |
|
84 }, |
|
85 function (error) { |
|
86 Cu.reportError(error); |
|
87 }, |
|
88 aSubject.innerWindowID); |
|
89 }, |
|
90 |
|
91 getDeviceButtons: function(audioDevices, videoDevices, aCallID) { |
|
92 return [{ |
|
93 label: Strings.browser.GetStringFromName("getUserMedia.denyRequest.label"), |
|
94 callback: function() { |
|
95 Services.obs.notifyObservers(null, "getUserMedia:response:deny", aCallID); |
|
96 } |
|
97 }, |
|
98 { |
|
99 label: Strings.browser.GetStringFromName("getUserMedia.shareRequest.label"), |
|
100 callback: function(checked /* ignored */, inputs) { |
|
101 let allowedDevices = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray); |
|
102 |
|
103 let audioId = 0; |
|
104 if (inputs && inputs.audioDevice != undefined) |
|
105 audioId = inputs.audioDevice; |
|
106 if (audioDevices[audioId]) |
|
107 allowedDevices.AppendElement(audioDevices[audioId]); |
|
108 |
|
109 let videoId = 0; |
|
110 if (inputs && inputs.videoDevice != undefined) |
|
111 videoId = inputs.videoDevice; |
|
112 if (videoDevices[videoId]) |
|
113 allowedDevices.AppendElement(videoDevices[videoId]); |
|
114 |
|
115 Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID); |
|
116 } |
|
117 }]; |
|
118 }, |
|
119 |
|
120 // Get a list of string names for devices. Ensures that none of the strings are blank |
|
121 _getList: function(aDevices, aType) { |
|
122 let defaultCount = 0; |
|
123 return aDevices.map(function(device) { |
|
124 // if this is a Camera input, convert the name to something readable |
|
125 let res = /Camera\ \d+,\ Facing (front|back)/.exec(device.name); |
|
126 if (res) |
|
127 return Strings.browser.GetStringFromName("getUserMedia." + aType + "." + res[1] + "Camera"); |
|
128 |
|
129 if (device.name.startsWith("&") && device.name.endsWith(";")) |
|
130 return Strings.browser.GetStringFromName(device.name.substring(1, device.name.length -1)); |
|
131 |
|
132 if (device.name.trim() == "") { |
|
133 defaultCount++; |
|
134 return Strings.browser.formatStringFromName("getUserMedia." + aType + ".default", [defaultCount], 1); |
|
135 } |
|
136 return device.name |
|
137 }, this); |
|
138 }, |
|
139 |
|
140 _addDevicesToOptions: function(aDevices, aType, aOptions, extraOptions) { |
|
141 if (aDevices.length) { |
|
142 |
|
143 // Filter out empty items from the list |
|
144 let list = this._getList(aDevices, aType); |
|
145 if (extraOptions) |
|
146 list = list.concat(extraOptions); |
|
147 |
|
148 if (list.length > 0) { |
|
149 aOptions.inputs.push({ |
|
150 id: aType, |
|
151 type: "menulist", |
|
152 label: Strings.browser.GetStringFromName("getUserMedia." + aType + ".prompt"), |
|
153 values: list |
|
154 }); |
|
155 |
|
156 } |
|
157 } |
|
158 }, |
|
159 |
|
160 prompt: function prompt(aContentWindow, aCallID, aAudioRequested, |
|
161 aVideoRequested, aDevices) { |
|
162 let audioDevices = []; |
|
163 let videoDevices = []; |
|
164 for (let device of aDevices) { |
|
165 device = device.QueryInterface(Ci.nsIMediaDevice); |
|
166 switch (device.type) { |
|
167 case "audio": |
|
168 if (aAudioRequested) |
|
169 audioDevices.push(device); |
|
170 break; |
|
171 case "video": |
|
172 if (aVideoRequested) |
|
173 videoDevices.push(device); |
|
174 break; |
|
175 } |
|
176 } |
|
177 |
|
178 let requestType; |
|
179 if (audioDevices.length && videoDevices.length) |
|
180 requestType = "CameraAndMicrophone"; |
|
181 else if (audioDevices.length) |
|
182 requestType = "Microphone"; |
|
183 else if (videoDevices.length) |
|
184 requestType = "Camera"; |
|
185 else |
|
186 return; |
|
187 |
|
188 let host = aContentWindow.document.documentURIObject.host; |
|
189 let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host; |
|
190 let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1); |
|
191 |
|
192 let options = { inputs: [] }; |
|
193 // if the users only option would be to select "No Audio" or "No Video" |
|
194 // i.e. we're only showing audio or only video and there is only one device for that type |
|
195 // don't bother showing a menulist to select from |
|
196 var extraItems = null; |
|
197 if (videoDevices.length > 1 || audioDevices.length > 0) { |
|
198 // Only show the No Video option if there are also Audio devices to choose from |
|
199 if (audioDevices.length > 0) |
|
200 extraItems = [ Strings.browser.GetStringFromName("getUserMedia.videoDevice.none") ]; |
|
201 this._addDevicesToOptions(videoDevices, "videoDevice", options, extraItems); |
|
202 } |
|
203 |
|
204 if (audioDevices.length > 1 || videoDevices.length > 0) { |
|
205 // Only show the No Audio option if there are also Video devices to choose from |
|
206 if (videoDevices.length > 0) |
|
207 extraItems = [ Strings.browser.GetStringFromName("getUserMedia.audioDevice.none") ]; |
|
208 this._addDevicesToOptions(audioDevices, "audioDevice", options, extraItems); |
|
209 } |
|
210 |
|
211 let buttons = this.getDeviceButtons(audioDevices, videoDevices, aCallID); |
|
212 |
|
213 NativeWindow.doorhanger.show(message, "webrtc-request", buttons, BrowserApp.selectedTab.id, options); |
|
214 } |
|
215 } |