Bug 1156472 - Part 9 - Implement the minimum frontend to play with the feature. r=gijs,florian
☠☠ backed out by 6a28170cf9a2 ☠ ☠
authorPaul Adenot <paul@paul.cx>
Fri, 24 Jul 2015 14:28:17 +0200
changeset 254558 12805598e6fa6eabc8a25b7db9eb5a47985f806b
parent 254557 e2f0062a1f67ab2f9a428256dcbd4de71d147244
child 254559 4824d987466351de4268c09b030329da9ad275ab
push id29108
push userryanvm@gmail.com
push dateMon, 27 Jul 2015 14:12:01 +0000
treeherdermozilla-central@27ae736ef960 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgijs, florian
bugs1156472
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1156472 - Part 9 - Implement the minimum frontend to play with the feature. r=gijs,florian
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/ContentWebRTC.jsm
browser/modules/webrtcUI.jsm
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -753,16 +753,17 @@ you can use these alternative items. Oth
 <!ENTITY social.markpageMenu.label "Save Page To…">
 <!ENTITY social.marklinkMenu.accesskey "L">
 <!ENTITY social.marklinkMenu.label "Save Link To…">
 
 <!ENTITY getUserMedia.selectCamera.label "Camera to share:">
 <!ENTITY getUserMedia.selectCamera.accesskey "C">
 <!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
 <!ENTITY getUserMedia.selectMicrophone.accesskey "M">
+<!ENTITY getUserMedia.audioCapture.label "Audio from the tab will be shared.">
 <!ENTITY getUserMedia.allWindowsShared.message "All visible windows on your screen will be shared.">
 
 <!-- Bad Content Blocker Doorhanger Notification -->
 <!ENTITY badContentBlocked.moreinfo "Most websites will work properly even if content is blocked.">
 
 <!ENTITY mixedContentBlocked2.message "Insecure content">
 <!ENTITY mixedContentBlocked2.moreinfo "Some unencrypted elements on this website have been blocked.">
 <!ENTITY mixedContentBlocked2.learnMore "Learn More">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -548,23 +548,27 @@ identity.next.accessKey = n
 # LOCALIZATION NOTE: shown in the popup notification when a user successfully logs into a website
 # LOCALIZATION NOTE (identity.loggedIn.description): %S is the user's identity (e.g. user@example.com)
 identity.loggedIn.description = Signed in as: %S
 identity.loggedIn.signOut.label = Sign Out
 identity.loggedIn.signOut.accessKey = O
 
 # LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message,
 #                    getUserMedia.shareScreen.message, getUserMedia.shareCameraAndMicrophone.message,
-#                    getUserMedia.shareScreenAndMicrophone.message):
+#                    getUserMedia.shareScreenAndMicrophone.message, getUserMedia.shareCameraAndAudioCapture.message,
+#                    getUserMedia.shareAudioCapture.message, getUserMedia.shareScreenAndAudioCapture.message):
 #  %S is the website origin (e.g. www.mozilla.org)
 getUserMedia.shareCamera.message = Would you like to share your camera with %S?
 getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
 getUserMedia.shareScreen.message = Would you like to share your screen with %S?
 getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
+getUserMedia.shareCameraAndAudioCapture.message = Would you like to share your camera and this tab's audio with %S?
 getUserMedia.shareScreenAndMicrophone.message = Would you like to share your microphone and screen with %S?
+getUserMedia.shareScreenAndAudioCapture.message = Would you like to share this tab's audio and your screen with %S?
+getUserMedia.shareAudioCapture.message = Would you like to share this tab's audio with %S?
 getUserMedia.selectWindow.label=Window to share:
 getUserMedia.selectWindow.accesskey=W
 getUserMedia.selectScreen.label=Screen to share:
 getUserMedia.selectScreen.accesskey=S
 getUserMedia.selectApplication.label=Application to share:
 getUserMedia.selectApplication.accesskey=A
 getUserMedia.noVideo.label = No Video
 getUserMedia.noApplication.label = No Application
