Bug 1354682 Add legacy badges in about:addons
MozReview-Commit-ID: 3ch4lfApSIc
--- 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;