Bug 1672432 - Don't remove persistent device permissions when clicking 'Stop Sharing' in the WebRTC global indicator. r=pbz a=pascalc
authorMike Conley <mconley@mozilla.com>
Tue, 27 Oct 2020 16:28:25 +0000
changeset 619350 80a3373a44fc70175f92ab464d76001689ac5836
parent 619349 59be4b6bb544f332c55cab3b2e3bd16031ef2e28
child 619351 fe59ac66d1f8eaa9bdf90a13f88d12982edf7c42
push id14429
push userarchaeopteryx@coole-files.de
push dateWed, 28 Oct 2020 18:03:55 +0000
treeherdermozilla-beta@08a92e35a77c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspbz, pascalc
bugs1672432
milestone83.0
Bug 1672432 - Don't remove persistent device permissions when clicking 'Stop Sharing' in the WebRTC global indicator. r=pbz a=pascalc Differential Revision: https://phabricator.services.mozilla.com/D94501
browser/base/content/test/webrtc/browser.ini
browser/base/content/test/webrtc/browser_stop_sharing_button.js
browser/base/content/test/webrtc/browser_stop_streams_on_indicator_close.js
browser/base/content/test/webrtc/browser_tab_switch_warning.js
browser/base/content/test/webrtc/head.js
browser/base/content/webrtcIndicator.js
browser/modules/webrtcUI.jsm
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -4,17 +4,17 @@ support-files =
   get_user_media_in_frame.html
   get_user_media_in_oop_frame.html
   get_user_media_in_xorigin_frame.html
   get_user_media_in_xorigin_frame_ancestor.html
   head.js
 prefs =
   privacy.webrtc.allowSilencingNotifications=true
   privacy.webrtc.legacyGlobalIndicator=false
-  privacy.webrtc.sharedTabWarning=true
+  privacy.webrtc.sharedTabWarning=false
 
 [browser_device_controls_menus.js]
 [browser_devices_get_user_media.js]
 skip-if = (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_anim.js]
 [browser_devices_get_user_media_default_permissions.js]
 [browser_devices_get_user_media_in_frame.js]
 skip-if = debug # bug 1369731
