Bug 1635257 - Add a checkbox to allow users to suppress notifications from the browser when sharing the screen. r=johannh,fluent-reviewers,flod, a=pascalc
authorMike Conley <mconley@mozilla.com>
Sat, 16 May 2020 00:58:48 +0000
changeset 591493 7cfdc9c5b7093370bac2a18a67fd2fe09fd54378
parent 591492 57b45999b145adced082b05149144c55d0b90ac0
child 591494 d89dbbcf87a89b4bd21c06d07b749e6a5727e8fb
push id13152
push userjcristau@mozilla.com
push dateMon, 18 May 2020 14:17:16 +0000
treeherdermozilla-beta@d89dbbcf87a8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh, fluent-reviewers, flod, pascalc
bugs1635257
milestone77.0
Bug 1635257 - Add a checkbox to allow users to suppress notifications from the browser when sharing the screen. r=johannh,fluent-reviewers,flod, a=pascalc Differential Revision: https://phabricator.services.mozilla.com/D74244
browser/actors/WebRTCChild.jsm
browser/actors/WebRTCParent.jsm
browser/base/content/popup-notifications.ftl
browser/modules/webrtcUI.jsm
--- a/browser/actors/WebRTCChild.jsm
+++ b/browser/actors/WebRTCChild.jsm
@@ -18,16 +18,28 @@ XPCOMUtils.defineLazyServiceGetter(
   "MediaManagerService",
   "@mozilla.org/mediaManagerService;1",
   "nsIMediaManagerService"
 );
 
 const kBrowserURL = AppConstants.BROWSER_CHROME_URL;
 
 class WebRTCChild extends JSWindowActorChild {
+  actorCreated() {
+    // The user might request that DOM notifications be silenced
+    // when sharing the screen. There doesn't seem to be a great
+    // way of storing that state in any of the objects going into
+    // the WebRTC API or coming out via the observer notification
+    // service, so we store it here on the actor.
+    //
+    // If the user chooses to silence notifications during screen
+    // share, this will get set to true.
+    this.suppressNotifications = false;
+  }
+
   // Called only for 'unload' to remove pending gUM prompts in reloaded frames.
   static handleEvent(aEvent) {
     let contentWindow = aEvent.target.defaultView;
     let actor = getActorForWindow(contentWindow);
     if (actor) {
       for (let key of contentWindow.pendingGetUserMediaRequests.keys()) {
         actor.sendAsyncMessage("webrtc:CancelRequest", key);
       }
@@ -90,16 +102,19 @@ class WebRTCChild extends JSWindowActorC
           allowedDevices.appendElement(devices[deviceIndex]);
         }
 
         Services.obs.notifyObservers(
           allowedDevices,
           "getUserMedia:response:allow",
           callID
         );
+
+        this.suppressNotifications = !!aMessage.data.suppressNotifications;
+
         break;
       }
       case "webrtc:Deny":
         denyGUMRequest(aMessage.data);
         break;
       case "webrtc:StopSharing":
         Services.obs.notifyObservers(
           null,
@@ -396,16 +411,17 @@ function updateIndicators(aSubject, aTop
   }
 
   let contentWindow = aSubject.getProperty("window");
 
   let actor = contentWindow ? getActorForWindow(contentWindow) : null;
   if (actor) {
     let tabState = getTabStateForContentWindow(contentWindow, false);
     tabState.windowId = getInnerWindowIDForWindow(contentWindow);
+    tabState.suppressNotifications = actor.suppressNotifications;
 
     actor.sendAsyncMessage("webrtc:UpdateIndicators", tabState);
   }
 }
 
 function removeBrowserSpecificIndicator(aSubject, aTopic, aData) {
   let contentWindow = Services.wm.getOuterWindowWithId(aData);
   if (contentWindow.document.documentURI == kBrowserURL) {
--- a/browser/actors/WebRTCParent.jsm
+++ b/browser/actors/WebRTCParent.jsm
@@ -968,17 +968,25 @@ function prompt(aActor, aBrowser, aReque
           .removeAttribute("invalidselection");
       }
 
       if (!sharingAudio) {
         listDevices(micMenupopup, audioDevices);
       }
 
       this.mainAction.callback = async function(aState) {
-        let remember = aState && aState.checkboxChecked;
+        let remember = false;
+        let silenceNotifications = false;
+
+        if (notificationSilencingEnabled && sharingScreen) {
+          silenceNotifications = aState && aState.checkboxChecked;
+        } else {
+          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;
@@ -1074,16 +1082,17 @@ function prompt(aActor, aBrowser, aReque
           aActor.denyRequestNoPermission(aRequest);
           return;
         }
 
         aActor.sendAsyncMessage("webrtc:Allow", {
           callID: aRequest.callID,
           windowID: aRequest.windowID,
           devices: allowedDevices,
+          suppressNotifications: silenceNotifications,
         });
       };
 
       // If we haven't handled the permission yet, we want to show the doorhanger.
       return false;
     },
   };
 
@@ -1119,17 +1128,34 @@ function prompt(aActor, aBrowser, aReque
     } else if (sharingAudio) {
       reasonForNoPermanentAllow =
         "getUserMedia.reasonForNoPermanentAllow.audio";
     } else if (!aRequest.secure) {
       reasonForNoPermanentAllow =
         "getUserMedia.reasonForNoPermanentAllow.insecure";
     }
 
-    if (!notificationSilencingEnabled) {
+    if (notificationSilencingEnabled && sharingScreen) {
+      let [
+        silenceNotifications,
+        silenceNotificationsWarning,
+      ] = localization.formatMessagesSync([
+        { id: "popup-silence-notifications-checkbox" },
+        { id: "popup-silence-notifications-checkbox-warning" },
+      ]);
+
+      options.checkbox = {
+        label: silenceNotifications.value,
+        checked: false,
+        checkedState: {
+          disableMainAction: false,
+          warningLabel: silenceNotificationsWarning.value,
+        },
+      };
+    } else {
       options.checkbox = {
         label: stringBundle.getString("getUserMedia.remember"),
         checked: principal.isAddonOrExpandedAddonPrincipal,
         checkedState: reasonForNoPermanentAllow
           ? {
               disableMainAction: true,
               warningLabel: stringBundle.getFormattedString(
                 reasonForNoPermanentAllow,
--- a/browser/base/content/popup-notifications.ftl
+++ b/browser/base/content/popup-notifications.ftl
@@ -9,8 +9,11 @@
 
 popup-screen-sharing-not-now =
   .label = Not Now
   .accesskey = w
 
 popup-screen-sharing-never =
   .label = Never Allow
   .accesskey = N
+
+popup-silence-notifications-checkbox = Disable notifications from { -brand-short-name } while sharing
+popup-silence-notifications-checkbox-warning = { -brand-short-name } will not display notifications while you are sharing.
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -275,18 +275,21 @@ var webrtcUI = {
         browsingContext: aBrowsingContext,
         topBrowsingContext: aBrowsingContext.top,
         state: aData,
       };
     }
 
     let sharedWindowRawDeviceIds = new Set();
     this.sharingScreen = false;
+    let suppressNotifications = false;
     for (let stream of this._streams) {
       let { state } = stream;
+      suppressNotifications |= state.suppressNotifications;
+
       for (let device of state.devices) {
         if (!device.scary) {
           continue;
         }
 
         let mediaSource = device.mediaSource;
 
         if (mediaSource == "window") {
@@ -308,16 +311,28 @@ var webrtcUI = {
         // window primitives don't exist. In that case, we can skip
         // to the next window.
         continue;
       }
       if (sharedWindowRawDeviceIds.has(rawDeviceId)) {
         this.sharedWindows.add(win);
       }
     }
+
+    if (
+      Services.prefs.getBoolPref(
+        "privacy.webrtc.allowSilencingNotifications",
+        false
+      )
+    ) {
+      let alertsService = Cc["@mozilla.org/alerts-service;1"]
+        .getService(Ci.nsIAlertsService)
+        .QueryInterface(Ci.nsIAlertsDoNotDisturb);
+      alertsService.suppressForScreenSharing = suppressNotifications;
+    }
   },
 
   /**
    * Remove all the streams associated with a given
    * browsing context.
    */
   forgetStreamsFromBrowserContext(aBrowsingContext) {
     for (let index = 0; index < webrtcUI._streams.length; ) {