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
/* global windowRoot */
const { XPCOMUtils } = ChromeUtils.importESModule(
);
const { formatUTMParams } = ChromeUtils.importESModule(
);
ChromeUtils.defineESModuleGetters(this, {
});
XPCOMUtils.defineLazyPreferenceGetter(
this,
"alternativeAddonSearchUrl",
"extensions.alternativeAddonSearch.url"
);
function getBrowserElement() {
return window.docShell.chromeEventHandler;
}
(async function () {
window.MozXULElement.insertFTLIfNeeded("branding/brand.ftl");
window.MozXULElement.insertFTLIfNeeded("toolkit/about/aboutAddons.ftl");
window.MozXULElement.insertFTLIfNeeded("messenger/aboutAddonsExtra.ftl");
// Needed for webext-perms-description-experiment-access.
window.MozXULElement.insertFTLIfNeeded("messenger/extensionPermissions.ftl");
UIFontSize.registerWindow(window);
// Consume clicks on a-tags and let openTrustedLinkIn() decide how to open them.
window.addEventListener("click", event => {
if (event.target.matches("a[href]") && event.target.href) {
const uri = Services.io.newURI(event.target.href);
if (uri.scheme == "http" || uri.scheme == "https") {
event.preventDefault();
event.stopPropagation();
windowRoot.documentGlobal.openTrustedLinkIn(event.target.href, "tab");
}
}
});
// Fix the "Search on addons.mozilla.org" placeholder text in the searchbox.
const input = document.querySelector("search-addons > moz-input-search");
document.l10n.setAttributes(input, "atn-addons-heading-search-input");
const button = document.querySelector("search-addons > moz-button");
document.l10n.setAttributes(button, "atn-addons-heading-search-button");
// Add our stylesheet.
const contentStylesheet = document.createElement("link");
contentStylesheet.rel = "stylesheet";
document.head.appendChild(contentStylesheet);
// Add logic to detect add-ons using the unsupported legacy API or suppressed
// Experiments.
const getMozillaAddonMessageInfo = window.getAddonMessageInfo;
window.getAddonMessageInfo = async function (
addon,
{ isCardExpanded, isInDisabledSection }
) {
const { name } = addon;
const data = await parseManifest(addon);
if (data.isSuppressedExperiment) {
return {
linkId: "add-on-learn-more-and-search-alternative-button-label",
linkUrl: `${alternativeAddonSearchUrl}?id=${encodeURIComponent(
addon.id
)}&q=${encodeURIComponent(name)}&isSuppressed=true`,
messageId: "details-notification-suppressed-esr",
messageArgs: { name },
type: "warning",
};
}
if (
addon.type == "extension" &&
(data.isLegacy ||
(!addon.isCompatible &&
(AddonManager.checkCompatibility ||
addon.blocklistState !== Ci.nsIBlocklistService.STATE_SOFTBLOCKED)))
) {
return {
linkId: "add-on-search-alternative-button-label",
linkUrl: `${alternativeAddonSearchUrl}?id=${encodeURIComponent(
addon.id
)}&q=${encodeURIComponent(name)}`,
messageId: "details-notification-incompatible2",
messageArgs: { name, version: Services.appinfo.version },
type: "warning",
};
}
return getMozillaAddonMessageInfo(addon, {
isCardExpanded,
isInDisabledSection,
});
};
document.querySelectorAll("addon-card").forEach(card => card.updateMessage());
// Override parts of the addon-card customElement to be able
// to add a dedicated button for extension preferences.
await customElements.whenDefined("addon-card");
const AddonCard = customElements.get("addon-card");
AddonCard.prototype.addOptionsButton = async function () {
const { addon, optionsButton } = this;
if (addon.type != "extension") {
return;
}
let addonOptionsButton = this.querySelector(".extension-options-button");
if (!addonOptionsButton) {
addonOptionsButton = document.createElement("button");
addonOptionsButton.classList.add("extension-options-button");
addonOptionsButton.setAttribute("action", "preferences");
document.l10n.setAttributes(addonOptionsButton, "add-on-options-button");
addonOptionsButton.disabled = true;
optionsButton.parentNode.insertBefore(addonOptionsButton, optionsButton);
}
// Upon fresh install the manifest has not been parsed and optionsType
// is not known, manually trigger parsing.
if (addon.isActive && !addon.optionsType) {
await parseManifest(addon);
}
addonOptionsButton.disabled = !(addon.isActive && addon.optionsType);
};
AddonCard.prototype._update = AddonCard.prototype.update;
AddonCard.prototype.update = function () {
this._update();
this.addOptionsButton();
};
// Override parts of the addon-permission-list customElement to be able
// to show the usage of Experiments in the permission list.
await customElements.whenDefined("addon-permissions-list");
const AddonPermissionsList = customElements.get("addon-permissions-list");
AddonPermissionsList.prototype.renderExperimentOnly = function () {
this.textContent = "";
const frag = AddonPermissionsList.fragment;
const section = frag.querySelector(".addon-permissions-required");
section.hidden = false;
const list = section.querySelector(".addon-permissions-list");
const item = document.createElement("li");
document.l10n.setAttributes(
item,
"webext-perms-description-experiment-access"
);
item.classList.add("permission-info", "permission-checked");
list.appendChild(item);
this.appendChild(frag);
};
// We change this function from sync to async, which does not matter.
// It calls this.render() which is async without awaiting it anyway.
AddonPermissionsList.prototype.setAddon = async function (addon) {
this.addon = addon;
const data = await parseManifest(addon);
if (data.isExperiment) {
this.renderExperimentOnly();
} else {
this.render();
}
};
await customElements.whenDefined("recommended-addon-card");
const RecommendedAddonCard = customElements.get("recommended-addon-card");
RecommendedAddonCard.prototype._setCardContent =
RecommendedAddonCard.prototype.setCardContent;
RecommendedAddonCard.prototype.setCardContent = function (card, addon) {
this._setCardContent(card, addon);
card.addEventListener("click", event => {
if (event.target.matches("a[href]") || event.target.matches("button")) {
return;
}
windowRoot.documentGlobal.openTrustedLinkIn(
card.querySelector(".disco-addon-author a").href,
"tab"
);
});
};
await customElements.whenDefined("search-addons");
const SearchAddons = customElements.get("search-addons");
SearchAddons.prototype.searchAddons = function (query) {
if (query.length === 0) {
return;
}
const url = new URL(
formatUTMParams(
"addons-manager-search",
AddonRepository.getSearchURL(query)
)
);
// Limit search to themes, if the themes section is currently active.
if (
document.getElementById("page-header").getAttribute("type") == "theme"
) {
url.searchParams.set("cat", "themes");
}
const browser = getBrowserElement();
const chromewin = browser.documentGlobal;
chromewin.openLinkIn(url.href, "tab", {
fromChrome: true,
triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal(
{}
),
});
};
})();