bug 1335697 Display permissions and post-install notifications from about:addons search draft
authorAndrew Swan <aswan@mozilla.com>
Tue, 07 Feb 2017 20:22:26 -0800
changeset 486214 09942354528a9738b9ea2f323089fdc90c1ebc97
parent 485446 bf6b9caab2c7eb3ebc642afd82bc19598829c6f3
child 546185 5242d60fbb69672252ce25fb9b9c8682488dffac
push id45917
push useraswan@mozilla.com
push dateFri, 17 Feb 2017 16:51:26 +0000
bugs1335697
milestone54.0a1
bug 1335697 Display permissions and post-install notifications from about:addons search MozReview-Commit-ID: IqBoudbO7Vd
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_extension_permissions.js
browser/base/content/test/general/browser_webext_search.xml
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -79,16 +79,17 @@ support-files =
   browser_webext_nopermissions.xpi
   browser_webext_update1.xpi
   browser_webext_update2.xpi
   browser_webext_update_icon1.xpi
   browser_webext_update_icon2.xpi
   browser_webext_update_perms1.xpi
   browser_webext_update_perms2.xpi
   browser_webext_update.json
+  browser_webext_search.xml
   !/image/test/mochitest/blue.png
   !/toolkit/content/tests/browser/common/mockTransfer.js
   !/toolkit/modules/tests/browser/metadata_*.html
   !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/installtrigger.html
   !/toolkit/mozapps/extensions/test/xpinstall/redirect.sjs
--- a/browser/base/content/test/general/browser_extension_permissions.js
+++ b/browser/base/content/test/general/browser_extension_permissions.js
@@ -1,31 +1,41 @@
 "use strict";
 
+// See but 1340586 for proposal to reorganize permissions tests to
+// get rid of this...
+requestLongerTimeout(2);
+
 const BASE = getRootDirectory(gTestPath)
   .replace("chrome://mochitests/content/", "https://example.com/");
 
 const INSTALL_PAGE = `${BASE}/file_install_extensions.html`;
 const PERMS_XPI = "browser_webext_permissions.xpi";
 const NO_PERMS_XPI = "browser_webext_nopermissions.xpi";
 const ID = "permissions@test.mozilla.org";
 
-const DEFAULT_EXTENSION_ICON = "chrome://browser/content/extension.svg";
-
 Services.perms.add(makeURI("https://example.com/"), "install",
                    Services.perms.ALLOW_ACTION);
 
 registerCleanupFunction(async function() {
   let addon = await AddonManager.getAddonByID(ID);
   if (addon) {
     ok(false, `Addon ${ID} was still installed at the end of the test`);
     addon.uninstall();
   }
 });
 