--- a/browser/base/content/test/webrtc/browser_stop_sharing_button.js
+++ b/browser/base/content/test/webrtc/browser_stop_sharing_button.js
@@ -21,25 +21,16 @@ add_task(async function setup() {
 });
 
 /**
  * Tests that if the user chooses to "Stop Sharing" a display while
  * also sharing their microphone or camera, that only the display
  * stream is stopped.
  */
 add_task(async function test_stop_sharing() {
-  let prefs = [
-    [PREF_PERMISSION_FAKE, true],
-    [PREF_AUDIO_LOOPBACK, ""],
-    [PREF_VIDEO_LOOPBACK, ""],
-    [PREF_FAKE_STREAMS, true],
-    [PREF_FOCUS_SOURCE, false],
-  ];
-  await SpecialPowers.pushPrefEnv({ set: prefs });
-
   await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => {
     let indicatorPromise = promiseIndicatorWindow();
 
     await shareDevices(
       browser,
       true /* camera */,
       true /* microphone */,
       SHARE_SCREEN
@@ -110,8 +101,72 @@ add_task(async function test_stop_sharin
     BrowserTestUtils.is_hidden(
       indicator.document.getElementById("display-share")
     ),
     "The display-share section of the indicator should now be hidden."
   );
 
   BrowserTestUtils.removeTab(tab1);
 });
+
+/**
+ * Tests that if the user chooses to "Stop Sharing" a display, persistent
+ * permissions are not removed for camera or microphone devices.
+ */
+add_task(async function test_keep_permissions() {
+  await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => {
+    let indicatorPromise = promiseIndicatorWindow();
+
+    await shareDevices(
+      browser,
+      true /* camera */,
+      true /* microphone */,
+      SHARE_SCREEN,
+      true /* remember */
+    );
+
+    let indicator = await indicatorPromise;
+
+    let stopSharingButton = indicator.document.getElementById("stop-sharing");
+    let stopSharingPromise = expectObserverCalled("recording-device-events");
+    stopSharingButton.click();
+    await stopSharingPromise;
+
+    // Ensure that we're still sharing the other streams.
+    await checkSharingUI({ audio: true, video: true });
+
+    // Ensure that the "display-share" section of the indicator is now hidden
+    Assert.ok(
+      BrowserTestUtils.is_hidden(
+        indicator.document.getElementById("display-share")
+      ),
+      "The display-share section of the indicator should now be hidden."
+    );
+
+    let { state: micState, scope: micScope } = SitePermissions.getForPrincipal(
+      browser.contentPrincipal,
+      "microphone",
+      browser
+    );
+
+    Assert.equal(micState, SitePermissions.ALLOW);
+    Assert.equal(micScope, SitePermissions.SCOPE_PERSISTENT);
+
+    let { state: camState, scope: camScope } = SitePermissions.getForPrincipal(
+      browser.contentPrincipal,
+      "camera",
+      browser
+    );
+    Assert.equal(camState, SitePermissions.ALLOW);
+    Assert.equal(camScope, SitePermissions.SCOPE_PERSISTENT);
+
+    SitePermissions.removeFromPrincipal(
+      browser.contentPrincipal,
+      "camera",
+      browser
+    );
+    SitePermissions.removeFromPrincipal(
+      browser.contentPrincipal,
+      "microphone",
+      browser
+    );
+  });
+});
--- a/browser/base/content/test/webrtc/browser_stop_streams_on_indicator_close.js
+++ b/browser/base/content/test/webrtc/browser_stop_streams_on_indicator_close.js
@@ -4,47 +4,57 @@
 "use strict";
 
 const TEST_ROOT = getRootDirectory(gTestPath).replace(
   "chrome://mochitests/content/",
   "https://example.com/"
 );
 const TEST_PAGE = TEST_ROOT + "get_user_media.html";
 
-/**
- * Tests that if the indicator is closed somehow by the user when streams
- * still ongoing, that all of those streams are stopped, and the most recent
- * tab that a stream was shared with is selected.
- */
-add_task(async function test_close_indicator() {
+add_task(async function setup() {
   let prefs = [
     [PREF_PERMISSION_FAKE, true],
     [PREF_AUDIO_LOOPBACK, ""],
     [PREF_VIDEO_LOOPBACK, ""],
     [PREF_FAKE_STREAMS, true],
     [PREF_FOCUS_SOURCE, false],
   ];
   await SpecialPowers.pushPrefEnv({ set: prefs });
+});
+
+/**
+ * Tests that if the indicator is closed somehow by the user when streams
+ * still ongoing, that all of those streams it represents are stopped, and
+ * the most recent tab that a stream was shared with is selected.
+ *
+ * This test makes sure the global mute toggles for camera and microphone
+ * are disabled, so the indicator only represents display streams, and only
+ * those streams should be stopped on close.
+ */
+add_task(async function test_close_indicator_no_global_toggles() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["privacy.webrtc.globalMuteToggles", false]],
+  });
 
   let indicatorPromise = promiseIndicatorWindow();
 
   info("Opening first tab");
   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
   info("Sharing camera, microphone and screen");
-  await shareDevices(tab1.linkedBrowser, true, true, SHARE_SCREEN);
+  await shareDevices(tab1.linkedBrowser, true, true, SHARE_SCREEN, false);
 
   info("Opening second tab");
   let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
-  info("Sharing camera and screen");
-  await shareDevices(tab2.linkedBrowser, true, false, SHARE_SCREEN);
+  info("Sharing camera, microphone and screen");
+  await shareDevices(tab2.linkedBrowser, true, true, SHARE_SCREEN, true);
 
   info("Opening third tab");
   let tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
   info("Sharing screen");
-  await shareDevices(tab3.linkedBrowser, false, false, SHARE_SCREEN);
+  await shareDevices(tab3.linkedBrowser, false, false, SHARE_SCREEN, false);
 
   info("Opening fourth tab");
   let tab4 = await BrowserTestUtils.openNewForegroundTab(
     gBrowser,
     "https://example.com"
   );
 
   Assert.equal(
@@ -52,26 +62,154 @@ add_task(async function test_close_indic
     tab4,
     "Most recently opened tab is selected"
   );
 
   let indicator = await indicatorPromise;
 
   indicator.close();
 
-  await checkNotSharing();
+  // Wait a tick of the event loop to give the unload handler in the indicator
+  // a chance to run.
+  await new Promise(resolve => executeSoon(resolve));
 
+  // Make sure the media capture state has a chance to flush up to the parent.
+  await getMediaCaptureState();
+
+  // The camera and microphone streams should still be active.
+  let camStreams = webrtcUI.getActiveStreams(true, false);
+  Assert.equal(camStreams.length, 2, "Should have found two camera streams");
+  let micStreams = webrtcUI.getActiveStreams(false, true);
   Assert.equal(
-    webrtcUI.activePerms.size,
-    0,
-    "There shouldn't be any active stream permissions."
+    micStreams.length,
+    2,
+    "Should have found two microphone streams"
   );
 
+  // The camera and microphone permission were remembered for tab2, so check to
+  // make sure that the permissions remain.
+  let { state: camState, scope: camScope } = SitePermissions.getForPrincipal(
+    tab2.linkedBrowser.contentPrincipal,
+    "camera",
+    tab2.linkedBrowser
+  );
+  Assert.equal(camState, SitePermissions.ALLOW);
+  Assert.equal(camScope, SitePermissions.SCOPE_PERSISTENT);
+
+  let { state: micState, scope: micScope } = SitePermissions.getForPrincipal(
+    tab2.linkedBrowser.contentPrincipal,
+    "microphone",
+    tab2.linkedBrowser
+  );
+  Assert.equal(micState, SitePermissions.ALLOW);
+  Assert.equal(micScope, SitePermissions.SCOPE_PERSISTENT);
+
   Assert.equal(
     gBrowser.selectedTab,
     tab3,
     "Most recently tab that streams were shared with is selected"
   );
+
+  SitePermissions.removeFromPrincipal(
+    tab2.linkedBrowser.contentPrincipal,
+    "camera",
+    tab2.linkedBrowser
+  );
+
+  SitePermissions.removeFromPrincipal(
+    tab2.linkedBrowser.contentPrincipal,
+    "microphone",
+    tab2.linkedBrowser
+  );
+
   BrowserTestUtils.removeTab(tab1);
   BrowserTestUtils.removeTab(tab2);
   BrowserTestUtils.removeTab(tab3);
   BrowserTestUtils.removeTab(tab4);
 });
