Bug 1580189 - Clear geolocation permission for sub-frames with active sharing state. r=johannh
authorPaul Zuehlcke <pzuhlcke@mozilla.com>
Wed, 25 Sep 2019 09:16:32 +0000
changeset 494889 e6f9d4a2f40c43052a582d0fcb427c02b864232a
parent 494888 bdd3e554925b0e8ca03f7ffade6cfe91a98f8ddc
child 494890 c01855ea7c2daabc3ded72eddd2fb3e3e0cc55a2
push id114131
push userdluca@mozilla.com
push dateThu, 26 Sep 2019 09:47:34 +0000
treeherdermozilla-inbound@1dc1a755079a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1580189
milestone71.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 1580189 - Clear geolocation permission for sub-frames with active sharing state. r=johannh Differential Revision: https://phabricator.services.mozilla.com/D45827
browser/base/content/browser-siteIdentity.js
browser/modules/PermissionUI.jsm
browser/modules/webrtcUI.jsm
toolkit/content/widgets/browser-custom-element.js
--- a/browser/base/content/browser-siteIdentity.js
+++ b/browser/base/content/browser-siteIdentity.js
@@ -1583,54 +1583,77 @@ var gIdentityHandler = {
     }
 
     let button = this._createPermissionClearButton(aPermission, container);
     container.appendChild(button);
 
     return container;
   },
 