+function isDefaultIcon(icon) {
+  // These are basically the same icon, but code within webextensions
+  // generates references to the former and generic add-ons manager code
+  // generates referces to the latter.
+  return (icon == "chrome://browser/content/extension.svg" ||
+          icon == "chrome://mozapps/skin/extensions/extensionGeneric.svg");
+}
+
 function promisePopupNotificationShown(name) {
   return new Promise(resolve => {
     function popupshown() {
       let notification = PopupNotifications.getNotification(name);
       if (!notification) { return; }
 
       ok(notification, `${name} notification shown`);
       ok(PopupNotifications.isPanelOpen, "notification panel open");
@@ -51,17 +61,17 @@ function checkNotification(panel, filena
     ok(icon.startsWith("jar:file://"), "Icon is a jar url");
     ok(icon.endsWith("/icon.png"), "Icon is icon.png inside a jar");
 
     is(header.getAttribute("hidden"), "", "Permission list header is visible");
     is(ul.childElementCount, 4, "Permissions list has 4 entries");
     // Real checking of the contents here is deferred until bug 1316996 lands
   } else if (filename == NO_PERMS_XPI) {
     // This extension has no icon, it should have the default
-    is(icon, DEFAULT_EXTENSION_ICON, "Icon is the default extension icon");
+    ok(isDefaultIcon(icon), "Icon is the default extension icon");
 
     is(header.getAttribute("hidden"), "true", "Permission list header is hidden");
     is(ul.childElementCount, 0, "Permissions list has 0 entries");
   }
 }
 
 // Navigate the current tab to the given url and return a Promise
 // that resolves when the page is loaded.
@@ -101,16 +111,55 @@ const INSTALL_FUNCTIONS = [
 
     await BrowserOpenAddonsMgr("addons://list/extension");
     let contentWin = gBrowser.selectedTab.linkedBrowser.contentWindow;
 
     // Do the install...
     contentWin.gViewController.doCommand("cmd_installFromFile");
     MockFilePicker.cleanup();
   },
+
+  async function installSearch(filename) {
+    await SpecialPowers.pushPrefEnv({set: [
+      ["extensions.getAddons.maxResults", 10],
+      ["extensions.getAddons.search.url", `${BASE}/browser_webext_search.xml`],
+    ]});
+
+    let win = await BrowserOpenAddonsMgr("addons://list/extension");
+
+    let searchResultsPromise = new Promise(resolve => {
+      win.document.addEventListener("ViewChanged", resolve, {once: true});
+    });
+    let search = win.document.getElementById("header-search");
+    search.focus();
+    search.value = "search text";
+    EventUtils.synthesizeKey("VK_RETURN", {}, win);
+
+    await searchResultsPromise;
+    ok(win.gViewController.currentViewId.startsWith("addons://search"),
+       "about:addons is displaying search results");
+
+    let list = win.document.getElementById("search-list");
+    let item = null;
+    for (let child of list.childNodes) {
+      if (child.nodeName == "richlistitem" &&
+          child.mAddon.install.sourceURI.path.endsWith(filename)) {
+            item = child;
+            break;
+      }
+    }
+    ok(item, `Found ${filename} in search results`);
+
+    // abracadabara XBL
+    item.clientTop;
+
+    let install = win.document.getAnonymousElementByAttribute(item, "anonid", "install-status");
+    let button = win.document.getAnonymousElementByAttribute(install, "anonid", "install-remote-btn");
+    EventUtils.synthesizeMouseAtCenter(button, {}, win);
+  },
 ];
 
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [
     ["extensions.webapi.testing", true],
     ["extensions.install.requireBuiltInCerts", false],
 
     // XXX remove this when prompts are enabled by default
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_webext_search.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="2">
+  <addon>
+    <name>permissions test</name>
+    <type id='1'>Extension</type>
+    <guid>permissions@tests.mozilla.org</guid>
+    <version>1.1</version>
+    <authors>
+      <author>
+        <name>Test Creator</name>
+        <link>http://example.com/creator.html</link>
+      </author>
+    </authors>
+    <status id='4'>Public</status>
+    <compatible_applications>
+      <application>
+        <name>Firefox</name>
+        <appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
+        <min_version>0</min_version>
+        <max_version>*</max_version>
+      </application>
+    </compatible_applications>
+    <compatible_os>ALL</compatible_os>
+    <install size="1">https://example.com/browser/browser/base/content/test/general/browser_webext_permissions.xpi</install>
+  </addon>
+
+  <addon>
+    <name>no permissions</name>
+    <type id='1'>Extension</type>
+    <guid>nopermissions@tests.mozilla.org</guid>
+    <version>1.0</version>
+    <authors>
+      <author>
+        <name>Test Creator</name>
+        <link>http://example.com/creator.html</link>
+      </author>
+    </authors>
+    <status id='4'>Public</status>
+    <compatible_applications>
+      <application>
+        <name>Firefox</name>
+        <appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
+        <min_version>0</min_version>
+        <max_version>*</max_version>
+      </application>
+    </compatible_applications>
+    <compatible_os>ALL</compatible_os>
+    <install size="1">https://example.com/browser/browser/base/content/test/general/browser_webext_nopermissions.xpi</install>
+  </addon>
+</searchresults>
+
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -2710,16 +2710,37 @@ var gSearchView = {
   onDownloadCancelled(aInstall) {
     this.removeInstall(aInstall);
   },
 
   onInstallCancelled(aInstall) {
     this.removeInstall(aInstall);
   },
 
+  onInstallEnded(aInstall) {
+    // If this is a webextension that was installed from this page,
+    // display the post-install notification.
+    if (!WEBEXT_PERMISSION_PROMPTS || !aInstall.addon.isWebExtension) {
+      return;
+    }
+
+    for (let item of this._listBox.childNodes) {
+      if (item.mInstall == aInstall) {
+        let subject = {
+          wrappedJSObject: {
+            target: getBrowserElement(),
+            addon: aInstall.addon,
+          },
+        };
+        Services.obs.notifyObservers(subject, "webextension-install-notify", null);
+        return;
+      }
+    }
+  },
+
   removeInstall(aInstall) {
     for (let item of this._listBox.childNodes) {
       if (item.mInstall == aInstall) {
         this._listBox.removeChild(item);
         return;
       }
     }
   },
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -648,16 +648,37 @@
                               "chrome,dialog,modal,centerscreen,resizable=no", data);
             if (!data.accepted)
               return;
           }
 
           delete this.mControl.mAddon;
           this.mControl.mInstall = this.mInstall;
           this.mControl.setAttribute("status", "installing");
+          let prompt = false;
+          try {
+            prompt = Services.prefs.getBoolPref("extensions.webextPermissionPrompts");
+          } catch (err) {}
+          if (prompt) {
+            this.mInstall.promptHandler = info => new Promise((resolve, reject) => {
+              let subject = {
+                wrappedJSObject: {
+                  target: window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                .getInterface(Ci.nsIDocShell).chromeEventHandler,
+                  info: {
+                    addon: info.addon,
+                    icon: info.addon.iconURL,
+                    resolve,
+                    reject,
+                  },
+                },
+              };
+              Services.obs.notifyObservers(subject, "webextension-permission-prompt", null);
+            });
+          }
           this.mInstall.install();
         ]]></body>
       </method>
 
       <method name="undoAction">
         <body><![CDATA[
           if (!this.mAddon)
             return;