Bug 1354682 Add legacy badges in about:addons draft
authorAndrew Swan <aswan@mozilla.com>
Fri, 28 Apr 2017 16:11:39 -0700
changeset 570529 a84ef94dd437c7f121db7b1099e80eb234499441
parent 570528 c951faa31ccf429e30c1a2018f558450e19d8880
child 570530 27b3a451eccad9770482449d2767fe729801671d
push id56522
push useraswan@mozilla.com
push dateSat, 29 Apr 2017 05:44:25 +0000
bugs1354682
milestone55.0a1
Bug 1354682 Add legacy badges in about:addons MozReview-Commit-ID: 3ch4lfApSIc
toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
toolkit/mozapps/extensions/content/extensions.css
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/extensions.xml
toolkit/mozapps/extensions/content/extensions.xul
toolkit/mozapps/extensions/test/browser/browser.ini
toolkit/mozapps/extensions/test/browser/browser_legacy.js
toolkit/themes/shared/extensions/extensions.inc.css
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd
@@ -183,16 +183,20 @@
 <!ENTITY search.filter2.installed.tooltip     "Show installed add-ons">
 <!ENTITY search.filter2.available.label       "Available Add-ons">
 <!ENTITY search.filter2.available.tooltip     "Show add-ons available to install">
 
 <!ENTITY addon.homepage                       "Homepage">
 <!ENTITY addon.details.label                  "More">
 <!ENTITY addon.details.tooltip                "Show more details about this add-on">
 <!ENTITY addon.unknownDate                    "Unknown">
+<!-- LOCALIZATION NOTE (addon.legacy.label): This appears in a badge next
+     to the add-on name for extensions that are not webextensions, which
+     will stop working in Firefox 57. -->
+<!ENTITY addon.legacy.label                   "LEGACY">
 <!-- LOCALIZATION NOTE (addon.disabled.postfix): This is used in a normal list
      to signify that an add-on is disabled, in the form
      "<Addon name> <1.0> (disabled)" -->
 <!ENTITY addon.disabled.postfix               "(disabled)">
 <!-- LOCALIZATION NOTE (addon.update.postfix): This is used in the available
      updates list to signify that an item is an update, in the form
      "<Addon name> <1.1> Update". It is fine to use constructs like brackets if
      necessary -->