@@ -596,62 +600,79 @@ getUserMedia.never.label = Never Share
 getUserMedia.never.accesskey = N
 getUserMedia.sharingCamera.message2 = You are currently sharing your camera with this page.
 getUserMedia.sharingMicrophone.message2 = You are currently sharing your microphone with this page.
 getUserMedia.sharingCameraAndMicrophone.message2 = You are currently sharing your camera and microphone with this page.
 getUserMedia.sharingApplication.message = You are currently sharing an application with this page.
 getUserMedia.sharingScreen.message = You are currently sharing your screen with this page.
 getUserMedia.sharingWindow.message = You are currently sharing a window with this page.
 getUserMedia.sharingBrowser.message = You are currently sharing a tab with this page.
+getUserMedia.sharingAudioCapture.message = You are currently sharing a tab's audio with this page.
 getUserMedia.continueSharing.label = Continue Sharing
 getUserMedia.continueSharing.accesskey = C
 getUserMedia.stopSharing.label = Stop Sharing
 getUserMedia.stopSharing.accesskey = S
 
 getUserMedia.sharingMenu.label = Tabs sharing devices
 getUserMedia.sharingMenu.accesskey = d
 # LOCALIZATION NOTE (getUserMedia.sharingMenuCamera
 #                    getUserMedia.sharingMenuMicrophone,
+#                    getUserMedia.sharingMenuAudioCapture,
 #                    getUserMedia.sharingMenuApplication,
 #                    getUserMedia.sharingMenuScreen,
 #                    getUserMedia.sharingMenuWindow,
 #                    getUserMedia.sharingMenuBrowser,
 #                    getUserMedia.sharingMenuCameraMicrophone,
 #                    getUserMedia.sharingMenuCameraMicrophoneApplication,
 #                    getUserMedia.sharingMenuCameraMicrophoneScreen,
 #                    getUserMedia.sharingMenuCameraMicrophoneWindow,
 #                    getUserMedia.sharingMenuCameraMicrophoneBrowser,
+#                    getUserMedia.sharingMenuCameraAudioCapture,
+#                    getUserMedia.sharingMenuCameraAudioCaptureApplication,
+#                    getUserMedia.sharingMenuCameraAudioCaptureScreen,
+#                    getUserMedia.sharingMenuCameraAudioCaptureWindow,
+#                    getUserMedia.sharingMenuCameraAudioCaptureBrowser,
 #                    getUserMedia.sharingMenuCameraApplication,
 #                    getUserMedia.sharingMenuCameraScreen,
 #                    getUserMedia.sharingMenuCameraWindow,
 #                    getUserMedia.sharingMenuCameraBrowser,
 #                    getUserMedia.sharingMenuMicrophoneApplication,
 #                    getUserMedia.sharingMenuMicrophoneScreen,
 #                    getUserMedia.sharingMenuMicrophoneWindow,
 #                    getUserMedia.sharingMenuMicrophoneBrowser):
 # %S is the website origin (e.g. www.mozilla.org)
 getUserMedia.sharingMenuCamera = %S (camera)
 getUserMedia.sharingMenuMicrophone = %S (microphone)
+getUserMedia.sharingMenuAudioCapture = %S (tab audio)
 getUserMedia.sharingMenuApplication = %S (application)
 getUserMedia.sharingMenuScreen = %S (screen)
 getUserMedia.sharingMenuWindow = %S (window)
 getUserMedia.sharingMenuBrowser = %S (tab)
 getUserMedia.sharingMenuCameraMicrophone = %S (camera and microphone)
 getUserMedia.sharingMenuCameraMicrophoneApplication = %S (camera, microphone and application)
 getUserMedia.sharingMenuCameraMicrophoneScreen = %S (camera, microphone and screen)
 getUserMedia.sharingMenuCameraMicrophoneWindow = %S (camera, microphone and window)
 getUserMedia.sharingMenuCameraMicrophoneBrowser = %S (camera, microphone and tab)