+
+/**
+ * Tests that if the indicator is closed somehow by the user when streams
+ * still ongoing, that all of those streams is represents are stopped, and
+ * the most recent tab that a stream was shared with is selected.
+ *
+ * This test makes sure the global mute toggles are enabled. This means that
+ * when the user manages to close the indicator, we should revoke camera
+ * and microphone permissions too.
+ */
+add_task(async function test_close_indicator_with_global_toggles() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["privacy.webrtc.globalMuteToggles", true]],
+  });
+
+  let indicatorPromise = promiseIndicatorWindow();
+
+  info("Opening first tab");
+  let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
+  info("Sharing camera, microphone and screen");
+  await shareDevices(tab1.linkedBrowser, true, true, SHARE_SCREEN, false);
+
+  info("Opening second tab");
+  let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
+  info("Sharing camera, microphone and screen");
+  await shareDevices(tab2.linkedBrowser, true, true, SHARE_SCREEN, true);
+
+  info("Opening third tab");
+  let tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
+  info("Sharing screen");
+  await shareDevices(tab3.linkedBrowser, false, false, SHARE_SCREEN, false);
+
+  info("Opening fourth tab");
+  let tab4 = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    "https://example.com"
+  );
+
+  Assert.equal(
+    gBrowser.selectedTab,
+    tab4,
+    "Most recently opened tab is selected"
+  );
+
+  let indicator = await indicatorPromise;
+
+  indicator.close();
+
+  // Wait a tick of the event loop to give the unload handler in the indicator
+  // a chance to run.
+  await new Promise(resolve => executeSoon(resolve));
+
+  Assert.deepEqual(
+    await getMediaCaptureState(),
+    {},
+    "expected nothing to be shared"
+  );
+
+  // Ensuring we no longer have any active streams.
+  let streams = webrtcUI.getActiveStreams(true, true, true, true);
+  Assert.equal(streams.length, 0, "Should have found no active streams");
+
+  // The camera and microphone permissions should have been cleared.
+  let { state: camState } = SitePermissions.getForPrincipal(
+    tab2.linkedBrowser.contentPrincipal,
+    "camera",
+    tab2.linkedBrowser
+  );
+  Assert.equal(camState, SitePermissions.UNKNOWN);
+
+  let { state: micState } = SitePermissions.getForPrincipal(
+    tab2.linkedBrowser.contentPrincipal,
+    "microphone",
+    tab2.linkedBrowser
+  );
+  Assert.equal(micState, SitePermissions.UNKNOWN);
+
+  Assert.equal(
+    gBrowser.selectedTab,
+    tab3,
+    "Most recently tab that streams were shared with is selected"
+  );
+
+  BrowserTestUtils.removeTab(tab1);
+  BrowserTestUtils.removeTab(tab2);
+  BrowserTestUtils.removeTab(tab3);
+  BrowserTestUtils.removeTab(tab4);
+});
--- a/browser/base/content/test/webrtc/browser_tab_switch_warning.js
+++ b/browser/base/content/test/webrtc/browser_tab_switch_warning.js
@@ -184,16 +184,20 @@ async function ensureWarning(tab) {
   Assert.equal(
     panel.anchorNode,
     tab,
     "Expected the warning to be anchored to the right tab."
   );
 }
 
 add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({
+    set: [["privacy.webrtc.sharedTabWarning", true]],
+  });
+
   // Loads up NEW_BACKGROUND_TABS_TO_OPEN background tabs at about:blank,
   // and waits until they're fully open.
   let uris = new Array(NEW_BACKGROUND_TABS_TO_OPEN).fill("about:blank");
 
   let loadPromises = Promise.all(
     uris.map(uri => BrowserTestUtils.waitForNewTab(gBrowser, uri, false, true))
   );
 
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -976,33 +976,48 @@ async function runTests(tests, options =
  * Given a browser from a tab in this window, chooses to share
  * some combination of camera, mic or screen.
  *
  * @param {<xul:browser} browser - The browser to share devices with.
  * @param {boolean} camera - True to share a camera device.
  * @param {boolean} mic - True to share a microphone device.
  * @param {Number} [screenOrWin] - One of either SHARE_WINDOW or SHARE_SCREEN
  *   to share a window or screen. Defaults to neither.
+ * @param {boolean} remember - True to persist the permission to the
+ *   SitePermissions database as SitePermissions.SCOPE_PERSISTENT. Note that
+ *   callers are responsible for clearing this persistent permission.
  * @return {Promise}
  * @resolves {undefined} - Once the sharing is complete.
  */
-async function shareDevices(browser, camera, mic, screenOrWin = 0) {
+async function shareDevices(
+  browser,
+  camera,
+  mic,
+  screenOrWin = 0,
+  remember = false
+) {
   if (camera || mic) {
     let promise = promisePopupNotificationShown(
       "webRTC-shareDevices",
       null,
       window
     );
 
     await promiseRequestDevice(mic, camera, null, null, browser);
     await promise;
 
     checkDeviceSelectors(mic, camera);
     let observerPromise1 = expectObserverCalled("getUserMedia:response:allow");
     let observerPromise2 = expectObserverCalled("recording-device-events");
+
+    let rememberCheck = PopupNotifications.panel.querySelector(
+      ".popup-notification-checkbox"
+    );
+    rememberCheck.checked = remember;
+
     promise = promiseMessage("ok", () => {
       PopupNotifications.panel.firstElementChild.button.click();
     });
 
     await observerPromise1;
     await observerPromise2;
     await promise;
   }
--- a/browser/base/content/webrtcIndicator.js
+++ b/browser/base/content/webrtcIndicator.js
@@ -346,16 +346,21 @@ const WebRTCIndicator = {
       case "popuphiding": {
         this.onPopupHiding(event);
         break;
       }
       case "command": {
         this.onCommand(event);
         break;
       }
+      case "DOMWindowClose":
+      case "close": {
+        this.onClose(event);
+        break;
+      }
     }
   },
 
   onLoad() {
     this.loaded = true;
 
     if (AppConstants.platform == "macosx" || AppConstants.platform == "win") {
       this.statusBar = Cc["@mozilla.org/widget/systemstatusbar;1"].getService(
@@ -364,16 +369,22 @@ const WebRTCIndicator = {
     }
 
     this.updateIndicatorState();
 
     window.addEventListener("click", this);
     window.addEventListener("change", this);
     window.addEventListener("sizemodechange", this);
 
+    // There are two ways that the dialog can close - either via the
+    // .close() window method, or via the OS. We handle both of those
+    // cases here.
+    window.addEventListener("DOMWindowClose", this);
+    window.addEventListener("close", this);
+
     if (this.statusBar) {
       // We only want these events for the system status bar menus.
       window.addEventListener("popupshowing", this);
       window.addEventListener("popuphiding", this);
       window.addEventListener("command", this);
     }
 
     window.windowRoot.addEventListener("MozUpdateWindowPos", this);
@@ -385,40 +396,68 @@ const WebRTCIndicator = {
       bubbles: true,
       cancelable: true,
     });
     document.documentElement.dispatchEvent(ev);
 
     this.loaded = true;
   },
 
+  onClose(event) {
+    // This event is fired from when the indicator window tries to be closed.
+    // If we preventDefault() the event, we are able to cancel that close
+    // attempt.
+    //
+    // We want to do that if we're not showing the global mute toggles
+    // and we're still sharing a camera or a microphone so that we can
+    // keep the status bar indicators present (since those status bar
+    // indicators are bound to this window).
+    if (
+      !this.showGlobalMuteToggles &&
+      (webrtcUI.showCameraIndicator || webrtcUI.showMicrophoneIndicator)
+    ) {
+      event.preventDefault();
+      this.setVisibility(false);
+    }
+
+    if (!this.isClosingInternally) {
+      // Something has tried to close the indicator, but it wasn't webrtcUI.
+      // This means we might still have some streams being shared. To protect
+      // the user from unknowingly sharing streams, we shut those streams
+      // down.
+      //
+      // This only includes the camera and microphone streams if the user
+      // has the global mute toggles enabled, since these toggles visually
+      // associate the indicator with those streams.
+      let activeStreams = webrtcUI.getActiveStreams(
+        this.showGlobalMuteToggles /* camera */,
+        this.showGlobalMuteToggles /* microphone */,
+        true /* screen */,
+        true /* window */
+      );
+      webrtcUI.stopSharingStreams(
+        activeStreams,
+        this.showGlobalMuteToggles /* camera */,
+        this.showGlobalMuteToggles /* microphone */,
+        true /* screen */,
+        true /* window */
+      );
+    }
+  },
+
   onUnload() {
     Services.ppmm.sharedData.set("WebRTC:GlobalCameraMute", false);
     Services.ppmm.sharedData.set("WebRTC:GlobalMicrophoneMute", false);
     Services.ppmm.sharedData.flush();
 
     if (this.statusBar) {
       for (let menu of this.statusBarMenus) {
         this.statusBar.removeItem(menu);
       }
     }
-
-    if (!this.isClosingInternally) {
-      // Something has closed the indicator, but it wasn't webrtcUI. This
-      // means we might still have some streams being shared. To protect
-      // the user from unknowingly sharing streams, we shut those streams
-      // down.
-      let activeStreams = webrtcUI.getActiveStreams(
-        true /* camera */,
-        true /* microphone */,
-        true /* screen */,
-        true /* window */
-      );
-      webrtcUI.stopSharingStreams(activeStreams);
-    }
   },
 
   onClick(event) {
     switch (event.target.id) {
       case "stop-sharing": {
         let activeStreams = webrtcUI.getActiveStreams(
           false /* camera */,
           false /* microphone */,
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -495,16 +495,19 @@ var webrtcUI = {
    * Given some set of streams, stops device access for those streams.
    * Optionally, it's possible to stop a subset of the devices on those
    * streams by passing in optional arguments.
    *
    * Once the streams have been stopped, this method will also find the
    * newest stream's <xul:browser> and window, focus the window, and
    * select the browser.
    *
+   * For camera and microphone streams, this will also revoke any associated
+   * persistent permissions from SitePermissions.
+   *
    * @param {Array<Object>} activeStreams - An array of streams obtained via webrtcUI.getActiveStreams.
    * @param {boolean} stopCameras - True to stop the camera streams (defaults to true)
    * @param {boolean} stopMics - True to stop the microphone streams (defaults to true)
    * @param {boolean} stopScreens - True to stop the screen streams (defaults to true)
    * @param {boolean} stopWindows - True to stop the window streams (defaults to true)
    */
   stopSharingStreams(
     activeStreams,
@@ -567,59 +570,64 @@ var webrtcUI = {
               scope: SitePermissions.SCOPE_REQUEST,
               sharingState: webrtcState[id],
             });
           }
         }
       }
 
       for (let permission of permissions) {
-        let windowId = tab._sharingState.webRTC.windowId;
+        if (clearRequested[permission.id]) {
+          let windowId = tab._sharingState.webRTC.windowId;
 
-        if (permission.id == "screen") {
-          windowId = `screen:${webrtcState.windowId}`;
-        } else if (permission.id == "camera" || permission.id == "microphone") {
-          // If we set persistent permissions or the sharing has
-          // started due to existing persistent permissions, we need
-          // to handle removing these even for frames with different hostnames.
-          let origins = browser.getDevicePermissionOrigins("webrtc");
-          for (let origin of origins) {
-            // It's not possible to stop sharing one of camera/microphone
-            // without the other.
-            let principal;
-            for (let id of ["camera", "microphone"]) {
-              if (webrtcState[id]) {
-                if (!principal) {
-                  principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
-                    origin
-                  );
-                }
-                let perm = SitePermissions.getForPrincipal(principal, id);
-                if (
-                  perm.state == SitePermissions.ALLOW &&
-                  perm.scope == SitePermissions.SCOPE_PERSISTENT
-                ) {
-                  SitePermissions.removeFromPrincipal(principal, id);
+          if (permission.id == "screen") {
+            windowId = `screen:${webrtcState.windowId}`;
+          } else if (
+            permission.id == "camera" ||
+            permission.id == "microphone"
+          ) {
+            // If we set persistent permissions or the sharing has
+            // started due to existing persistent permissions, we need
+            // to handle removing these even for frames with different hostnames.
+            let origins = browser.getDevicePermissionOrigins("webrtc");
+            for (let origin of origins) {
+              // It's not possible to stop sharing one of camera/microphone
+              // without the other.
+              let principal;
+              for (let id of ["camera", "microphone"]) {
+                if (webrtcState[id]) {
+                  if (!principal) {
+                    principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+                      origin
+                    );
+                  }
+                  let perm = SitePermissions.getForPrincipal(principal, id);
+                  if (
+                    perm.state == SitePermissions.ALLOW &&
+                    perm.scope == SitePermissions.SCOPE_PERSISTENT
+                  ) {
+                    SitePermissions.removeFromPrincipal(principal, id);
+                  }
                 }
               }
             }
           }
-        }
+
+          let bc = webrtcState.browsingContext;
+          bc.currentWindowGlobal
+            .getActor("WebRTC")
+            .sendAsyncMessage("webrtc:StopSharing", windowId);
+          webrtcUI.forgetActivePermissionsFromBrowser(browser);
 
-        let bc = webrtcState.browsingContext;
-        bc.currentWindowGlobal
-          .getActor("WebRTC")
-          .sendAsyncMessage("webrtc:StopSharing", windowId);
-        webrtcUI.forgetActivePermissionsFromBrowser(browser);
-
-        SitePermissions.removeFromPrincipal(
-          browser.contentPrincipal,
-          permission.id,
-          browser
-        );
+          SitePermissions.removeFromPrincipal(
+            browser.contentPrincipal,
+            permission.id,
+            browser
+          );
+        }
       }
     }
 
     let window = browserToSelect.ownerGlobal;
     let gBrowser = browserToSelect.getTabBrowser();
     let tab = gBrowser.getTabForBrowser(browserToSelect);
     window.focus();
     gBrowser.selectedTab = tab;