diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /toolkit/mozapps/extensions/content | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/mozapps/extensions/content')
7 files changed, 11 insertions, 1631 deletions
diff --git a/toolkit/mozapps/extensions/content/aboutaddons.css b/toolkit/mozapps/extensions/content/aboutaddons.css index 2f5157bea3..bb0bf04dda 100644 --- a/toolkit/mozapps/extensions/content/aboutaddons.css +++ b/toolkit/mozapps/extensions/content/aboutaddons.css @@ -185,8 +185,7 @@ search-addons > search-textbox { max-width: var(--section-width); } -global-warnings, -#abuse-reports-messages { +global-warnings { margin-inline-start: var(--main-margin-start); max-width: var(--section-width); } @@ -645,7 +644,7 @@ panel-item[action="report"]::part(button) { .addon-inline-options { width: 100%; - background-color: white; + background-color: Canvas; margin-block: 4px; /* * Makes sure the browser minimal height is going to be the same as when @@ -675,6 +674,7 @@ addon-permissions-list > .addon-detail-row { background-position: 0 center; background-size: 1.6rem 1.6rem; background-repeat: no-repeat; + word-break: break-all; } .addon-permissions-list > li:dir(rtl) { diff --git a/toolkit/mozapps/extensions/content/aboutaddons.html b/toolkit/mozapps/extensions/content/aboutaddons.html index d0930ef42d..b48c219c05 100644 --- a/toolkit/mozapps/extensions/content/aboutaddons.html +++ b/toolkit/mozapps/extensions/content/aboutaddons.html @@ -30,7 +30,6 @@ <link rel="localization" href="branding/brand.ftl" /> <link rel="localization" href="toolkit/about/aboutAddons.ftl" /> - <link rel="localization" href="toolkit/about/abuseReports.ftl" /> <!-- Defer scripts so all the templates are loaded by the time they run. --> <script @@ -163,13 +162,6 @@ ></addon-page-header> <addon-page-options id="page-options"></addon-page-options> - <message-bar-stack - id="abuse-reports-messages" - reverse - max-message-bar-count="3" - > - </message-bar-stack> - <div id="main"></div> </div> </div> diff --git a/toolkit/mozapps/extensions/content/aboutaddonsCommon.js b/toolkit/mozapps/extensions/content/aboutaddonsCommon.js index fd91ba58be..5aa5f7ea0a 100644 --- a/toolkit/mozapps/extensions/content/aboutaddonsCommon.js +++ b/toolkit/mozapps/extensions/content/aboutaddonsCommon.js @@ -150,13 +150,13 @@ function shouldShowPermissionsPrompt(addon) { return false; } - let perms = addon.userPermissions; + let perms = addon.installPermissions; return perms?.origins.length || perms?.permissions.length; } function showPermissionsPrompt(addon) { return new Promise(resolve => { - const permissions = addon.userPermissions; + const permissions = addon.installPermissions; const target = getBrowserElement(); const onAddonEnabled = () => { diff --git a/toolkit/mozapps/extensions/content/abuse-report-frame.html b/toolkit/mozapps/extensions/content/abuse-report-frame.html deleted file mode 100644 index 64148ecece..0000000000 --- a/toolkit/mozapps/extensions/content/abuse-report-frame.html +++ /dev/null @@ -1,213 +0,0 @@ -<!-- 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/. --> - -<!DOCTYPE html> -<html> - <head> - <title></title> - <link rel="stylesheet" href="chrome://global/skin/in-content/common.css" /> - <link - rel="stylesheet" - href="chrome://mozapps/content/extensions/aboutaddons.css" - /> - <link - rel="stylesheet" - href="chrome://mozapps/content/extensions/abuse-report-panel.css" - /> - - <link rel="localization" href="branding/brand.ftl" /> - <link rel="localization" href="toolkit/about/aboutAddons.ftl" /> - <link rel="localization" href="toolkit/about/abuseReports.ftl" /> - <script - type="module" - src="chrome://global/content/elements/moz-button-group.mjs" - ></script> - <script - type="module" - src="chrome://global/content/elements/moz-support-link.mjs" - ></script> - <script - defer - src="chrome://mozapps/content/extensions/abuse-report-panel.js" - ></script> - </head> - - <body> - <addon-abuse-report></addon-abuse-report> - - <!-- WebComponents Templates --> - <template id="tmpl-modal"> - <div class="modal-overlay-outer"></div> - <div class="modal-panel-container"></div> - </template> - - <template id="tmpl-abuse-report"> - <form class="addon-abuse-report" onsubmit="return false;"> - <div class="abuse-report-header"> - <img class="card-heading-icon addon-icon" /> - <div class="card-contents"> - <span class="addon-name"></span> - <span - class="addon-author-box" - data-l10n-args='{"author-name": "author placeholder"}' - data-l10n-id="abuse-report-addon-authored-by" - > - <a - data-l10n-name="author-name" - class="author" - href="#" - target="_blank" - ></a> - </span> - </div> - </div> - <button class="abuse-report-close-icon" type="button"></button> - <div class="abuse-report-contents"> - <abuse-report-reasons-panel></abuse-report-reasons-panel> - <abuse-report-submit-panel hidden></abuse-report-submit-panel> - </div> - <div class="abuse-report-buttons"> - <moz-button-group class="abuse-report-reasons-buttons"> - <button - class="abuse-report-cancel" - type="button" - data-l10n-id="abuse-report-cancel-button" - ></button> - <button - class="primary abuse-report-next" - type="button" - data-l10n-id="abuse-report-next-button" - ></button> - </moz-button-group> - <moz-button-group class="abuse-report-submit-buttons" hidden> - <button - class="abuse-report-goback" - type="button" - data-l10n-id="abuse-report-goback-button" - ></button> - <button - class="primary abuse-report-submit" - type="button" - data-l10n-id="abuse-report-submit-button" - ></button> - </moz-button-group> - </div> - </form> - </template> - - <template id="tmpl-reasons-panel"> - <h2 class="abuse-report-title"></h2> - <hr /> - <p class="abuse-report-subtitle" data-l10n-id="abuse-report-subtitle"></p> - <ul class="abuse-report-reasons"> - <li is="abuse-report-reason-listitem" report-reason="other"></li> - </ul> - <p> - <span data-l10n-id="abuse-report-learnmore-intro"></span> - <a - is="moz-support-link" - target="_blank" - support-page="reporting-extensions-and-themes-abuse" - data-l10n-id="abuse-report-learnmore-link" - > - </a> - </p> - </template> - - <template id="tmpl-submit-panel"> - <h2 class="abuse-reason-title"></h2> - <abuse-report-reason-suggestions></abuse-report-reason-suggestions> - <hr /> - <p - class="abuse-report-subtitle" - data-l10n-id="abuse-report-submit-description" - ></p> - <textarea name="message" data-l10n-id="abuse-report-textarea"></textarea> - <p class="abuse-report-note" data-l10n-id="abuse-report-submit-note"></p> - </template> - - <template id="tmpl-reason-listitem"> - <label> - <input type="radio" name="reason" class="radio" /> - <span class="reason-description"></span> - <span hidden class="abuse-report-note reason-example"></span> - </label> - </template> - - <template id="tmpl-suggestions-settings"> - <p data-l10n-id="abuse-report-settings-suggestions"></p> - <p></p> - <ul> - <li> - <a - is="moz-support-link" - target="_blank" - data-l10n-id="abuse-report-settings-suggestions-search" - support-page="prefs-search" - > - </a> - </li> - <li> - <a - is="moz-support-link" - target="_blank" - data-l10n-id="abuse-report-settings-suggestions-homepage" - support-page="prefs-homepage" - > - </a> - </li> - </ul> - </template> - - <template id="tmpl-suggestions-policy"> - <p data-l10n-id="abuse-report-policy-suggestions"> - <a - class="abuse-policy-learnmore" - target="_blank" - data-l10n-name="report-infringement-link" - > - </a> - </p> - </template> - - <template id="tmpl-suggestions-broken-extension"> - <p data-l10n-id="abuse-report-broken-suggestions-extension"> - <a - class="extension-support-link" - target="_blank" - data-l10n-name="support-link" - > - </a> - </p> - - <p></p - ></template> - - <template id="tmpl-suggestions-broken-theme"> - <p data-l10n-id="abuse-report-broken-suggestions-theme"> - <a - class="extension-support-link" - target="_blank" - data-l10n-name="support-link" - > - </a> - </p> - - <p></p - ></template> - - <template id="tmpl-suggestions-broken-sitepermission"> - <p data-l10n-id="abuse-report-broken-suggestions-sitepermission"> - <a - class="extension-support-link" - target="_blank" - data-l10n-name="support-link" - > - </a> - </p> - - <p></p - ></template> - </body> -</html> diff --git a/toolkit/mozapps/extensions/content/abuse-report-panel.css b/toolkit/mozapps/extensions/content/abuse-report-panel.css deleted file mode 100644 index aa2243c162..0000000000 --- a/toolkit/mozapps/extensions/content/abuse-report-panel.css +++ /dev/null @@ -1,181 +0,0 @@ -/* 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/. */ - -/* Abuse Reports card */ - -:root { - --close-icon-url: url("chrome://global/skin/icons/close.svg"); - --close-icon-size: 20px; - - --modal-panel-min-width: 60%; - --modal-panel-margin-top: 36px; - --modal-panel-margin-bottom: 36px; - --modal-panel-margin: 20%; - --modal-panel-padding: 40px; - - --line-height: 20px; - --textarea-height: 220px; - --listitem-padding-bottom: 14px; - --list-radio-column-size: 28px; - --note-font-size: 14px; - --note-font-weight: 400; - --subtitle-font-size: 16px; - --subtitle-font-weight: 600; -} - -/* Ensure that the document (embedded in the XUL about:addons using a - XUL browser) has a transparent background */ -html { - background-color: transparent; -} - -.modal-overlay-outer { - background: var(--grey-90-a60); - width: 100%; - height: 100%; - position: fixed; - z-index: -1; -} - -.modal-panel-container { - padding-top: var(--modal-panel-margin-top); - padding-bottom: var(--modal-panel-margin-bottom); - padding-left: var(--modal-panel-margin); - padding-right: var(--modal-panel-margin); -} - -.addon-abuse-report { - min-width: var(--modal-panel-min-width); - padding: var(--modal-panel-padding); - display: flex; - flex-direction: column; - position: relative; -} - -.addon-abuse-report:hover { - /* Avoid the card box highlighting on hover. */ - box-shadow: none; -} - - -.abuse-report-close-icon { - /* position the close button in the panel upper-right corner */ - position: absolute; - top: 12px; - inset-inline-end: 16px; -} - -button.abuse-report-close-icon { - background: var(--close-icon-url) no-repeat center center; - -moz-context-properties: fill; - color: inherit !important; - fill: currentColor; - min-width: auto; - min-height: auto; - width: var(--close-icon-size); - height: var(--close-icon-size); - margin: 0; - padding: 0; -} - -button.abuse-report-close-icon:hover { - fill-opacity: 0.1; -} - -button.abuse-report-close-icon:hover:active { - fill-opacity: 0.2; -} - -.abuse-report-header { - display: flex; - flex-direction: row; -} - -.abuse-report-contents, -.abuse-report-contents > hr { - width: 100%; -} - -.abuse-report-note { - color: var(--text-color-deemphasized); - font-size: var(--note-font-size); - font-weight: var(--note-font-weight); - line-height: var(--line-height); -} - -.abuse-report-subtitle { - font-size: var(--subtitle-font-size); - font-weight: var(--subtitle-font-weight); - line-height: var(--line-height); -} - -ul.abuse-report-reasons { - list-style-type: none; - padding-inline-start: 0; -} - -ul.abuse-report-reasons > li { - display: flex; - padding-bottom: var(--listitem-padding-bottom); -} - -ul.abuse-report-reasons > li > label { - display: grid; - grid-template-columns: var(--list-radio-column-size) auto; - grid-template-rows: 50% auto; - width: 100%; - line-height: var(--line-height); - font-size: var(--subtitle-font-size); - font-weight: var(--note-font-weight); - margin-inline-start: 4px; -} - -ul.abuse-report-reasons > li > label > [type="radio"] { - grid-column: 1; -} - -ul.abuse-report-reasons > li > label > span { - grid-column: 2; -} - -abuse-report-submit-panel textarea { - width: 100%; - height: var(--textarea-height); - resize: none; - box-sizing: border-box; -} - -/* Adapt styles for the panel opened in its own dialog window */ - -html.dialog-window { - background-color: var(--in-content-box-background); - height: 100%; - min-width: 740px; -} - -html.dialog-window body { - overflow: hidden; - min-height: 100%; - display: flex; - flex-direction: column; -} - -html.dialog-window .abuse-report-close-icon { - display: none; -} - -html.dialog-window addon-abuse-report { - flex-grow: 1; - display: flex; - /* Ensure that the dialog window starts from a reasonable initial size */ - --modal-panel-min-width: 700px; -} - -html.dialog-window addon-abuse-report form { - display: flex; -} - -html.dialog-window addon-abuse-report form .abuse-report-contents { - flex-grow: 1; -} diff --git a/toolkit/mozapps/extensions/content/abuse-report-panel.js b/toolkit/mozapps/extensions/content/abuse-report-panel.js deleted file mode 100644 index 53e0d76db0..0000000000 --- a/toolkit/mozapps/extensions/content/abuse-report-panel.js +++ /dev/null @@ -1,873 +0,0 @@ -/* 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/. */ -/* eslint max-len: ["error", 80] */ - -"use strict"; - -ChromeUtils.defineESModuleGetters(this, { - AbuseReporter: "resource://gre/modules/AbuseReporter.sys.mjs", -}); - -const IS_DIALOG_WINDOW = window.arguments && window.arguments.length; - -let openWebLink = IS_DIALOG_WINDOW - ? window.arguments[0].wrappedJSObject.openWebLink - : url => { - window.windowRoot.ownerGlobal.openWebLinkIn(url, "tab", { - relatedToCurrent: true, - }); - }; - -const showOnAnyType = () => false; -const hideOnAnyType = () => true; -const hideOnAddonTypes = hideForTypes => { - return addonType => hideForTypes.includes(addonType); -}; - -// The reasons string used as a key in this Map is expected to stay in sync -// with the reasons string used in the "abuseReports.ftl" locale file and -// the suggestions templates included in abuse-report-frame.html. -const ABUSE_REASONS = (window.ABUSE_REPORT_REASONS = { - damage: { - isExampleHidden: showOnAnyType, - isReasonHidden: hideOnAddonTypes(["theme"]), - }, - spam: { - isExampleHidden: showOnAnyType, - isReasonHidden: hideOnAddonTypes(["sitepermission"]), - }, - settings: { - hasSuggestions: true, - isExampleHidden: hideOnAnyType, - isReasonHidden: hideOnAddonTypes(["theme", "sitepermission"]), - }, - deceptive: { - isExampleHidden: showOnAnyType, - isReasonHidden: hideOnAddonTypes(["sitepermission"]), - }, - broken: { - hasAddonTypeL10nId: true, - hasAddonTypeSuggestionTemplate: true, - hasSuggestions: true, - isExampleHidden: hideOnAddonTypes(["theme"]), - isReasonHidden: showOnAnyType, - requiresSupportURL: true, - }, - policy: { - hasSuggestions: true, - isExampleHidden: hideOnAnyType, - isReasonHidden: hideOnAddonTypes(["sitepermission"]), - }, - unwanted: { - isExampleHidden: showOnAnyType, - isReasonHidden: hideOnAddonTypes(["theme"]), - }, - other: { - isExampleHidden: hideOnAnyType, - isReasonHidden: showOnAnyType, - }, -}); - -// Maps the reason id to the last version of the related fluent id. -// NOTE: when changing the localized string, increase the `-vN` suffix -// in the abuseReports.ftl fluent file and update this mapping table. -const REASON_L10N_STRING_MAPPING = { - "abuse-report-damage-reason": "abuse-report-damage-reason-v2", - "abuse-report-spam-reason": "abuse-report-spam-reason-v2", - "abuse-report-settings-reason": "abuse-report-settings-reason-v2", - "abuse-report-deceptive-reason": "abuse-report-deceptive-reason-v2", - "abuse-report-broken-reason-extension": - "abuse-report-broken-reason-extension-v2", - "abuse-report-broken-reason-sitepermission": - "abuse-report-broken-reason-sitepermission-v2", - "abuse-report-broken-reason-theme": "abuse-report-broken-reason-theme-v2", - "abuse-report-policy-reason": "abuse-report-policy-reason-v2", - "abuse-report-unwanted-reason": "abuse-report-unwanted-reason-v2", -}; - -function getReasonL10nId(reason, addonType) { - let reasonId = `abuse-report-${reason}-reason`; - // Special case reasons that have a addonType-specific - // l10n id. - if (ABUSE_REASONS[reason].hasAddonTypeL10nId) { - reasonId += `-${addonType}`; - } - // Map the reason to the corresponding versionized fluent string, using the - // mapping table above, if available. - return REASON_L10N_STRING_MAPPING[reasonId] || reasonId; -} - -function getSuggestionsTemplate({ addonType, reason, supportURL }) { - const reasonInfo = ABUSE_REASONS[reason]; - - if ( - !addonType || - !reasonInfo.hasSuggestions || - (reasonInfo.requiresSupportURL && !supportURL) - ) { - return null; - } - - let templateId = `tmpl-suggestions-${reason}`; - // Special case reasons that have a addonType-specific - // suggestion template. - if (reasonInfo.hasAddonTypeSuggestionTemplate) { - templateId += `-${addonType}`; - } - - return document.getElementById(templateId); -} - -// Map of the learnmore links metadata, keyed by link element class. -const LEARNMORE_LINKS = { - ".abuse-policy-learnmore": { - baseURL: "https://www.mozilla.org/%LOCALE%/", - path: "about/legal/report-infringement/", - }, -}; - -// Format links that match the selector in the LEARNMORE_LINKS map -// found in a given container element. -function formatLearnMoreURLs(containerEl) { - for (const [linkClass, linkInfo] of Object.entries(LEARNMORE_LINKS)) { - for (const element of containerEl.querySelectorAll(linkClass)) { - const baseURL = Services.urlFormatter.formatURL(linkInfo.baseURL); - - element.href = baseURL + linkInfo.path; - } - } -} - -// Define a set of getters from a Map<propertyName, selector>. -function defineElementSelectorsGetters(object, propsMap) { - const props = Object.entries(propsMap).reduce((acc, entry) => { - const [name, selector] = entry; - acc[name] = { get: () => object.querySelector(selector) }; - return acc; - }, {}); - Object.defineProperties(object, props); -} - -// Define a set of properties getters and setters for a -// Map<propertyName, attributeName>. -function defineElementAttributesProperties(object, propsMap) { - const props = Object.entries(propsMap).reduce((acc, entry) => { - const [name, attr] = entry; - acc[name] = { - get: () => object.getAttribute(attr), - set: value => { - object.setAttribute(attr, value); - }, - }; - return acc; - }, {}); - Object.defineProperties(object, props); -} - -// Return an object with properties associated to elements -// found using the related selector in the propsMap. -function getElements(containerEl, propsMap) { - return Object.entries(propsMap).reduce((acc, entry) => { - const [name, selector] = entry; - let elements = containerEl.querySelectorAll(selector); - acc[name] = elements.length > 1 ? elements : elements[0]; - return acc; - }, {}); -} - -function dispatchCustomEvent(el, eventName, detail) { - el.dispatchEvent(new CustomEvent(eventName, { detail })); -} - -// This WebComponent extends the li item to represent an abuse report reason -// and it is responsible for: -// - embedding a photon styled radio buttons -// - localizing the reason list item -// - optionally embedding a localized example, positioned -// below the reason label, and adjusts the item height -// accordingly -class AbuseReasonListItem extends HTMLLIElement { - constructor() { - super(); - defineElementAttributesProperties(this, { - addonType: "addon-type", - reason: "report-reason", - checked: "checked", - }); - } - - connectedCallback() { - this.update(); - } - - async update() { - if (this.reason !== "other" && !this.addonType) { - return; - } - - const { reason, checked, addonType } = this; - - this.textContent = ""; - const content = document.importNode(this.template.content, true); - - if (reason) { - const reasonId = `abuse-reason-${reason}`; - const reasonInfo = ABUSE_REASONS[reason] || {}; - - const { labelEl, descriptionEl, radioEl } = getElements(content, { - labelEl: "label", - descriptionEl: ".reason-description", - radioEl: "input[type=radio]", - }); - - labelEl.setAttribute("for", reasonId); - radioEl.id = reasonId; - radioEl.value = reason; - radioEl.checked = !!checked; - - // This reason has a different localized description based on the - // addon type. - document.l10n.setAttributes( - descriptionEl, - getReasonL10nId(reason, addonType) - ); - - // Show the reason example if supported for the addon type. - if (!reasonInfo.isExampleHidden(addonType)) { - const exampleEl = content.querySelector(".reason-example"); - document.l10n.setAttributes( - exampleEl, - `abuse-report-${reason}-example` - ); - exampleEl.hidden = false; - } - } - - formatLearnMoreURLs(content); - - this.appendChild(content); - } - - get template() { - return document.getElementById("tmpl-reason-listitem"); - } -} - -// This WebComponents implements the first step of the abuse -// report submission and embeds a randomized reasons list. -class AbuseReasonsPanel extends HTMLElement { - constructor() { - super(); - defineElementAttributesProperties(this, { - addonType: "addon-type", - }); - } - - connectedCallback() { - this.update(); - } - - update() { - if (!this.isConnected || !this.addonType) { - return; - } - - const { addonType } = this; - - this.textContent = ""; - const content = document.importNode(this.template.content, true); - - const { titleEl, listEl } = getElements(content, { - titleEl: ".abuse-report-title", - listEl: "ul.abuse-report-reasons", - }); - - // Change the title l10n-id if the addon type is theme. - document.l10n.setAttributes(titleEl, `abuse-report-title-${addonType}`); - - // Create the randomized list of reasons. - const reasons = Object.keys(ABUSE_REASONS) - .filter(reason => reason !== "other") - .sort(() => Math.random() - 0.5); - - for (const reason of reasons) { - const reasonInfo = ABUSE_REASONS[reason]; - if (!reasonInfo || reasonInfo.isReasonHidden(addonType)) { - // Skip an extension only reason while reporting a theme. - continue; - } - const item = document.createElement("li", { - is: "abuse-report-reason-listitem", - }); - item.reason = reason; - item.addonType = addonType; - - listEl.prepend(item); - } - - listEl.firstElementChild.checked = true; - formatLearnMoreURLs(content); - - this.appendChild(content); - } - - get template() { - return document.getElementById("tmpl-reasons-panel"); - } -} - -// This WebComponent is responsible for the suggestions, which are: -// - generated based on a template keyed by abuse report reason -// - localized by assigning fluent ids generated from the abuse report reason -// - learn more and extension support url are then generated when the -// specific reason expects it -class AbuseReasonSuggestions extends HTMLElement { - constructor() { - super(); - defineElementAttributesProperties(this, { - extensionSupportURL: "extension-support-url", - reason: "report-reason", - }); - } - - update() { - const { addonType, extensionSupportURL, reason } = this; - - this.textContent = ""; - - let template = getSuggestionsTemplate({ - addonType, - reason, - supportURL: extensionSupportURL, - }); - - if (template) { - let content = document.importNode(template.content, true); - - formatLearnMoreURLs(content); - - let extSupportLink = content.querySelector("a.extension-support-link"); - if (extSupportLink) { - extSupportLink.href = extensionSupportURL; - } - - this.appendChild(content); - this.hidden = false; - } else { - this.hidden = true; - } - } -} - -// This WebComponents implements the last step of the abuse report submission. -class AbuseSubmitPanel extends HTMLElement { - constructor() { - super(); - defineElementAttributesProperties(this, { - addonType: "addon-type", - reason: "report-reason", - extensionSupportURL: "extensionSupportURL", - }); - defineElementSelectorsGetters(this, { - _textarea: "textarea", - _title: ".abuse-reason-title", - _suggestions: "abuse-report-reason-suggestions", - }); - } - - connectedCallback() { - this.render(); - } - - render() { - this.textContent = ""; - this.appendChild(document.importNode(this.template.content, true)); - } - - update() { - if (!this.isConnected || !this.addonType) { - return; - } - const { addonType, reason, _suggestions, _title } = this; - document.l10n.setAttributes(_title, getReasonL10nId(reason, addonType)); - _suggestions.reason = reason; - _suggestions.addonType = addonType; - _suggestions.extensionSupportURL = this.extensionSupportURL; - _suggestions.update(); - } - - clear() { - this._textarea.value = ""; - } - - get template() { - return document.getElementById("tmpl-submit-panel"); - } -} - -// This WebComponent provides the abuse report -class AbuseReport extends HTMLElement { - constructor() { - super(); - this._report = null; - defineElementSelectorsGetters(this, { - _form: "form", - _textarea: "textarea", - _radioCheckedReason: "[type=radio]:checked", - _reasonsPanel: "abuse-report-reasons-panel", - _submitPanel: "abuse-report-submit-panel", - _reasonsPanelButtons: ".abuse-report-reasons-buttons", - _submitPanelButtons: ".abuse-report-submit-buttons", - _iconClose: ".abuse-report-close-icon", - _btnNext: "button.abuse-report-next", - _btnCancel: "button.abuse-report-cancel", - _btnGoBack: "button.abuse-report-goback", - _btnSubmit: "button.abuse-report-submit", - _addonAuthorContainer: ".abuse-report-header .addon-author-box", - _addonIconElement: ".abuse-report-header img.addon-icon", - _addonNameElement: ".abuse-report-header .addon-name", - _linkAddonAuthor: ".abuse-report-header .addon-author-box a.author", - }); - } - - connectedCallback() { - this.render(); - - this.addEventListener("click", this); - - // Start listening to keydown events (to close the modal - // when Escape has been pressed and to handling the keyboard - // navigation). - document.addEventListener("keydown", this); - } - - disconnectedCallback() { - this.textContent = ""; - this.removeEventListener("click", this); - document.removeEventListener("keydown", this); - } - - handleEvent(evt) { - if (!this.isConnected || !this.addon) { - return; - } - - switch (evt.type) { - case "keydown": - if (evt.key === "Escape") { - // Prevent Esc to close the panel if the textarea is - // empty. - if (this.message && !this._submitPanel.hidden) { - return; - } - this.cancel(); - } - if (!IS_DIALOG_WINDOW) { - // Workaround keyboard navigation issues when - // the panel is running in its own dialog window. - this.handleKeyboardNavigation(evt); - } - break; - case "click": - if (evt.target === this._iconClose || evt.target === this._btnCancel) { - // NOTE: clear the focus on the clicked element to ensure that - // -moz-focusring pseudo class is not still set on the element - // when the panel is going to be shown again (See Bug 1560949). - evt.target.blur(); - this.cancel(); - } - if (evt.target === this._btnNext) { - this.switchToSubmitMode(); - } - if (evt.target === this._btnGoBack) { - this.switchToListMode(); - } - if (evt.target === this._btnSubmit) { - this.submit(); - } - if (evt.target.localName === "a") { - evt.preventDefault(); - evt.stopPropagation(); - const url = evt.target.getAttribute("href"); - // Ignore if url is empty. - if (url) { - openWebLink(url); - } - } - break; - } - } - - handleKeyboardNavigation(evt) { - if ( - evt.keyCode !== evt.DOM_VK_TAB || - evt.altKey || - evt.controlKey || - evt.metaKey - ) { - return; - } - - const fm = Services.focus; - const backward = evt.shiftKey; - - const isFirstFocusableElement = el => { - // Also consider the document body as a valid first focusable element. - if (el === document.body) { - return true; - } - // XXXrpl unfortunately there is no way to get the first focusable element - // without asking the focus manager to move focus to it (similar strategy - // is also being used in about:prefereces subdialog.js). - const rv = el == fm.moveFocus(window, null, fm.MOVEFOCUS_FIRST, 0); - fm.setFocus(el, 0); - return rv; - }; - - // If the focus is exiting the panel while navigating - // backward, focus the previous element sibling on the - // Firefox UI. - if (backward && isFirstFocusableElement(evt.target)) { - evt.preventDefault(); - evt.stopImmediatePropagation(); - const chromeWin = window.windowRoot.ownerGlobal; - Services.focus.moveFocus( - chromeWin, - null, - Services.focus.MOVEFOCUS_BACKWARD, - Services.focus.FLAG_BYKEY - ); - } - } - - render() { - this.textContent = ""; - const formTemplate = document.importNode(this.template.content, true); - if (IS_DIALOG_WINDOW) { - this.appendChild(formTemplate); - } else { - // Append the report form inside a modal overlay when the report panel - // is a sub-frame of the about:addons tab. - const modalTemplate = document.importNode( - this.modalTemplate.content, - true - ); - - this.appendChild(modalTemplate); - this.querySelector(".modal-panel-container").appendChild(formTemplate); - - // Add the card styles to the form. - this.querySelector("form").classList.add("card"); - } - } - - async update() { - if (!this.addon) { - return; - } - - const { - addonId, - addonType, - _addonAuthorContainer, - _addonIconElement, - _addonNameElement, - _linkAddonAuthor, - _reasonsPanel, - _submitPanel, - } = this; - - // Ensure that the first step of the abuse submission is the one - // currently visible. - this.switchToListMode(); - - // Cancel the abuse report if the addon is not an extension or theme. - if (!AbuseReporter.isSupportedAddonType(addonType)) { - Cu.reportError( - new Error( - `Closing abuse report panel on unexpected addon type: ${addonType}` - ) - ); - this.cancel(); - return; - } - - _addonNameElement.textContent = this.addonName; - - if (this.authorName) { - _linkAddonAuthor.href = this.authorURL || this.homepageURL; - _linkAddonAuthor.textContent = this.authorName; - document.l10n.setAttributes( - _linkAddonAuthor.parentNode, - "abuse-report-addon-authored-by", - { "author-name": this.authorName } - ); - _addonAuthorContainer.hidden = false; - } else { - _addonAuthorContainer.hidden = true; - } - - _addonIconElement.setAttribute("src", this.iconURL); - - _reasonsPanel.addonType = this.addonType; - _reasonsPanel.update(); - - _submitPanel.addonType = this.addonType; - _submitPanel.reason = this.reason; - _submitPanel.extensionSupportURL = this.supportURL; - _submitPanel.update(); - - this.focus(); - - dispatchCustomEvent(this, "abuse-report:updated", { - addonId, - panel: "reasons", - }); - } - - setAbuseReport(abuseReport) { - this._report = abuseReport; - // Clear the textarea from any previously entered content. - this._submitPanel.clear(); - - if (abuseReport) { - this.update(); - this.hidden = false; - } else { - this.hidden = true; - } - } - - focus() { - if (!this.isConnected || !this.addon) { - return; - } - if (this._reasonsPanel.hidden) { - const { _textarea } = this; - _textarea.focus(); - _textarea.select(); - } else { - const { _radioCheckedReason } = this; - if (_radioCheckedReason) { - _radioCheckedReason.focus(); - } - } - } - - cancel() { - if (!this.isConnected || !this.addon) { - return; - } - this._report = null; - dispatchCustomEvent(this, "abuse-report:cancel"); - } - - submit() { - if (!this.isConnected || !this.addon) { - return; - } - this._report.setMessage(this.message); - this._report.setReason(this.reason); - dispatchCustomEvent(this, "abuse-report:submit", { - addonId: this.addonId, - report: this._report, - }); - } - - switchToSubmitMode() { - if (!this.isConnected || !this.addon) { - return; - } - this._submitPanel.reason = this.reason; - this._submitPanel.update(); - this._reasonsPanel.hidden = true; - this._reasonsPanelButtons.hidden = true; - this._submitPanel.hidden = false; - this._submitPanelButtons.hidden = false; - // Adjust the focused element when switching to the submit panel. - this.focus(); - dispatchCustomEvent(this, "abuse-report:updated", { - addonId: this.addonId, - panel: "submit", - }); - } - - switchToListMode() { - if (!this.isConnected || !this.addon) { - return; - } - this._submitPanel.hidden = true; - this._submitPanelButtons.hidden = true; - this._reasonsPanel.hidden = false; - this._reasonsPanelButtons.hidden = false; - // Adjust the focused element when switching back to the list of reasons. - this.focus(); - dispatchCustomEvent(this, "abuse-report:updated", { - addonId: this.addonId, - panel: "reasons", - }); - } - - get addon() { - return this._report?.addon; - } - - get addonId() { - return this.addon?.id; - } - - get addonName() { - return this.addon?.name; - } - - get addonType() { - // TODO(Bug 1789718): Remove after the deprecated XPIProvider-based - // implementation is also removed. - if (this.addon?.type === "sitepermission-deprecated") { - return "sitepermission"; - } - return this.addon?.type; - } - - get addonCreator() { - return this.addon?.creator; - } - - get homepageURL() { - return this.addon?.homepageURL || this.authorURL || ""; - } - - get authorName() { - // The author name may be missing on some of the test extensions - // (or for temporarily installed add-ons). - return this.addonCreator?.name || ""; - } - - get authorURL() { - return this.addonCreator?.url || ""; - } - - get iconURL() { - if (this.addonType === "sitepermission") { - return "chrome://mozapps/skin/extensions/category-sitepermission.svg"; - } - return ( - this.addon?.iconURL || - // Some extensions (e.g. static theme addons) may not have an icon, - // and so we fallback to use the generic extension icon. - "chrome://mozapps/skin/extensions/extensionGeneric.svg" - ); - } - - get supportURL() { - let url = this.addon?.supportURL || this.homepageURL || ""; - if (!url && this.addonType === "sitepermission" && this.addon?.siteOrigin) { - return this.addon.siteOrigin; - } - return url; - } - - get message() { - return this._form.elements.message.value; - } - - get reason() { - return this._form.elements.reason.value; - } - - get modalTemplate() { - return document.getElementById("tmpl-modal"); - } - - get template() { - return document.getElementById("tmpl-abuse-report"); - } -} - -customElements.define("abuse-report-reason-listitem", AbuseReasonListItem, { - extends: "li", -}); -customElements.define( - "abuse-report-reason-suggestions", - AbuseReasonSuggestions -); -customElements.define("abuse-report-reasons-panel", AbuseReasonsPanel); -customElements.define("abuse-report-submit-panel", AbuseSubmitPanel); -customElements.define("addon-abuse-report", AbuseReport); - -// The panel has been opened in a new dialog window. -if (IS_DIALOG_WINDOW) { - // CSS customizations when panel is in its own window - // (vs. being an about:addons subframe). - document.documentElement.className = "dialog-window"; - - const { report, deferredReport, deferredReportPanel } = - window.arguments[0].wrappedJSObject; - - window.addEventListener( - "unload", - () => { - // If the window has been closed resolve the deferredReport - // promise and reject the deferredReportPanel one, in case - // they haven't been resolved yet. - deferredReport.resolve({ userCancelled: true }); - deferredReportPanel.reject(new Error("report dialog closed")); - }, - { once: true } - ); - - document.l10n.setAttributes( - document.querySelector("head > title"), - "abuse-report-dialog-title", - { - "addon-name": report.addon.name, - } - ); - - const el = document.querySelector("addon-abuse-report"); - el.addEventListener("abuse-report:submit", () => { - deferredReport.resolve({ - userCancelled: false, - report, - }); - }); - el.addEventListener( - "abuse-report:cancel", - () => { - // Resolve the report panel deferred (in case the report - // has been cancelled automatically before it has been fully - // rendered, e.g. in case of non-supported addon types). - deferredReportPanel.resolve(el); - // Resolve the deferred report as cancelled. - deferredReport.resolve({ userCancelled: true }); - }, - { once: true } - ); - - // Adjust window size (if needed) once the fluent strings have been - // added to the document and the document has been flushed. - el.addEventListener( - "abuse-report:updated", - async () => { - const form = document.querySelector("form"); - await document.l10n.translateFragment(form); - const { scrollWidth, scrollHeight } = await window.promiseDocumentFlushed( - () => form - ); - // Resolve promiseReportPanel once the panel completed the initial render - // (used in tests). - deferredReportPanel.resolve(el); - if ( - window.innerWidth !== scrollWidth || - window.innerHeight !== scrollHeight - ) { - const width = window.outerWidth - window.innerWidth + scrollWidth; - const height = window.outerHeight - window.innerHeight + scrollHeight; - window.resizeTo(width, height); - } - }, - { once: true } - ); - el.setAbuseReport(report); -} diff --git a/toolkit/mozapps/extensions/content/abuse-reports.js b/toolkit/mozapps/extensions/content/abuse-reports.js index 38fa0f9f46..d978aa1293 100644 --- a/toolkit/mozapps/extensions/content/abuse-reports.js +++ b/toolkit/mozapps/extensions/content/abuse-reports.js @@ -4,188 +4,22 @@ /* eslint max-len: ["error", 80] */ /* import-globals-from aboutaddonsCommon.js */ -/* exported openAbuseReport */ +/* exported AbuseReporter, openAbuseReport */ /* global windowRoot */ /** * This script is part of the HTML about:addons page and it provides some - * helpers used for the Abuse Reporting submission (and related message bars). + * helpers used for abuse reports. */ const { AbuseReporter } = ChromeUtils.importESModule( "resource://gre/modules/AbuseReporter.sys.mjs" ); -// Message Bars definitions. -const ABUSE_REPORT_MESSAGE_BARS = { - // Idle message-bar (used while the submission is still ongoing). - submitting: { - actions: ["cancel"], - l10n: { - id: "abuse-report-messagebar-submitting2", - actionIds: { - cancel: "abuse-report-messagebar-action-cancel", - }, - }, - }, - // Submitted report message-bar. - submitted: { - actions: ["remove", "keep"], - dismissable: true, - l10n: { - id: "abuse-report-messagebar-submitted2", - actionIdsPerAddonType: { - extension: { - remove: "abuse-report-messagebar-action-remove-extension", - keep: "abuse-report-messagebar-action-keep-extension", - }, - sitepermission: { - remove: "abuse-report-messagebar-action-remove-sitepermission", - keep: "abuse-report-messagebar-action-keep-sitepermission", - }, - theme: { - remove: "abuse-report-messagebar-action-remove-theme", - keep: "abuse-report-messagebar-action-keep-theme", - }, - }, - }, - }, - // Submitted report message-bar (with no remove actions). - "submitted-no-remove-action": { - dismissable: true, - l10n: { id: "abuse-report-messagebar-submitted-noremove2" }, - }, - // Submitted report and remove addon message-bar. - "submitted-and-removed": { - dismissable: true, - l10n: { - idsPerAddonType: { - extension: "abuse-report-messagebar-removed-extension2", - sitepermission: "abuse-report-messagebar-removed-sitepermission2", - theme: "abuse-report-messagebar-removed-theme2", - }, - }, - }, - // The "aborted report" message bar is rendered as a generic informative one, - // because aborting a report is triggered by a user choice. - ERROR_ABORTED_SUBMIT: { - type: "info", - dismissable: true, - l10n: { id: "abuse-report-messagebar-aborted2" }, - }, - // Errors message bars. - ERROR_ADDON_NOTFOUND: { - type: "error", - dismissable: true, - l10n: { id: "abuse-report-messagebar-error2" }, - }, - ERROR_CLIENT: { - type: "error", - dismissable: true, - l10n: { id: "abuse-report-messagebar-error2" }, - }, - ERROR_NETWORK: { - actions: ["retry", "cancel"], - type: "error", - l10n: { - id: "abuse-report-messagebar-error2", - actionIds: { - retry: "abuse-report-messagebar-action-retry", - cancel: "abuse-report-messagebar-action-cancel", - }, - }, - }, - ERROR_RECENT_SUBMIT: { - actions: ["retry", "cancel"], - type: "error", - l10n: { - id: "abuse-report-messagebar-error-recent-submit2", - actionIds: { - retry: "abuse-report-messagebar-action-retry", - cancel: "abuse-report-messagebar-action-cancel", - }, - }, - }, - ERROR_SERVER: { - actions: ["retry", "cancel"], - type: "error", - l10n: { - id: "abuse-report-messagebar-error2", - actionIds: { - retry: "abuse-report-messagebar-action-retry", - cancel: "abuse-report-messagebar-action-cancel", - }, - }, - }, - ERROR_UNKNOWN: { - actions: ["retry", "cancel"], - type: "error", - l10n: { - id: "abuse-report-messagebar-error2", - actionIds: { - retry: "abuse-report-messagebar-action-retry", - cancel: "abuse-report-messagebar-action-cancel", - }, - }, - }, -}; +async function openAbuseReport({ addonId }) { + // TODO: `reportEntryPoint` is also passed to this function but we aren't + // using it currently. Maybe we should? -async function openAbuseReport({ addonId, reportEntryPoint }) { - try { - const reportDialog = await AbuseReporter.openDialog( - addonId, - reportEntryPoint, - window.docShell.chromeEventHandler - ); - - // Warn the user before the about:addons tab while an - // abuse report dialog is still open, and close the - // report dialog if the user choose to close the related - // about:addons tab. - const beforeunloadListener = evt => evt.preventDefault(); - const unloadListener = () => reportDialog.close(); - const clearUnloadListeners = () => { - window.removeEventListener("beforeunload", beforeunloadListener); - window.removeEventListener("unload", unloadListener); - }; - window.addEventListener("beforeunload", beforeunloadListener); - window.addEventListener("unload", unloadListener); - - reportDialog.promiseReport - .then( - report => { - if (report) { - submitReport({ report }); - } - }, - err => { - Cu.reportError( - `Unexpected abuse report panel error: ${err} :: ${err.stack}` - ); - reportDialog.close(); - } - ) - .then(clearUnloadListeners); - } catch (err) { - // Log the detailed error to the browser console. - Cu.reportError(err); - document.dispatchEvent( - new CustomEvent("abuse-report:create-error", { - detail: { - addonId, - addon: err.addon, - errorType: err.errorType, - }, - }) - ); - } -} - -// Unlike the openAbuseReport function, technically this method wouldn't need -// to be async, but it is so that both the implementations will be providing -// the same type signatures (returning a promise) to the callers, independently -// from which abuse reporting feature is enabled. -async function openAbuseReportAMOForm({ addonId }) { const amoUrl = AbuseReporter.getAMOFormURL({ addonId }); windowRoot.ownerGlobal.openTrustedLinkIn(amoUrl, "tab", { // Make sure the newly open tab is going to be focused, independently @@ -194,183 +28,4 @@ async function openAbuseReportAMOForm({ addonId }) { }); } -window.openAbuseReport = AbuseReporter.amoFormEnabled - ? openAbuseReportAMOForm - : openAbuseReport; - -// Helper function used to create abuse report message bars in the -// HTML about:addons page. -function createReportMessageBar( - definitionId, - { addonId, addonName, addonType }, - { onclose, onaction } = {} -) { - const barInfo = ABUSE_REPORT_MESSAGE_BARS[definitionId]; - if (!barInfo) { - throw new Error(`message-bar definition not found: ${definitionId}`); - } - const { dismissable, actions, type, l10n } = barInfo; - - // TODO(Bug 1789718): Remove after the deprecated XPIProvider-based - // implementation is also removed. - const mappingAddonType = - addonType === "sitepermission-deprecated" ? "sitepermission" : addonType; - - const getMessageL10n = () => { - return l10n.idsPerAddonType - ? l10n.idsPerAddonType[mappingAddonType] - : l10n.id; - }; - const getActionL10n = action => { - return l10n.actionIdsPerAddonType - ? l10n.actionIdsPerAddonType[mappingAddonType][action] - : l10n.actionIds[action]; - }; - - const messagebar = document.createElement("moz-message-bar"); - - document.l10n.setAttributes(messagebar, getMessageL10n(), { - "addon-name": addonName || addonId, - }); - messagebar.setAttribute("data-l10n-attrs", "message"); - - actions?.forEach(action => { - const buttonEl = document.createElement("button"); - buttonEl.addEventListener("click", () => onaction && onaction(action)); - document.l10n.setAttributes(buttonEl, getActionL10n(action)); - buttonEl.setAttribute("slot", "actions"); - messagebar.appendChild(buttonEl); - }); - - messagebar.setAttribute("type", type || "info"); - messagebar.dismissable = dismissable; - messagebar.addEventListener("message-bar:close", onclose, { once: true }); - - document.getElementById("abuse-reports-messages").append(messagebar); - - document.dispatchEvent( - new CustomEvent("abuse-report:new-message-bar", { - detail: { definitionId, messagebar }, - }) - ); - return messagebar; -} - -async function submitReport({ report }) { - const { addon } = report; - const addonId = addon.id; - const addonName = addon.name; - const addonType = addon.type; - - // Ensure that the tab that originated the report dialog is selected - // when the user is submitting the report. - const { gBrowser } = window.windowRoot.ownerGlobal; - if (gBrowser && gBrowser.getTabForBrowser) { - let tab = gBrowser.getTabForBrowser(window.docShell.chromeEventHandler); - gBrowser.selectedTab = tab; - } - - // Create a message bar while we are still submitting the report. - const mbSubmitting = createReportMessageBar( - "submitting", - { addonId, addonName, addonType }, - { - onaction: action => { - if (action === "cancel") { - report.abort(); - mbSubmitting.remove(); - } - }, - } - ); - - try { - await report.submit(); - mbSubmitting.remove(); - - // Create a submitted message bar when the submission has been - // successful. - let barId; - if ( - !(addon.permissions & AddonManager.PERM_CAN_UNINSTALL) && - !isPending(addon, "uninstall") - ) { - // Do not offer remove action if the addon can't be uninstalled. - barId = "submitted-no-remove-action"; - } else if (report.reportEntryPoint === "uninstall") { - // With reportEntryPoint "uninstall" a specific message bar - // is going to be used. - barId = "submitted-and-removed"; - } else { - // All the other reportEntryPoint ("menu" and "toolbar_context_menu") - // use the same kind of message bar. - barId = "submitted"; - } - - const mbInfo = createReportMessageBar( - barId, - { - addonId, - addonName, - addonType, - }, - { - onaction: action => { - mbInfo.remove(); - // action "keep" doesn't require any further action, - // just handle "remove". - if (action === "remove") { - report.addon.uninstall(true); - } - }, - } - ); - } catch (err) { - // Log the complete error in the console. - console.error("Error submitting abuse report for", addonId, err); - mbSubmitting.remove(); - // The report has a submission error, create a error message bar which - // may optionally allow the user to retry to submit the same report. - const barId = - err.errorType in ABUSE_REPORT_MESSAGE_BARS - ? err.errorType - : "ERROR_UNKNOWN"; - - const mbError = createReportMessageBar( - barId, - { - addonId, - addonName, - addonType, - }, - { - onaction: action => { - mbError.remove(); - switch (action) { - case "retry": - submitReport({ report }); - break; - case "cancel": - report.abort(); - break; - } - }, - } - ); - } -} - -document.addEventListener("abuse-report:submit", ({ detail }) => { - submitReport(detail); -}); - -document.addEventListener("abuse-report:create-error", ({ detail }) => { - const { addonId, addon, errorType } = detail; - const barId = - errorType in ABUSE_REPORT_MESSAGE_BARS ? errorType : "ERROR_UNKNOWN"; - createReportMessageBar(barId, { - addonId, - addonName: addon && addon.name, - addonType: addon && addon.type, - }); -}); +window.openAbuseReport = openAbuseReport; |