+getUserMedia.sharingMenuCameraAudioCapture = %S (camera and tab audio)
+getUserMedia.sharingMenuCameraAudioCaptureApplication = %S (camera, tab audio and application)
+getUserMedia.sharingMenuCameraAudioCaptureScreen = %S (camera, tab audio and screen)
+getUserMedia.sharingMenuCameraAudioCaptureWindow = %S (camera, tab audio and window)
+getUserMedia.sharingMenuCameraAudioCaptureBrowser = %S (camera, tab audio and tab)
 getUserMedia.sharingMenuCameraApplication = %S (camera and application)
 getUserMedia.sharingMenuCameraScreen = %S (camera and screen)
 getUserMedia.sharingMenuCameraWindow = %S (camera and window)
 getUserMedia.sharingMenuCameraBrowser = %S (camera and tab)
 getUserMedia.sharingMenuMicrophoneApplication = %S (microphone and application)
 getUserMedia.sharingMenuMicrophoneScreen = %S (microphone and screen)
 getUserMedia.sharingMenuMicrophoneWindow = %S (microphone and window)
 getUserMedia.sharingMenuMicrophoneBrowser = %S (microphone and tab)
+getUserMedia.sharingMenuMicrophoneApplication = %S (tab audio and application)
+getUserMedia.sharingMenuMicrophoneScreen = %S (tab audio and screen)
+getUserMedia.sharingMenuMicrophoneWindow = %S (tab audio and window)
+getUserMedia.sharingMenuMicrophoneBrowser = %S (tab audio and tab)
 # LOCALIZATION NOTE(getUserMedia.sharingMenuUnknownHost): this is used for the website
 # origin for the sharing menu if no readable origin could be deduced from the URL.
 getUserMedia.sharingMenuUnknownHost = Unknown origin
 
 # LOCALIZATION NOTE(emeNotifications.drmContentPlaying.message2): %S is brandShortName.
 emeNotifications.drmContentPlaying.message2 = Some audio or video on this site uses DRM software, which may limit what %S can let you do with it.
 emeNotifications.drmContentPlaying.button.label = Configure…
 emeNotifications.drmContentPlaying.button.accesskey = C
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -81,24 +81,31 @@ function handleRequest(aSubject, aTopic,
 
 function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSecure) {
   let audioDevices = [];
   let videoDevices = [];
   let devices = [];
 
   // MediaStreamConstraints defines video as 'boolean or MediaTrackConstraints'.
   let video = aConstraints.video || aConstraints.picture;
+  let audio = aConstraints.audio;
   let sharingScreen = video && typeof(video) != "boolean" &&
                       video.mediaSource != "camera";
+  let sharingAudio = audio && typeof(audio) != "boolean" &&
+                     audio.mediaSource != "microphone";
   for (let device of aDevices) {
     device = device.QueryInterface(Ci.nsIMediaDevice);
     switch (device.type) {
       case "audio":
-        if (aConstraints.audio) {
-          audioDevices.push({name: device.name, deviceIndex: devices.length});
+        // Check that if we got a microphone, we have not requested an audio
+        // capture, and if we have requested an audio capture, we are not
+        // getting a microphone instead.
+        if (audio && (device.mediaSource == "microphone") != sharingAudio) {
+          audioDevices.push({name: device.name, deviceIndex: devices.length,
+                             mediaSource: device.mediaSource});
           devices.push(device);
         }
         break;
       case "video":
         // Verify that if we got a camera, we haven't requested a screen share,
         // or that if we requested a screen share we aren't getting a camera.
         if (video && (device.mediaSource == "camera") != sharingScreen) {
           videoDevices.push({name: device.name, deviceIndex: devices.length,
@@ -108,17 +115,17 @@ function prompt(aContentWindow, aWindowI
         break;
     }
   }
 
   let requestTypes = [];
   if (videoDevices.length)
     requestTypes.push(sharingScreen ? "Screen" : "Camera");
   if (audioDevices.length)
-    requestTypes.push("Microphone");
+    requestTypes.push(sharingAudio ? "AudioCapture" : "Microphone");
 
   if (!requestTypes.length) {
     denyRequest({callID: aCallID}, "NotFoundError");
     return;
   }
 
   if (!aContentWindow.pendingGetUserMediaRequests) {
     aContentWindow.pendingGetUserMediaRequests = new Map();
@@ -128,16 +135,17 @@ function prompt(aContentWindow, aWindowI
 
   let request = {
     callID: aCallID,
     windowID: aWindowID,
     documentURI: aContentWindow.document.documentURI,
     secure: aSecure,
     requestTypes: requestTypes,
     sharingScreen: sharingScreen,
+    sharingAudio: sharingAudio,
     audioDevices: audioDevices,
     videoDevices: videoDevices
   };
 
   let mm = getMessageManagerForWindow(aContentWindow);
   mm.sendAsyncMessage("webrtc:Request", request);
 }
 
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -183,30 +183,30 @@ function getHost(uri, href) {
       host = bundle.GetStringFromName("getUserMedia.sharingMenuUnknownHost");
     }
   }
   return host;
 }
 
 function prompt(aBrowser, aRequest) {
   let {audioDevices: audioDevices, videoDevices: videoDevices,
-       sharingScreen: sharingScreen, requestTypes: requestTypes} = aRequest;
+       sharingScreen: sharingScreen, sharingAudio: sharingAudio,
+       requestTypes: requestTypes} = aRequest;
   let uri = Services.io.newURI(aRequest.documentURI, null, null);
   let host = getHost(uri);
   let chromeDoc = aBrowser.ownerDocument;
   let chromeWin = chromeDoc.defaultView;
   let stringBundle = chromeWin.gNavigatorBundle;
   let stringId = "getUserMedia.share" + requestTypes.join("And") + ".message";
   let message = stringBundle.getFormattedString(stringId, [host]);
 
   let mainLabel;
-  if (sharingScreen) {
+  if (sharingScreen || sharingAudio) {
     mainLabel = stringBundle.getString("getUserMedia.shareSelectedItems.label");
-  }
-  else {
+  } else {
     let string = stringBundle.getString("getUserMedia.shareSelectedDevices.label");
     mainLabel = PluralForm.get(requestTypes.length, string);
   }
 
   let notification; // Used by action callbacks.
   let mainAction = {
     label: mainLabel,
     accessKey: stringBundle.getString("getUserMedia.shareSelectedDevices.accesskey"),
@@ -220,38 +220,38 @@ function prompt(aBrowser, aRequest) {
     {
       label: stringBundle.getString("getUserMedia.denyRequest.label"),
       accessKey: stringBundle.getString("getUserMedia.denyRequest.accesskey"),
       callback: function () {
         denyRequest(notification.browser, aRequest);
       }
     }
   ];
-
-  if (!sharingScreen) { // Bug 1037438: implement 'never' for screen sharing.
+  // Bug 1037438: implement 'never' for screen sharing.
+  if (!sharingScreen && !sharingAudio) {
     secondaryActions.push({
       label: stringBundle.getString("getUserMedia.never.label"),
       accessKey: stringBundle.getString("getUserMedia.never.accesskey"),
       callback: function () {
         denyRequest(notification.browser, aRequest);
         // Let someone save "Never" for http sites so that they can be stopped from
         // bothering you with doorhangers.
         let perms = Services.perms;
         if (audioDevices.length)
           perms.add(uri, "microphone", perms.DENY_ACTION);
         if (videoDevices.length)
           perms.add(uri, "camera", perms.DENY_ACTION);
       }
     });
   }
 
-  if (aRequest.secure && !sharingScreen) {
+  if (aRequest.secure && !sharingScreen && !sharingAudio) {
     // Don't show the 'Always' action if the connection isn't secure, or for
-    // screen sharing (because we can't guess which window the user wants to
-    // share without prompting).
+    // screen/audio sharing (because we can't guess which window the user wants
+    // to share without prompting).
     secondaryActions.unshift({
       label: stringBundle.getString("getUserMedia.always.label"),
       accessKey: stringBundle.getString("getUserMedia.always.accesskey"),
       callback: function () {
         mainAction.callback(true);
       }
     });
   }
@@ -261,17 +261,18 @@ function prompt(aBrowser, aRequest) {
       if (aTopic == "swapping")
         return true;
 
       let chromeDoc = this.browser.ownerDocument;
 
       if (aTopic == "shown") {
         let PopupNotifications = chromeDoc.defaultView.PopupNotifications;
         let popupId = "Devices";
-        if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
+        if (requestTypes.length == 1 && (requestTypes[0] == "Microphone" ||
+                                         requestTypes[0] == "AudioCapture"))
           popupId = "Microphone";
         if (requestTypes.indexOf("Screen") != -1)
           popupId = "Screen";
         PopupNotifications.panel.firstChild.setAttribute("popupid", "webRTC-share" + popupId);
       }
 
       if (aTopic != "showing")
         return false;
@@ -379,31 +380,35 @@ function prompt(aBrowser, aRequest) {
         menuitem.setAttribute("tooltiptext", deviceName);
         if (type)
           menuitem.setAttribute("devicetype", type);
         menupopup.appendChild(menuitem);
       }
 
       chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
       chromeDoc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
-      chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length;
+      chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length || sharingAudio;
 
       let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup");
       let windowMenupopup = chromeDoc.getElementById("webRTC-selectWindow-menupopup");
       let micMenupopup = chromeDoc.getElementById("webRTC-selectMicrophone-menupopup");
       if (sharingScreen)
         listScreenShareDevices(windowMenupopup, videoDevices);
       else
         listDevices(camMenupopup, videoDevices);
-      listDevices(micMenupopup, audioDevices);
+
+      if (!sharingAudio)
+        listDevices(micMenupopup, audioDevices);
+
       if (requestTypes.length == 2) {
         let stringBundle = chromeDoc.defaultView.gNavigatorBundle;
         if (!sharingScreen)
           addDeviceToList(camMenupopup, stringBundle.getString("getUserMedia.noVideo.label"), "-1");
-        addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
+        if (!sharingAudio)
+          addDeviceToList(micMenupopup, stringBundle.getString("getUserMedia.noAudio.label"), "-1");
       }
 
       this.mainAction.callback = function(aRemember) {
         let allowedDevices = [];
         let perms = Services.perms;
         if (videoDevices.length) {
           let listId = "webRTC-select" + (sharingScreen ? "Window" : "Camera") + "-menulist";
           let videoDeviceIndex = chromeDoc.getElementById(listId).value;
@@ -411,23 +416,28 @@ function prompt(aBrowser, aRequest) {
           if (allowCamera)
             allowedDevices.push(videoDeviceIndex);
           if (aRemember) {
             perms.add(uri, "camera",
                       allowCamera ? perms.ALLOW_ACTION : perms.DENY_ACTION);
           }
         }
         if (audioDevices.length) {
-          let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value;
-          let allowMic = audioDeviceIndex != "-1";
-          if (allowMic)
-            allowedDevices.push(audioDeviceIndex);
-          if (aRemember) {
-            perms.add(uri, "microphone",
-                      allowMic ? perms.ALLOW_ACTION : perms.DENY_ACTION);
+          if (!sharingAudio) {
+            let audioDeviceIndex = chromeDoc.getElementById("webRTC-selectMicrophone-menulist").value;
+            let allowMic = audioDeviceIndex != "-1";
+            if (allowMic)
+              allowedDevices.push(audioDeviceIndex);
+            if (aRemember) {
+              perms.add(uri, "microphone",
+                        allowMic ? perms.ALLOW_ACTION : perms.DENY_ACTION);
+            }
+          } else {
+            // Only one device possible for audio capture.
+            allowedDevices.push(0);
           }
         }
 
         if (!allowedDevices.length) {
           denyRequest(notification.browser, aRequest);
           return;
         }