summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/extensions/content/abuse-reports.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/mozapps/extensions/content/abuse-reports.js')
-rw-r--r--toolkit/mozapps/extensions/content/abuse-reports.js317
1 files changed, 317 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/content/abuse-reports.js b/toolkit/mozapps/extensions/content/abuse-reports.js
new file mode 100644
index 0000000000..cf5fe27ee5
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/abuse-reports.js
@@ -0,0 +1,317 @@
+/* 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] */
+/* import-globals-from aboutaddonsCommon.js */
+/* exported openAbuseReport */
+
+/**
+ * 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).
+ */
+
+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: { id: "submitting", actions: ["cancel"] },
+ // Submitted report message-bar.
+ submitted: {
+ id: "submitted",
+ actionAddonTypeSuffix: true,
+ actions: ["remove", "keep"],
+ dismissable: true,
+ },
+ // Submitted report message-bar (with no remove actions).
+ "submitted-no-remove-action": {
+ id: "submitted-noremove",
+ dismissable: true,
+ },
+ // Submitted report and remove addon message-bar.
+ "submitted-and-removed": {
+ id: "removed",
+ addonTypeSuffix: true,
+ dismissable: true,
+ },
+ // 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: {
+ id: "aborted",
+ type: "generic",
+ dismissable: true,
+ },
+ // Errors message bars.
+ ERROR_ADDON_NOTFOUND: {
+ id: "error",
+ type: "error",
+ dismissable: true,
+ },
+ ERROR_CLIENT: {
+ id: "error",
+ type: "error",
+ dismissable: true,
+ },
+ ERROR_NETWORK: {
+ id: "error",
+ actions: ["retry", "cancel"],
+ type: "error",
+ },
+ ERROR_RECENT_SUBMIT: {
+ id: "error-recent-submit",
+ actions: ["retry", "cancel"],
+ type: "error",
+ },
+ ERROR_SERVER: {
+ id: "error",
+ actions: ["retry", "cancel"],
+ type: "error",
+ },
+ ERROR_UNKNOWN: {
+ id: "error",
+ actions: ["retry", "cancel"],
+ type: "error",
+ },
+};
+
+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,
+ },
+ })
+ );
+ }
+}
+
+window.openAbuseReport = 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 getMessageL10n = id => `abuse-report-messagebar-${id}`;
+ const getActionL10n = action => getMessageL10n(`action-${action}`);
+
+ const barInfo = ABUSE_REPORT_MESSAGE_BARS[definitionId];
+ if (!barInfo) {
+ throw new Error(`message-bar definition not found: ${definitionId}`);
+ }
+ const { id, dismissable, actions, type } = barInfo;
+ const messageEl = document.createElement("span");
+
+ // The message element includes an addon-name span (also filled by
+ // Fluent), which can be used to apply custom styles to the addon name
+ // included in the message bar (if needed).
+ const addonNameEl = document.createElement("span");
+ addonNameEl.setAttribute("data-l10n-name", "addon-name");
+ messageEl.append(addonNameEl);
+
+ // TODO(Bug 1789718): Remove after the deprecated XPIProvider-based
+ // implementation is also removed.
+ const mappingAddonType =
+ addonType === "sitepermission-deprecated" ? "sitepermission" : addonType;
+
+ document.l10n.setAttributes(
+ messageEl,
+ getMessageL10n(barInfo.addonTypeSuffix ? `${id}-${mappingAddonType}` : id),
+ { "addon-name": addonName || addonId }
+ );
+
+ const barActions = actions
+ ? actions.map(action => {
+ // Some of the message bars require a different per addonType
+ // Fluent id for their actions.
+ const actionId = barInfo.actionAddonTypeSuffix
+ ? `${action}-${mappingAddonType}`
+ : action;
+ const buttonEl = document.createElement("button");
+ buttonEl.addEventListener("click", () => onaction && onaction(action));
+ document.l10n.setAttributes(buttonEl, getActionL10n(actionId));
+ return buttonEl;
+ })
+ : [];
+
+ const messagebar = document.createElement("message-bar");
+ messagebar.setAttribute("type", type || "generic");
+ if (dismissable) {
+ messagebar.setAttribute("dismissable", "");
+ }
+ messagebar.append(messageEl, ...barActions);
+ 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,
+ });
+});