+  _removePermPersistentAllow(principal, id) {
+    let perm = SitePermissions.getForPrincipal(principal, id);
+    if (
+      perm.state == SitePermissions.ALLOW &&
+      perm.scope == SitePermissions.SCOPE_PERSISTENT
+    ) {
+      SitePermissions.removeFromPrincipal(principal, id);
+    }
+  },
+
   _createPermissionClearButton(aPermission, container) {
     let button = document.createXULElement("button");
     button.setAttribute("class", "identity-popup-permission-remove-button");
     let tooltiptext = gNavigatorBundle.getString("permissions.remove.tooltip");
     button.setAttribute("tooltiptext", tooltiptext);
     button.addEventListener("command", () => {
       let browser = gBrowser.selectedBrowser;
       this._permissionList.removeChild(container);
-      if (
-        aPermission.sharingState &&
-        ["camera", "microphone", "screen"].includes(aPermission.id)
-      ) {
-        let windowId = this._sharingState.webRTC.windowId;
-        if (aPermission.id == "screen") {
-          windowId = "screen:" + windowId;
-        } else {
-          // 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 principals = browser._devicePermissionPrincipals || [];
-          for (let principal of principals) {
-            // It's not possible to stop sharing one of camera/microphone
-            // without the other.
-            for (let id of ["camera", "microphone"]) {
-              if (this._sharingState.webRTC[id]) {
-                let perm = SitePermissions.getForPrincipal(principal, id);
-                if (
-                  perm.state == SitePermissions.ALLOW &&
-                  perm.scope == SitePermissions.SCOPE_PERSISTENT
-                ) {
-                  SitePermissions.removeFromPrincipal(principal, id);
+      if (aPermission.sharingState) {
+        if (aPermission.id === "geo") {
+          let origins = browser.getDevicePermissionOrigins("geo");
+          for (let origin of origins) {
+            let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+              origin
+            );
+            this._removePermPersistentAllow(principal, aPermission.id);
+          }
+          origins.clear();
+        } else if (
+          ["camera", "microphone", "screen"].includes(aPermission.id)
+        ) {
+          let windowId = this._sharingState.webRTC.windowId;
+          if (aPermission.id == "screen") {
+            windowId = "screen:" + windowId;
+          } else {
+            // 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 (this._sharingState.webRTC[id]) {
+                  if (!principal) {
+                    principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+                      origin
+                    );
+                  }
+                  this._removePermPersistentAllow(principal, id);
                 }
               }
             }
           }
+          browser.messageManager.sendAsyncMessage(
+            "webrtc:StopSharing",
+            windowId
+          );
+          webrtcUI.forgetActivePermissionsFromBrowser(gBrowser.selectedBrowser);
         }
-        browser.messageManager.sendAsyncMessage("webrtc:StopSharing", windowId);
-        webrtcUI.forgetActivePermissionsFromBrowser(gBrowser.selectedBrowser);
       }
       SitePermissions.removeFromPrincipal(
         gBrowser.contentPrincipal,
         aPermission.id,
         browser
       );
 
       this._permissionReloadHint.removeAttribute("hidden");
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -813,19 +813,25 @@ GeolocationPermissionPrompt.prototype = 
   },
 
   _updateGeoSharing(state) {
     let gBrowser = this.browser.ownerGlobal.gBrowser;
     if (gBrowser == null) {
       return;
     }
     gBrowser.updateBrowserSharing(this.browser, { geo: state });
+
+    let devicePermOrigins = this.browser.getDevicePermissionOrigins("geo");
     if (!state) {
+      devicePermOrigins.delete(this.principal.origin);
       return;
     }
+    devicePermOrigins.add(this.principal.origin);
+
+    // Update last access timestamp
     let host;
     try {
       host = this.browser.currentURI.host;
     } catch (e) {
       return;
     }
     if (host == null || host == "") {
       return;
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -547,19 +547,17 @@ function checkRequestAllowed(aRequest, a
       allowedDevices.push((activeMic || audioDevices[0]).deviceIndex);
     }
 
     // Remember on which URIs we found persistent permissions so that we
     // can remove them if the user clicks 'Stop Sharing'. There's no
     // other way for the stop sharing code to know the hostnames of frames
     // using devices until bug 1066082 is fixed.
     let browser = aBrowser;
-    browser._devicePermissionPrincipals =
-      browser._devicePermissionPrincipals || [];
-    browser._devicePermissionPrincipals.push(aPrincipal);
+    browser.getDevicePermissionOrigins("webrtc").add(aPrincipal.origin);
 
     let camNeeded = !!videoDevices.length;
     let micNeeded = !!audioDevices.length;
     checkOSPermission(camNeeded, micNeeded).then(havePermission => {
       if (havePermission) {
         let mm = browser.messageManager;
         mm.sendAsyncMessage("webrtc:Allow", {
           callID: aRequest.callID,
@@ -1103,19 +1101,17 @@ function prompt(aBrowser, aRequest) {
         if (!allowedDevices.length) {
           denyRequest(notification.browser, aRequest);
           return;
         }
 
         if (remember) {
           // Remember on which URIs we set persistent permissions so that we
           // can remove them if the user clicks 'Stop Sharing'.
-          aBrowser._devicePermissionPrincipals =
-            aBrowser._devicePermissionPrincipals || [];
-          aBrowser._devicePermissionPrincipals.push(principal);
+          aBrowser.getDevicePermissionOrigins("webrtc").add(principal.origin);
         }
 
         let camNeeded = !!videoDevices.length;
         let micNeeded = !!audioDevices.length;
         let havePermission = await checkOSPermission(camNeeded, micNeeded);
         if (!havePermission) {
           denyRequestNoPermission(notification.browser, aRequest);
           return;
--- a/toolkit/content/widgets/browser-custom-element.js
+++ b/toolkit/content/widgets/browser-custom-element.js
@@ -2127,13 +2127,28 @@
 
     enterModalState() {
       this.sendMessageToActor("EnterModalState", {}, "BrowserElement", true);
     }
 
     leaveModalState() {
       this.sendMessageToActor("LeaveModalState", {}, "BrowserElement", true);
     }
+
+    getDevicePermissionOrigins(key) {
+      if (typeof key !== "string" || key.length === 0) {
+        throw new Error("Key must be non empty string.");
+      }
+      if (!this._devicePermissionOrigins) {
+        this._devicePermissionOrigins = new Map();
+      }
+      let origins = this._devicePermissionOrigins.get(key);
+      if (!origins) {
+        origins = new Set();
+        this._devicePermissionOrigins.set(key, origins);
+      }
+      return origins;
+    }
   }
 
   MozXULElement.implementCustomInterface(MozBrowser, [Ci.nsIBrowser]);
   customElements.define("browser", MozBrowser);
 }