Bug 1591755 - Support Web Permissions in FxR for Desktop r=Gijs,pbz
authorthomasmo <thomasmo@mozilla.com>
Thu, 14 Nov 2019 18:51:32 +0000
changeset 502015 3024dfdb5b4371e27276ee745a4c2200cf15a926
parent 502014 fb4b9875d44d472f27ea1df1066e1f359b28a053
child 502016 62bdc923737702055fba46dfd3bc7778cb005f39
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs, pbz
bugs1591755
milestone72.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 1591755 - Support Web Permissions in FxR for Desktop r=Gijs,pbz This change will be the first of multiple changes to control permissions in FxR on PC. This change introduces a new class, FxrPermissionPromptPrototype, in the FxR front end code. With the introduction of this class, all permission requests are denied by default. Subsequent changes will provide UI to give user control. Differential Revision: https://phabricator.services.mozilla.com/D52283
browser/components/BrowserGlue.jsm
browser/fxr/content/fxrui.html
browser/fxr/content/fxrui.js
browser/fxr/content/permissions.js
browser/fxr/jar.mn
browser/modules/webrtcUI.jsm
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -4100,16 +4100,24 @@ ContentPermissionPrompt.prototype = {
    *
    * Any time an error is thrown, the nsIContentPermissionRequest is
    * cancelled automatically.
    *
    * @param {nsIContentPermissionRequest} request
    *        The request that we're to show a prompt for.
    */
   prompt(request) {
+    if (request.element && request.element.fxrPermissionPrompt) {
+      // For Firefox Reality on Desktop, switch to a different mechanism to
+      // prompt the user since fewer permissions are available and since many
+      // UI dependencies are not availabe.
+      request.element.fxrPermissionPrompt(request);
+      return;
+    }
+
     let type;
     try {
       // Only allow exactly one permission request here.
       let types = request.types.QueryInterface(Ci.nsIArray);
       if (types.length != 1) {
         throw Components.Exception(
           "Expected an nsIContentPermissionRequest with only 1 type.",
           Cr.NS_ERROR_UNEXPECTED
--- a/browser/fxr/content/fxrui.html
+++ b/browser/fxr/content/fxrui.html
@@ -10,16 +10,17 @@
 <!-- 1280x720 chosen for default 16:9 ratio -->
 <html width="1280" height="720">
 <head>
   <title>Firefox Reality</title>
   <link rel="stylesheet" href="common.css" />
   <link rel="stylesheet" href="fxrui.css" />
   <link rel="stylesheet" href="fxrui_blue.css" />
   <script src="common.js"></script>
+  <script src="permissions.js"></script>
   <script src="fxrui.js"></script>
 </head>
 
 <body>
   <div id="eBrowserContainer" class="browser_container"></div>
 
   <div class="navbar_container">
     <button id="eBack"    class="icon_container icon_backward"></button>
--- a/browser/fxr/content/fxrui.js
+++ b/browser/fxr/content/fxrui.js
@@ -1,25 +1,32 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* import-globals-from common.js */
+/* import-globals-from permissions.js */
 
 // Configuration vars
 let homeURL = "https://webxr.today/";
 // Bug 1586294 - Localize the privacy policy URL (Services.urlFormatter?)
 let privacyPolicyURL = "https://www.mozilla.org/en-US/privacy/firefox/";
 let reportIssueURL = "https://mzl.la/fxr";
 let licenseURL =
   "https://mixedreality.mozilla.org/FirefoxRealityPC/license.html";
 
 // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/browser
 let browser = null;
+// Keep track of the current Permissions request to only allow one outstanding
+// request/prompt at a time.
+let currentPermissionRequest = null;
+// And, keep a queue of pending Permissions requests to resolve when the
+// current request finishes
+let pendingPermissionRequests = [];
 // The following variable map to UI elements whose behavior changes depending
 // on some state from the browser control
 let urlInput = null;
 let secureIcon = null;
 let backButton = null;
 let forwardButton = null;
 let refreshButton = null;
 let stopButton = null;
@@ -74,16 +81,20 @@ function setupBrowser() {
     browser.setAttribute("remote", "true");
     browser.classList.add("browser_instance");
     document.getElementById("eBrowserContainer").appendChild(browser);
 
     browser.loadUrlWithSystemPrincipal = function(url) {
       this.loadURI(url, { triggeringPrincipal: gSystemPrincipal });
     };
 
+    // Expose this function for Permissions to be used on this browser element
+    // in other parts of the frontend
+    browser.fxrPermissionPrompt = permissionPrompt;
+
     urlInput.value = homeURL;
     browser.loadUrlWithSystemPrincipal(homeURL);
 
     browser.addProgressListener(
       {
         QueryInterface: ChromeUtils.generateQI([
           Ci.nsIWebProgressListener,
           Ci.nsISupportsWeakReference,
@@ -198,16 +209,20 @@ function setupUrlBar() {
   });
 
   // Upon focus, highlight the whole URL
   urlInput.addEventListener("focus", function() {
     urlInput.select();
   });
 }
 
+//
+// Code to manage Settings UI
+//
+
 function openSettings() {
   let browserSettingsUI = document.createXULElement("browser");
   browserSettingsUI.setAttribute("type", "chrome");
   browserSettingsUI.classList.add("browser_settings");
 
   showModalContainer(browserSettingsUI);
 
   browserSettingsUI.loadURI("chrome://fxr/content/prefs.html", {
@@ -228,8 +243,40 @@ function showLicenseInfo() {
   closeSettings();
   browser.loadUrlWithSystemPrincipal(licenseURL);
 }
 
 function showReportIssue() {
   closeSettings();
   browser.loadUrlWithSystemPrincipal(reportIssueURL);
 }
+
+//
+// Code to manage Permissions UI
+//
+
+function permissionPrompt(aRequest) {
+  let newPrompt;
+  if (aRequest instanceof Ci.nsIContentPermissionRequest) {
+    newPrompt = new FxrContentPrompt(aRequest, this, finishPrompt);
+  } else {
+    newPrompt = new FxrWebRTCPrompt(aRequest, this, finishPrompt);
+  }
+
+  if (currentPermissionRequest) {
+    // There is already an outstanding request running. Cache this new request
+    // to be prompted later
+    pendingPermissionRequests.push(newPrompt);
+  } else {
+    currentPermissionRequest = newPrompt;
+    currentPermissionRequest.showPrompt();
+  }
+}
+
+function finishPrompt() {
+  if (pendingPermissionRequests.length) {
+    // Prompt the next request
+    currentPermissionRequest = pendingPermissionRequests.shift();
+    currentPermissionRequest.showPrompt();
+  } else {
+    currentPermissionRequest = null;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/fxr/content/permissions.js
@@ -0,0 +1,135 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from fxrui.js */
+
+/**
+ * Code to manage Permissions UI
+ *
+ * FxR on Desktop only supports granting permission for
+ * - Location
+ * - Camera
+ * - Microphone
+ * Any other permissions are automatically denied.
+ *
+ */
+
+// Base class for managing permissions in FxR on PC
+class FxrPermissionPromptPrototype {
+  constructor(aRequest, aBrowser, aCallback) {
+    this.request = aRequest;
+    this.targetBrowser = aBrowser;
+    this.responseCallback = aCallback;
+  }
+
+  showPrompt() {
+    // For now, all permissions default to denied. Testing for allow must be
+    // done manually until UI is finished:
+    // Bug 1594840 - Add UI for Web Permissions in FxR for Desktop
+    this.defaultDeny();
+  }
+
+  defaultDeny() {
+    this.handleResponse(false);
+  }
+
+  handleResponse(allowed) {
+    if (allowed) {
+      this.allow();
+    } else {
+      this.deny();
+    }
+
+    this.responseCallback();
+  }
+}
+
+// WebRTC-specific class implementation
+class FxrWebRTCPrompt extends FxrPermissionPromptPrototype {
+  showPrompt() {
+    for (let typeName of this.request.requestTypes) {
+      if (typeName !== "Microphone" && typeName !== "Camera") {
+        // Only Microphone and Camera requests are allowed. Automatically deny
+        // any other request.
+        this.defaultDeny();
+        return;
+      }
+    }
+
+    super.showPrompt();
+  }
+
+  allow() {
+    let { audioDevices, videoDevices } = this.request;
+
+    let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+      this.request.origin
+    );
+
+    // For now, collect the first audio and video device by default. User
+    // selection will be enabled later:
+    // Bug 1594841 - Add UI to select device for WebRTC in FxR for Desktop
+    let allowedDevices = [];
+
+    if (audioDevices.length) {
+      allowedDevices.push(audioDevices[0].deviceIndex);
+    }
+
+    if (videoDevices.length) {
+      Services.perms.addFromPrincipal(
+        principal,
+        "MediaManagerVideo",
+        Services.perms.ALLOW_ACTION,
+        Services.perms.EXPIRE_SESSION
+      );
+      allowedDevices.push(videoDevices[0].deviceIndex);
+    }
+
+    this.targetBrowser.messageManager.sendAsyncMessage("webrtc:Allow", {
+      callID: this.request.callID,
+      windowID: this.request.windowID,
+      devices: allowedDevices,
+    });
+  }
+
+  deny() {
+    this.targetBrowser.messageManager.sendAsyncMessage("webrtc:Deny", {
+      callID: this.request.callID,
+      windowID: this.request.windowID,
+    });
+  }
+}
+
+// Implementation for other, non-WebRTC web permission prompts
+class FxrContentPrompt extends FxrPermissionPromptPrototype {
+  showPrompt() {
+    // Only allow exactly one permission request here.
+    let types = this.request.types.QueryInterface(Ci.nsIArray);
+    if (types.length != 1) {
+      this.defaultDeny();
+      return;
+    }
+
+    // Only Location is supported from this type of request
+    let type = types.queryElementAt(0, Ci.nsIContentPermissionType).type;
+    if (type !== "geolocation") {
+      this.defaultDeny();
+      return;
+    }
+
+    // Override type so that it can be more easily interpreted by the code
+    // for the prompt.
+    type = "Location";
+    super.showPrompt();
+  }
+
+  allow() {
+    this.request.allow();
+  }
+
+  deny() {
+    this.request.cancel();
+  }
+}
--- a/browser/fxr/jar.mn
+++ b/browser/fxr/jar.mn
@@ -7,16 +7,17 @@ browser.jar:
 %  content fxr %content/browser/fxr/
    content/browser/fxr/common.css               (content/common.css)
    content/browser/fxr/common.js                (content/common.js)
    content/browser/fxr/fxrui.html               (content/fxrui.html)
    content/browser/fxr/fxrui.css                (content/fxrui.css)
    content/browser/fxr/fxrui_blue.css           (content/fxrui_blue.css)
    content/browser/fxr/fxrui.js                 (content/fxrui.js)
    content/browser/fxr/fxr-fullScreen.js        (content/fxr-fullScreen.js)
+   content/browser/fxr/permissions.js           (content/permissions.js)
    content/browser/fxr/prefs.html               (content/prefs.html)
    content/browser/fxr/prefs.css                (content/prefs.css)
    content/browser/fxr/prefs.js                 (content/prefs.js)
    
    content/browser/fxr/assets/icon-backward.svg         (content/assets/icon-backward.svg)
    content/browser/fxr/assets/icon-forward.svg          (content/assets/icon-forward.svg)
    content/browser/fxr/assets/icon-home.svg             (content/assets/icon-home.svg)
    content/browser/fxr/assets/icon-logo-settings-preview.png  (content/assets/icon-logo-settings-preview.png)
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -293,17 +293,24 @@ var webrtcUI = {
         let params = Object.freeze({
           origin: aMessage.target.contentPrincipal.origin,
           callID: aMessage.data,
         });
         this.emitter.emit("peer-request-cancel", params);
         break;
       }
       case "webrtc:Request":
-        prompt(aMessage.target, aMessage.data);
+        if (aMessage.target.fxrPermissionPrompt) {
+          // For Firefox Reality on Desktop, switch to a different mechanism to
+          // prompt the user since fewer permissions are available and since many
+          // UI dependencies are not availabe.
+          aMessage.target.fxrPermissionPrompt(aMessage.data);
+        } else {
+          prompt(aMessage.target, aMessage.data);
+        }
         break;
       case "webrtc:StopRecording":
         stopRecording(aMessage.target, aMessage.data);
         break;
       case "webrtc:CancelRequest":
         removePrompt(aMessage.target, aMessage.data);
         break;
       case "webrtc:UpdatingIndicators":