Source code

Revision control

Copy as Markdown

Other Tools

/* 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 {
AddonManagerListenerHandler,
DiscoveryAPI,
} from "../aboutaddons-utils.mjs";
const { AddonManager } = ChromeUtils.importESModule(
"resource://gre/modules/AddonManager.sys.mjs"
);
class RecommendedAddonList extends HTMLElement {
connectedCallback() {
if (this.isConnected) {
this.loadCardsIfNeeded();
this.updateCardsWithAddonManager();
}
AddonManagerListenerHandler.addListener(this);
}
disconnectedCallback() {
AddonManagerListenerHandler.removeListener(this);
}
get type() {
return this.getAttribute("type");
}
/**
* Set the add-on type for this list. This will be used to filter the add-ons
* that are displayed.
*
* Must be set prior to the first render.
*
* @param {string} val The type to filter on.
*/
set type(val) {
this.setAttribute("type", val);
}
get hideInstalled() {
return this.hasAttribute("hide-installed");
}
/**
* Set whether installed add-ons should be hidden from the list. If false,
* installed add-ons will be shown with a "Manage" button, otherwise they
* will be hidden.
*
* Must be set prior to the first render.
*
* @param {boolean} val Whether to show installed add-ons.
*/
set hideInstalled(val) {
this.toggleAttribute("hide-installed", val);
}
getCardById(addonId) {
for (let card of this.children) {
if (card.addonId === addonId) {
return card;
}
}
return null;
}
setAddonForCard(card, addon) {
card.setAddon(addon);
let wasHidden = card.hidden;
card.hidden = this.hideInstalled && addon;
if (wasHidden != card.hidden) {
let eventName = card.hidden ? "card-hidden" : "card-shown";
this.dispatchEvent(new CustomEvent(eventName, { detail: { card } }));
}
}
/**
* Whether the client ID should be preferred. This is disabled for themes
* since they don't use the telemetry data and don't show the TAAR notice.
*/
get preferClientId() {
return !this.type || this.type == "extension";
}
async updateCardsWithAddonManager() {
let cards = Array.from(this.children);
let addonIds = cards.map(card => card.addonId);
let addons = await AddonManager.getAddonsByIDs(addonIds);
for (let [i, card] of cards.entries()) {
let addon = addons[i];
this.setAddonForCard(card, addon);
if (addon) {
// Already installed, move card to end.
this.append(card);
}
}
}
async loadCardsIfNeeded() {
// Use promise as guard. Also used by tests to detect when load completes.
if (!this.cardsReady) {
this.cardsReady = this._loadCards();
}
return this.cardsReady;
}
async _loadCards() {
let recommendedAddons;
try {
recommendedAddons = await DiscoveryAPI.getResults(this.preferClientId);
} catch (e) {
return;
}
let frag = document.createDocumentFragment();
for (let addon of recommendedAddons) {
if (this.type && addon.type != this.type) {
continue;
}
let card = document.createElement("recommended-addon-card");
card.setDiscoAddon(addon);
frag.append(card);
}
this.append(frag);
await this.updateCardsWithAddonManager();
}
/**
* AddonManager listener events.
*/
onInstalled(addon) {
let card = this.getCardById(addon.id);
if (card) {
this.setAddonForCard(card, addon);
}
}
onUninstalled(addon) {
let card = this.getCardById(addon.id);
if (card) {
this.setAddonForCard(card, null);
}
}
}
customElements.define("recommended-addon-list", RecommendedAddonList);