Bug 1543377 - Add report action to html about:addons addon card options panel. r=mstriemer,flod
authorLuca Greco <lgreco@mozilla.com>
Mon, 06 May 2019 18:36:20 +0000
changeset 472760 d15c62c6826bf796cabf60ea8ea23988aedea17a
parent 472759 274e9115edf07409a34b56f128ac5e117b53d877
child 472761 8f6b0e4004271985c3e14d28e9f3c608dc1d498e
push id113048
push usershindli@mozilla.com
push dateTue, 07 May 2019 09:56:38 +0000
treeherdermozilla-inbound@6fd64908d113 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstriemer, flod
bugs1543377
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1543377 - Add report action to html about:addons addon card options panel. r=mstriemer,flod Depends on D26906 Differential Revision: https://phabricator.services.mozilla.com/D27294
toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
toolkit/mozapps/extensions/content/aboutaddons.css
toolkit/mozapps/extensions/content/aboutaddons.html
toolkit/mozapps/extensions/content/aboutaddons.js
toolkit/mozapps/extensions/content/abuse-report-frame.js
toolkit/mozapps/extensions/content/abuse-reports.js
toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js
--- a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
@@ -339,16 +339,17 @@ shortcuts-card-expand-button =
     }
 
 shortcuts-card-collapse-button = Show Less
 
 go-back-button =
     .tooltiptext = Go back
 
 ## Add-on actions
+report-addon-button = Report
 remove-addon-button = Remove
 disable-addon-button = Disable
 enable-addon-button = Enable
 expand-addon-button = More Options
 
 addons-enabled-heading = Enabled
 addons-disabled-heading = Disabled
 
--- a/toolkit/mozapps/extensions/content/aboutaddons.css
+++ b/toolkit/mozapps/extensions/content/aboutaddons.css
@@ -254,16 +254,22 @@ panel-item[action="remove"] {
   fill: currentColor;
   --icon: url("chrome://global/skin/icons/delete.svg");
 }
 
 panel-item[action="install-update"] {
   --icon: url("chrome://global/skin/icons/update-icon.svg");
 }
 
+panel-item[action="report"] {
+  -moz-context-properties: fill;
+  fill: currentColor;
+  --icon: url(chrome://global/skin/icons/warning.svg);
+}
+
 panel-item-separator {
   display: block;
   height: 1px;
   background: var(--in-content-box-border-color);
   padding: 0;
   margin: 6px 0;
 }
 
--- a/toolkit/mozapps/extensions/content/aboutaddons.html
+++ b/toolkit/mozapps/extensions/content/aboutaddons.html
@@ -20,16 +20,18 @@
     </div>
 
     <template name="addon-options">
       <panel-list>
         <panel-item action="toggle-disabled"></panel-item>
         <panel-item data-l10n-id="remove-addon-button" action="remove"></panel-item>
         <panel-item data-l10n-id="install-update-button" action="install-update" badged></panel-item>
         <panel-item-separator></panel-item-separator>
+        <panel-item data-l10n-id="report-addon-button" action="report"></panel-item>
+        <panel-item-separator></panel-item-separator>
         <panel-item data-l10n-id="expand-addon-button" action="expand"></panel-item>
       </panel-list>
     </template>
 
     <template name="plugin-options">
       <panel-list>
         <panel-item data-l10n-id="ask-to-activate-button" action="ask-to-activate"></panel-item>
         <panel-item data-l10n-id="always-activate-button" action="always-activate"></panel-item>
--- a/toolkit/mozapps/extensions/content/aboutaddons.js
+++ b/toolkit/mozapps/extensions/content/aboutaddons.js
@@ -1,14 +1,15 @@
 /* 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] */
 /* exported initialize, hide, show */
 /* import-globals-from aboutaddonsCommon.js */
+/* import-globals-from abuse-reports.js */
 /* global windowRoot */
 
 "use strict";
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.jsm",
 });