--- a/toolkit/mozapps/extensions/content/extensions.css
+++ b/toolkit/mozapps/extensions/content/extensions.css
@@ -152,22 +152,24 @@ setting[type="menulist"] {
 .addon:not([notification="warning"]) .warning,
 .addon:not([notification="error"]) .error,
 .addon:not([notification="info"]) .info,
 .addon:not([pending]) .pending,
 .addon:not([upgrade="true"]) .update-postfix,
 .addon[active="true"] .disabled-postfix,
 .addon[pending="install"] .update-postfix,
 .addon[pending="install"] .disabled-postfix,
+.addon[legacy="false"] .legacy-warning,
 #detail-view:not([notification="warning"]) .warning,
 #detail-view:not([notification="error"]) .error,
 #detail-view:not([notification="info"]) .info,
 #detail-view:not([pending]) .pending,
 #detail-view:not([upgrade="true"]) .update-postfix,
 #detail-view[active="true"] .disabled-postfix,
+#detail-view[legacy="false"] .legacy-warning,
 #detail-view[loading] .detail-view-container,
 #detail-view:not([loading]) .alert-container,
 .detail-row:not([value]),
 #search-list[remote="false"] #search-allresults-link {
   display: none;
 }
 
 #addons-page:not([warning]) #list-view > .global-warning-container {
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -51,16 +51,17 @@ XPCOMUtils.defineLazyPreferenceGetter(th
 const PREF_DISCOVERURL = "extensions.webservice.discoverURL";
 const PREF_DISCOVER_ENABLED = "extensions.getAddons.showPane";
 const PREF_XPI_ENABLED = "xpinstall.enabled";
 const PREF_MAXRESULTS = "extensions.getAddons.maxResults";
 const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
 const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
 const PREF_UI_TYPE_HIDDEN = "extensions.ui.%TYPE%.hidden";
 const PREF_UI_LASTCATEGORY = "extensions.ui.lastCategory";
+const PREF_LEGACY_EXCEPTIONS = "extensions.legacy.exceptions";
 
 const LOADING_MSG_DELAY = 100;
 
 const SEARCH_SCORE_MULTIPLIER_NAME = 2;
 const SEARCH_SCORE_MULTIPLIER_DESCRIPTION = 2;
 
 // Use integers so search scores are sortable by nsIXULSortService
 const SEARCH_SCORE_MATCH_WHOLEWORD = 10;
@@ -99,16 +100,20 @@ XPCOMUtils.defineLazyGetter(gStrings, "a
 XPCOMUtils.defineLazyGetter(this, "gInlineOptionsStylesheets", () => {
   let stylesheets = ["chrome://browser/content/extension.css"];
   if (AppConstants.platform === "macosx") {
     stylesheets.push("chrome://browser/content/extension-mac.css");
   }
   return stylesheets;
 });
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "legacyWarningExceptions",
+                                      PREF_LEGACY_EXCEPTIONS, "",
+                                      raw => raw.split(","));
+
 document.addEventListener("load", initialize, true);
 window.addEventListener("unload", shutdown);
 
 class MessageDispatcher {
   constructor(target) {
     this.listeners = new Map();
     this.target = target;
   }
@@ -3054,16 +3059,42 @@ var gDetailView = {
     this.clearLoading();
 
     this._addon = aAddon;
     gEventManager.registerAddonListener(this, aAddon.id);
     gEventManager.registerInstallListener(this);
 
     this.node.setAttribute("type", aAddon.type);
 
+    let legacy = false;
+    if (!aAddon.install) {
+      if (aAddon.type == "extension" && !aAddon.isWebExtension) {
+        legacy = true;
+      }
+      if (aAddon.type == "theme") {
+        // The logic here is kind of clunky but we want to mark complete
+        // themes as legacy.  There's no explicit flag for complete
+        // themes so explicitly check for new style themes (for which
+        // isWebExtension is true) or lightweight themes (which have
+        // ids that end with @personas.mozilla.org)
+        legacy = !(aAddon.isWebExtension || aAddon.id.endsWith("@personas.mozilla.org"));
+      }
+
+      if (legacy && aAddon.signedStatus == AddonManager.SIGNEDSTATE_PRIVILEGED) {
+        legacy = false;
+      }
+
+      // Exceptions that can slip through above: the default theme plus
+      // test pilot addons until we get SIGNEDSTATE_PRIVILEGED deployed.
+      if (legacy && legacyWarningExceptions.includes(aAddon.id)) {
+        legacy = false;
+      }
+    }
+    this.node.setAttribute("legacy", legacy);
+
     // If the search category isn't selected then make sure to select the
     // correct category
     if (gCategories.selected != "addons://search/")
       gCategories.select("addons://list/" + aAddon.type);
 
     document.getElementById("detail-name").textContent = aAddon.name;
     var icon = AddonManager.getPreferredIconURL(aAddon, 64, window);
     document.getElementById("detail-icon").src = icon ? icon : "";
--- a/toolkit/mozapps/extensions/content/extensions.xml
+++ b/toolkit/mozapps/extensions/content/extensions.xml
@@ -743,16 +743,48 @@
     </implementation>
   </binding>
 
 
   <!-- Addon - base - parent binding of any item representing an addon. -->
   <binding id="addon-base"
            extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
     <implementation>
+      <property name="isLegacy" readonly="true">
+        <getter><![CDATA[
+          if (this.mAddon.install) {
+            return false;
+          }
+          let legacy = false;
+          if (this.mAddon.type == "extension" && !this.mAddon.isWebExtension) {
+            legacy = true;
+          }
+          if (this.mAddon.type == "theme") {
+            // The logic here is kind of clunky but we want to mark complete
+            // themes as legacy.  There's no explicit flag for complete
+            // themes so explicitly check for new style themes (for which
+            // isWebExtension is true) or lightweight themes (which have
+            // ids that end with @personas.mozilla.org)
+            legacy = !(this.mAddon.isWebExtension ||
+                       this.mAddon.id.endsWith("@personas.mozilla.org"));
+          }
+
+          if (legacy && this.mAddon.signedStatus == AddonManager.SIGNEDSTATE_PRIVILEGED) {
+            legacy = false;
+          }
+
+          // Exceptions that can slip through above: the default theme plus
+          // test pilot addons until we get SIGNEDSTATE_PRIVILEGED deployed.
+          if (legacy && legacyWarningExceptions.includes(this.mAddon.id)) {
+            legacy = false;
+          }
+          return legacy;
+        ]]></getter>
+      </property>
+
       <method name="hasPermission">
         <parameter name="aPerm"/>
         <body><![CDATA[
           var perm = AddonManager["PERM_CAN_" + aPerm.toUpperCase()];
           return !!(this.mAddon.permissions & perm);
         ]]></body>
       </method>
 
@@ -828,16 +860,17 @@
         <xul:vbox class="icon-container">
           <xul:image anonid="icon" class="icon"/>
         </xul:vbox>
         <xul:vbox class="content-inner-container" flex="1">
           <xul:hbox class="basicinfo-container">
               <xul:hbox class="name-container">
                 <xul:label anonid="name" class="name" crop="end" flex="1"
                            tooltip="addonitem-tooltip" xbl:inherits="value=name"/>
+                <xul:label anonid="legacy" class="legacy-warning" value="&addon.legacy.label;"/>
                 <xul:label class="disabled-postfix" value="&addon.disabled.postfix;"/>
                 <xul:label class="update-postfix" value="&addon.update.postfix;"/>
                 <xul:spacer flex="5000"/> <!-- Necessary to make the name crop -->
               </xul:hbox>
             <xul:label anonid="date-updated" class="date-updated"
                        unknown="&addon.unknownDate;"/>
           </xul:hbox>
           <xul:hbox class="experiment-container">
@@ -1149,16 +1182,18 @@
           else
             this._icon.src = "";
 
           if (this.mAddon.description)
             this._description.value = this.mAddon.description;
           else
             this._description.hidden = true;
 
+          this.setAttribute("legacy", this.isLegacy);
+
           if (!("applyBackgroundUpdates" in this.mAddon) ||
               (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE ||
                (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DEFAULT &&
                 !AddonManager.autoUpdateDefault))) {
             AddonManager.getAllInstalls(aInstallsList => {
               // This can return after the binding has been destroyed,
               // so try to detect that and return early
               if (!("onNewInstall" in this))
--- a/toolkit/mozapps/extensions/content/extensions.xul
+++ b/toolkit/mozapps/extensions/content/extensions.xul
@@ -562,16 +562,17 @@
                       <image id="detail-icon" class="icon"/>
                     </vbox>
                     <vbox flex="1">
                       <vbox id="detail-summary">
                         <hbox id="detail-name-container" class="name-container"
                               align="start">
                           <label id="detail-name" flex="1"/>
                           <label id="detail-version"/>
+                          <label class="legacy-warning" value="&addon.legacy.label;"/>
                           <label class="disabled-postfix" value="&addon.disabled.postfix;"/>
                           <label class="update-postfix" value="&addon.update.postfix;"/>
                           <spacer flex="5000"/> <!-- Necessary to allow the name to wrap -->
                         </hbox>
                         <label id="detail-creator" class="creator"/>
                       </vbox>
                       <hbox id="detail-experiment-container">
                         <svg width="8" height="8" viewBox="0 0 8 8" version="1.1"
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -59,16 +59,17 @@ support-files =
 skip-if = buildapp == 'mulet'
 [browser_getmorethemes.js]
 [browser_gmpProvider.js]
 [browser_hotfix.js]
 # Verifies the old style of signing hotfixes
 skip-if = require_signing
 [browser_install.js]
 [browser_installssl.js]
+[browser_legacy.js]
 [browser_newaddon.js]
 [browser_non_mpc.js]
 [browser_searching.js]
 [browser_system_addons_are_e10s.js]
 [browser_task_next_test.js]
 [browser_update.js]
 [browser_updatessl.js]
 [browser_webapi.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_legacy.js
@@ -0,0 +1,93 @@
+
+add_task(async function() {
+  const NAMES = {
+    fullTheme: "Full Theme",
+    newTheme: "New LWT",
+    legacy: "Legacy Extension",
+    webextension: "WebExtension",
+    dictionary: "Dictionary",
+    langpack: "Language Pack",
+  };
+  let addons = [
+    {
+      id: "full-theme@tests.mozilla.org",
+      name: NAMES.fullTheme,
+      type: "theme",
+      isWebExtension: false,
+    },
+    {
+      id: "new-theme@tests.mozilla.org",
+      name: NAMES.newTheme,
+      type: "theme",
+      isWebExtension: true,
+    },
+    {
+      id: "legacy@tests.mozilla.org",
+      name: NAMES.legacy,
+      type: "extension",
+      isWebExtension: false,
+    },
+    {
+      id: "webextension@tests.mozilla.org",
+      name: NAMES.webextension,
+      type: "extension",
+      isWebExtension: true,
+    },
+    {
+      id: "dictionary@tests.mozilla.org",
+      name: NAMES.dictionary,
+      type: "dictionary",
+    },
+  ];
+
+  let provider = new MockProvider();
+  provider.createAddons(addons);
+
+  let mgrWin = await open_manager(null);
+  let catUtils = new CategoryUtilities(mgrWin);
+
+  async function check(category, name, isLegacy) {
+    await catUtils.openType(category);
+
+    let document = mgrWin.document;
+    // First find the  entry in the list.
+    let item = Array.from(document.getElementById("addon-list").childNodes)
+                    .find(i => i.getAttribute("name") == name);
+
+    ok(item, `Found ${name} in list`);
+    item.parentNode.ensureElementIsVisible(item);
+
+    // Check the badge
+    let badge = document.getAnonymousElementByAttribute(item, "class", "legacy-warning");
+
+    if (isLegacy) {
+      is_element_visible(badge, `Legacy badge is visible for ${name}`);
+    } else {
+      is_element_hidden(badge, `Legacy badge is hidden for ${name}`);
+    }
+
+    // Click down to the details page.
+    let detailsButton = document.getAnonymousElementByAttribute(item, "anonid", "details-btn");
+    EventUtils.synthesizeMouseAtCenter(detailsButton, {}, mgrWin);
+    await new Promise(resolve => wait_for_view_load(mgrWin, resolve));
+
+    // And check the badge
+    let elements = document.getElementsByClassName("legacy-warning");
+    is(elements.length, 1, "Found the legacy-warning element");
+    badge = elements[0];
+
+    if (isLegacy) {
+      is_element_visible(badge, `Legacy badge is visible for ${name}`);
+    } else {
+      is_element_hidden(badge, `Legacy badge is hidden for ${name}`);
+    }
+  }
+
+  await check("theme", NAMES.fullTheme, true);
+  await check("theme", NAMES.newTheme, false);
+  await check("extension", NAMES.legacy, true);
+  await check("extension", NAMES.webextension, false);
+  await check("dictionary", NAMES.dictionary, false);
+
+  await close_manager(mgrWin);
+});
--- a/toolkit/themes/shared/extensions/extensions.inc.css
+++ b/toolkit/themes/shared/extensions/extensions.inc.css
@@ -452,16 +452,28 @@ button.warning {
 
 .name-container {
   font-size: 1.3rem;
   font-weight: bold;
   -moz-box-align: end;
   -moz-box-flex: 1;
 }
 
+.legacy-warning {
+  background-color: #FFE900;
+  color: #3E2800;
+  padding: 4px 5px 3px;
+  font-size: 0.9rem;
+  font-weight: 600;
+}
+
+#detail-view .legacy-warning {
+  margin-top: 0.78rem;
+}
+
 .creator {
   font-weight: bold;
 }
 
 .description-container {
   margin-inline-start: 6px;
   -moz-box-align: center;
   font-size: 1.25rem;