Bug 1459613 - Show extension name in PermissionUI / webrtcUI doorhangers and menus items. draft
authorLuca Greco <lgreco@mozilla.com>
Wed, 16 May 2018 19:32:42 +0200
changeset 811423 dd1f5a17ba7c28b28312edbf3efdc9d19eadf95f
parent 811302 9c7bb8874337c2d40aef3d9945b10490a5115188
push id114308
push userluca.greco@alcacoop.it
push dateWed, 27 Jun 2018 17:08:22 +0000
bugs1459613
milestone63.0a1
Bug 1459613 - Show extension name in PermissionUI / webrtcUI doorhangers and menus items. MozReview-Commit-ID: 2NbbsWkxDHm
browser/base/content/test/popupNotifications/browser_displayURI.js
browser/modules/PermissionUI.jsm
browser/modules/webrtcUI.jsm
--- a/browser/base/content/test/popupNotifications/browser_displayURI.js
+++ b/browser/base/content/test/popupNotifications/browser_displayURI.js
@@ -1,13 +1,13 @@
 /*
  * Make sure that the correct origin is shown for permission prompts.
  */
 
-async function check(contentTask) {
+async function check(contentTask, options = {}) {
   await BrowserTestUtils.withNewTab("https://test1.example.com/", async function(browser) {
     let popupShownPromise = waitForNotificationPanel();
     await ContentTask.spawn(browser, null, contentTask);
     let panel = await popupShownPromise;
     let notification = panel.children[0];
     let body = document.getAnonymousElementByAttribute(notification,
                                                        "class",
                                                        "popup-notification-body");
@@ -15,30 +15,65 @@ async function check(contentTask) {
   });
 
   let channel = NetUtil.newChannel({
     uri: getRootDirectory(gTestPath),
     loadUsingSystemPrincipal: true,
   });
   channel = channel.QueryInterface(Ci.nsIFileChannel);
 
-  return BrowserTestUtils.withNewTab(channel.file.path, async function(browser) {
+  await BrowserTestUtils.withNewTab(channel.file.path, async function(browser) {
     let popupShownPromise = waitForNotificationPanel();
     await ContentTask.spawn(browser, null, contentTask);
     let panel = await popupShownPromise;
     let notification = panel.children[0];
     let body = document.getAnonymousElementByAttribute(notification,
                                                        "class",
                                                        "popup-notification-body");
     if (notification.id == "geolocation-notification") {
       ok(body.innerHTML.includes("local file"), `file:// URIs should be displayed as local file.`);
     } else {
       ok(body.innerHTML.includes("Unknown origin"), "file:// URIs should be displayed as unknown origin.");
     }
   });
+
+  if (!options.skipOnExtension) {
+    // Test the scenario also on the extension page if not explicitly unsupported
+    // (e.g. an extension page can't be navigated on a blob URL).
+    let extension = ExtensionTestUtils.loadExtension({
+      manifest: {
+        name: "Test Extension Name",
+      },
+      background() {
+        let {browser} = this;
+        browser.test.sendMessage("extension-tab-url",
+                                 browser.extension.getURL("extension-tab-page.html"));
+      },
+      files: {
+        "extension-tab-page.html": `<!DOCTYPE html><html><body></body></html>`,
+      },
+    });
+
+    await extension.startup();
+    let extensionURI = await extension.awaitMessage("extension-tab-url");
+
+    await BrowserTestUtils.withNewTab(extensionURI, async function(browser) {
+      let popupShownPromise = waitForNotificationPanel();
+      await ContentTask.spawn(browser, null, contentTask);
+      let panel = await popupShownPromise;
+      let notification = panel.children[0];
+      let body = document.getAnonymousElementByAttribute(notification,
+                                                         "class",
+                                                         "popup-notification-body");
+      ok(body.innerHTML.includes("Test Extension Name"),
+         "Check the the extension name is present in the markup");
+    });
+
+    await extension.unload();
+  }
 }
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({set: [
     ["media.navigator.permission.fake", true],
     ["media.navigator.permission.force", true],
   ]});
 });
@@ -56,20 +91,20 @@ add_task(async function test_displayURI_
 });
 
 add_task(async function test_displayURI_geo_blob() {
   await check(async function() {
     let text = "<script>navigator.geolocation.getCurrentPosition(() => {})</script>";
     let blob = new Blob([text], {type: "text/html"});
     let url = content.URL.createObjectURL(blob);
     content.location.href = url;
-  });
+  }, {skipOnExtension: true});
 });
 
 add_task(async function test_displayURI_camera_blob() {
   await check(async function() {
     let text = "<script>navigator.mediaDevices.getUserMedia({video: true, fake: true})</script>";
     let blob = new Blob([text], {type: "text/html"});
     let url = content.URL.createObjectURL(blob);
     content.location.href = url;
-  });
+  }, {skipOnExtension: true});
 });
 
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -104,16 +104,30 @@ var PermissionPromptPrototype = {
    *
    * @return {nsIPrincipal}
    */
   get principal() {
     throw new Error("Not implemented.");
   },
 
   /**
+   * Provides the preferred name to use in the permission popups,
+   * based on the principal URI (the URI.hostPort for any URI scheme
+   * besides the moz-extension one which should default to the
+   * extension name).
+   */
+  get principalName() {
+    if (this.principal.addonPolicy) {
+      return this.principal.addonPolicy.name;
+    }
+
+    return this.principal.URI.hostPort;
+  },
+
+  /**
    * If the nsIPermissionManager is being queried and written
    * to for this permission request, set this to the key to be
    * used. If this is undefined, user permissions will not be
    * read from or written to.
    *
    * Note that if a permission is set, in any follow-up
    * prompting within the expiry window of that permission,
    * the prompt will be skipped and the allow or deny choice
@@ -422,17 +436,17 @@ GeolocationPermissionPrompt.prototype = 
     return "geo";
   },
 
   get popupOptions() {
     let pref = "browser.geolocation.warning.infoURL";
     let options = {
       learnMoreURL: Services.urlFormatter.formatURLPref(pref),
       displayURI: false,
-      name: this.principal.URI.hostPort,
+      name: this.principalName,
     };
 
     if (this.principal.URI.schemeIs("file")) {
       options.checkbox = { show: false };
     } else {
       // Don't offer "always remember" action in PB mode
       options.checkbox = {
         show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)
@@ -455,17 +469,17 @@ GeolocationPermissionPrompt.prototype = 
   },
 
   get message() {
     if (this.principal.URI.schemeIs("file")) {
       return gBrowserBundle.GetStringFromName("geolocation.shareWithFile3");
     }
 
     return gBrowserBundle.formatStringFromName("geolocation.shareWithSite3",
-                                                  ["<>"], 1);
+                                               ["<>"], 1);
   },
 
   get promptActions() {
     // We collect Telemetry data on Geolocation prompts and how users
     // respond to them. The probe keys are a bit verbose, so let's alias them.
     const SHARE_LOCATION =
       Ci.nsISecurityUITelemetry.WARNING_GEOLOCATION_REQUEST_SHARE_LOCATION;
     const ALWAYS_SHARE =
@@ -530,17 +544,17 @@ DesktopNotificationPermissionPrompt.prot
 
   get popupOptions() {
     let learnMoreURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
 
     return {
       learnMoreURL,
       displayURI: false,
-      name: this.principal.URI.hostPort,
+      name: this.principalName,
     };
   },
 
   get notificationID() {
     return "web-notifications";
   },
 
   get anchorID() {
@@ -612,17 +626,17 @@ PersistentStoragePermissionPrompt.protot
       checkbox.label = gBrowserBundle.GetStringFromName("persistentStorage.remember");
     }
     let learnMoreURL =
       Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
     return {
       checkbox,
       learnMoreURL,
       displayURI: false,
-      name: this.principal.URI.hostPort,
+      name: this.principalName,
     };
   },
 
   get notificationID() {
     return "persistent-storage";
   },
 
   get anchorID() {
@@ -679,17 +693,17 @@ MIDIPermissionPrompt.prototype = {
   get permissionKey() {
     return this.permName;
   },
 
   get popupOptions() {
     // TODO (bug 1433235) We need a security/permissions explanation URL for this
     let options = {
       displayURI: false,
-      name: this.principal.URI.hostPort,
+      name: this.principalName,
     };
 
     if (this.principal.URI.schemeIs("file")) {
       options.checkbox = { show: false };
     } else {
       // Don't offer "always remember" action in PB mode
       options.checkbox = {
         show: !PrivateBrowsingUtils.isWindowPrivate(this.browser.ownerGlobal)
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -295,24 +295,27 @@ var webrtcUI = {
 };
 
 function denyRequest(aBrowser, aRequest) {
   aBrowser.messageManager.sendAsyncMessage("webrtc:Deny",
                                            {callID: aRequest.callID,
                                             windowID: aRequest.windowID});
 }
 
-function getHost(uri, href) {
+function getHostOrExtensionName(uri, href) {
   let host;
   try {
     if (!uri) {
       uri = Services.io.newURI(href);
     }
-    host = uri.host;
+
+    let addonPolicy = WebExtensionPolicy.getByURI(uri);
+    host = addonPolicy ? addonPolicy.name : uri.host;
   } catch (ex) {}
+
   if (!host) {
     if (uri && uri.scheme.toLowerCase() == "about") {
       // For about URIs, just use the full spec, without any #hash parts.
       host = uri.specIgnoringRef;
     } else {
       // This is unfortunate, but we should display *something*...
       const kBundleURI = "chrome://browser/locale/browser.properties";
       let bundle = Services.strings.createBundle(kBundleURI);
@@ -414,17 +417,17 @@ function prompt(aBrowser, aRequest) {
                               SitePermissions.BLOCK, scope, notification.browser);
       }
     }
   ];
 
   let productName = gBrandBundle.GetStringFromName("brandShortName");
 
   let options = {
-    name: getHost(uri),
+    name: getHostOrExtensionName(uri),
     persistent: true,
     hideClose: !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton"),
     eventCallback(aTopic, aNewBrowser) {
       if (aTopic == "swapping")
         return true;
 
       let doc = this.browser.ownerDocument;
 
@@ -1002,17 +1005,17 @@ function onTabSharingMenuPopupShowing(e)
     if (types.microphone)
       stringName += "Microphone";
     if (types.screen)
       stringName += types.screen;
 
     let doc = e.target.ownerDocument;
     let bundle = doc.defaultView.gNavigatorBundle;
 
-    let origin = getHost(null, streamInfo.uri);
+    let origin = getHostOrExtensionName(null, streamInfo.uri);
     let menuitem = doc.createElement("menuitem");
     menuitem.setAttribute("label", bundle.getFormattedString(stringName, [origin]));
     menuitem.stream = streamInfo;
     menuitem.addEventListener("command", onTabSharingMenuPopupCommand);
     e.target.appendChild(menuitem);
   }
 }