@@ -17,16 +18,19 @@ XPCOMUtils.defineLazyPreferenceGetter(
   this, "allowPrivateBrowsingByDefault",
   "extensions.allowPrivateBrowsingByDefault", true);
 XPCOMUtils.defineLazyPreferenceGetter(
   this, "SUPPORT_URL", "app.support.baseURL",
   "", null, val => Services.urlFormatter.formatURL(val));
 
 const UPDATES_RECENT_TIMESPAN = 2 * 24 * 3600000; // 2 days (in milliseconds)
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "ABUSE_REPORT_ENABLED",
+                                      "extensions.abuseReport.enabled", false);
+
 const PLUGIN_ICON_URL = "chrome://global/skin/plugins/pluginGeneric.svg";
 const PERMISSION_MASKS = {
   "ask-to-activate": AddonManager.PERM_CAN_ASK_TO_ACTIVATE,
   enable: AddonManager.PERM_CAN_ENABLE,
   "always-activate": AddonManager.PERM_CAN_ENABLE,
   disable: AddonManager.PERM_CAN_DISABLE,
   "never-activate": AddonManager.PERM_CAN_DISABLE,
   uninstall: AddonManager.PERM_CAN_UNINSTALL,
@@ -357,43 +361,72 @@ customElements.define("panel-item", Pane
 
 class AddonOptions extends HTMLElement {
   connectedCallback() {
     if (this.children.length == 0) {
       this.render();
     }
   }
 
+  get panel() {
+    return this.querySelector("panel-list");
+  }
+
+  updateSeparatorsVisibility() {
+    let lastSeparator;
+    let elWasVisible = false;
+
+    // Collect the panel-list children that are not already hidden.
+    const children = Array.from(this.panel.children)
+                          .filter(el => !el.hidden);
+
+    for (let child of children) {
+      if (child.tagName == "PANEL-ITEM-SEPARATOR") {
+        child.hidden = !elWasVisible;
+        if (!child.hidden) {
+          lastSeparator = child;
+        }
+        elWasVisible = false;
+      } else {
+        elWasVisible = true;
+      }
+    }
+    if (!elWasVisible && lastSeparator) {
+      lastSeparator.hidden = true;
+    }
+  }
+
   render() {
     this.appendChild(importTemplate("addon-options"));
   }
 
   update(card, addon, updateInstall) {
     // Hide remove button if not allowed.
     let removeButton = this.querySelector('[action="remove"]');
     removeButton.hidden = !hasPermission(addon, "uninstall");
 
+    // Hide the report button if reporting is preffed off.
+    this.querySelector('[action="report"]').hidden = !ABUSE_REPORT_ENABLED;
+
     // Set disable label and hide if not allowed.
     let toggleDisabledButton = this.querySelector('[action="toggle-disabled"]');
     let toggleDisabledAction = addon.userDisabled ? "enable" : "disable";
     document.l10n.setAttributes(
       toggleDisabledButton, `${toggleDisabledAction}-addon-button`);
     toggleDisabledButton.hidden = !hasPermission(addon, toggleDisabledAction);
 
     // Set the update button and badge the menu if there's an update.
     this.querySelector('[action="install-update"]').hidden = !updateInstall;
 
-    // The separator isn't needed when expanded (nothing under it) or when the
-    // remove and disable buttons are hidden (nothing above it).
-    let separator = this.querySelector("panel-item-separator");
-    separator.hidden = card.expanded ||
-      removeButton.hidden && toggleDisabledButton.hidden;
-
     // Hide the expand button if we're expanded.
     this.querySelector('[action="expand"]').hidden = card.expanded;
+
+    // Update the separators visibility based on the updated visibility
+    // of the actions in the panel-list.
+    this.updateSeparatorsVisibility();
   }
 }
 customElements.define("addon-options", AddonOptions);
 
 class PluginOptions extends HTMLElement {
   connectedCallback() {
     if (this.children.length == 0) {
       this.render();
@@ -746,16 +779,20 @@ class AddonCard extends HTMLElement {
           loadViewFn("detail", this.addon.id);
           break;
         case "more-options":
           // Open panel on click from the keyboard.
           if (e.mozInputSource == MouseEvent.MOZ_SOURCE_KEYBOARD) {
             this.panel.toggle(e);
           }
           break;
+        case "report":
+          this.panel.hide();
+          openAbuseReport({addonId: addon.id, reportEntryPoint: "menu"});
+          break;
       }
     } else if (e.type == "change") {
       let {name} = e.target;
       if (name == "autoupdate") {
         addon.applyBackgroundUpdates = e.target.value;
       } else if (name == "private-browsing") {
         let policy = WebExtensionPolicy.getByID(addon.id);
         let extension = policy && policy.extension;
--- a/toolkit/mozapps/extensions/content/abuse-report-frame.js
+++ b/toolkit/mozapps/extensions/content/abuse-report-frame.js
@@ -33,37 +33,51 @@
 
       const browser = this.querySelector("browser");
       this.promiseBrowserLoaded = new Promise(resolve => {
         browser.addEventListener("load", () => resolve(browser), {once: true});
       });
 
       document.addEventListener("focus", this);
 
+      this.promiseHtmlAboutAddons.then(win => {
+        win.document.addEventListener("abuse-report:new", this);
+      });
+
       this.update();
     }
 
     disconnectedCallback() {
       this.textContent = "";
       this.browserLoadURI = false;
       this.promiseBrowserLoaded = null;
       this.report = null;
       document.removeEventListener("focus", this);
+
+      this.promiseHtmlAboutAddons.then(win => {
+        win.document.removeEventListener("abuse-report:new", this);
+      });
     }
 
     handleEvent(evt) {
+      // The "abuse-report:new" events are dispatched from the html about:addons sub-frame
+      // (on the html about:addons document).
       // "abuse-report:cancel", "abuse-report:submit" and "abuse-report:updated" are
       // all dispatched from the AbuseReport webcomponent (on the AbuseReport element itself).
       // All the "abuse-report:*" events are also forwarded (dispatched on the frame
       // DOM element itself) to make it easier for the tests to wait for certain conditions
       // to be reached.
       switch (evt.type) {
         case "focus":
           this.focus();
           break;
+        case "abuse-report:new":
+          this.openReport(evt.detail);
+          this.forwardEvent(evt);
+          break;
         case "abuse-report:cancel":
           this.cancelReport();
           this.forwardEvent(evt);
           break;
         case "abuse-report:submit":
           this.onSubmitReport(evt);
           this.forwardEvent(evt);
           break;
--- a/toolkit/mozapps/extensions/content/abuse-reports.js
+++ b/toolkit/mozapps/extensions/content/abuse-reports.js
@@ -1,14 +1,16 @@
 /* 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] */
 
+/* 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).
  */
 
 // Message Bars definitions.
 const ABUSE_REPORT_MESSAGE_BARS = {
   // Idle message-bar (used while the submission is still ongoing).
@@ -42,16 +44,22 @@ const ABUSE_REPORT_MESSAGE_BARS = {
   "ERROR_SERVER": {
     id: "error", actions: ["retry", "cancel"], type: "error",
   },
   "ERROR_UNKNOWN": {
     id: "error", actions: ["retry", "cancel"], type: "error",
   },
 };
 
+function openAbuseReport({addonId, reportEntryPoint}) {
+  document.dispatchEvent(new CustomEvent("abuse-report:new", {
+    detail: {addonId, reportEntryPoint},
+  }));
+}
+
 // 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}`);
--- a/toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_abuse_report.js
@@ -809,8 +809,58 @@ add_task(async function test_abuse_repor
     triggerNewAbuseReport(EXT_ID2, "uninstall");
     await promiseAbuseReportRendered();
     triggerSubmitAbuseReport("fake-reason", "fake-message");
   });
 
   await extension.unload();
   await extension2.unload();
 });
+
+add_task(async function test_trigger_abusereport_from_aboutaddons_menu() {
+  const EXT_ID = "test-report-from-aboutaddons-menu@mochi.test";
+  const extension = await installTestExtension(EXT_ID);
+
+  await openAboutAddons();
+  await gManagerWindow.htmlBrowserLoaded;
+
+  const abuseReportFrameEl = getAbuseReportFrame();
+  ok(abuseReportFrameEl.hidden,
+     "Abuse Report frame should be initially hidden");
+
+  const {contentDocument: doc} = gManagerWindow.getHtmlBrowser();
+
+  const addonCard = doc.querySelector(
+    `addon-list addon-card[addon-id="${extension.id}"]`);
+  ok(addonCard, "Got the addon-card for the test extension");
+
+  const reportButton = addonCard.querySelector("[action=report]");
+  ok(reportButton, "Got the report action for the test extension");
+
+  const onceReportNew = BrowserTestUtils.waitForEvent(
+    abuseReportFrameEl, "abuse-report:new");
+  const onceReportFrameShown = BrowserTestUtils.waitForEvent(
+    abuseReportFrameEl, "abuse-report:frame-shown");
+
+  info("Click the report action and wait for the 'abuse-report:new' event");
+  reportButton.click();
+  const newReportEvent = await onceReportNew;
+
+  Assert.deepEqual(newReportEvent.detail, {
+    addonId: extension.id,
+    reportEntryPoint: "menu",
+  }, "Got the expected details in the 'abuse-report:new' event");
+
+  info("Wait for the abuse report frame to be visible");
+  await onceReportFrameShown;
+  ok(!abuseReportFrameEl.hidden,
+    "Abuse Report frame should be visible");
+
+  info("Wait for the abuse report panel to be rendered");
+  const abuseReportEl = await abuseReportFrameEl.promiseAbuseReport;
+  await promiseAbuseReportRendered(abuseReportEl);
+
+  is(abuseReportEl.addon && abuseReportEl.addon.id, extension.id,
+     "Abuse Report panel rendered for the expected addonId");
+
+  await closeAboutAddons();
+  await extension.unload();
+});