Bug 1525193 - Move actions to more options menu in HTML about:addons r=Gijs,flod
authorMark Striemer <mstriemer@mozilla.com>
Fri, 15 Mar 2019 19:19:21 +0000
changeset 464428 e08f9ad9fcbb
parent 464427 2e1a91cd78fe
child 464429 77dbfa3a6e55
push id35716
push useraciure@mozilla.com
push dateSun, 17 Mar 2019 09:42:17 +0000
treeherdermozilla-central@8ee97c045359 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGijs, flod
bugs1525193
milestone67.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 1525193 - Move actions to more options menu in HTML about:addons r=Gijs,flod Differential Revision: https://phabricator.services.mozilla.com/D21294
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/panel-item.css
toolkit/mozapps/extensions/content/panel-list.css
toolkit/mozapps/extensions/jar.mn
toolkit/mozapps/extensions/test/browser/browser_html_list_view.js
toolkit/themes/shared/icons/delete.svg
toolkit/themes/shared/in-content/common.inc.css
toolkit/themes/shared/jar.inc.mn
toolkit/themes/windows/global/in-content/common.css
--- a/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
+++ b/toolkit/locales/en-US/toolkit/about/aboutAddons.ftl
@@ -325,11 +325,12 @@ shortcuts-card-collapse-button = Show Le
 
 go-back-button =
     .tooltiptext = Go back
 
 ## Add-on actions
 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
@@ -57,8 +57,42 @@
 }
 
 .addon-description {
   font-size: 14px;
   line-height: 20px;
   color: var(--grey-60);
   font-weight: 400;
 }
+
+.more-options-menu {
+  position: relative;
+  /* Add some negative margin to account for the button's padding */
+  margin-top: -10px;
+  margin-inline-end: -8px;
+}
+
+button[action="more-options"] {
+  min-width: auto;
+  min-height: auto;
+  width: 24px;
+  height: 24px;
+  margin: 0;
+  background: url("chrome://global/skin/icons/more.svg") no-repeat center center;
+}
+
+panel-item[action="remove"] {
+  -moz-context-properties: fill;
+  fill: currentColor;
+  --icon: url("chrome://global/skin/icons/delete.svg");
+}
+
+panel-item-separator {
+  display: block;
+  height: 1px;
+  background: var(--panel-border-color);
+  padding: 0;
+  margin: 6px 0;
+}
+
+panel-item-separator[hidden] {
+  display: none;
+}
--- a/toolkit/mozapps/extensions/content/aboutaddons.html
+++ b/toolkit/mozapps/extensions/content/aboutaddons.html
@@ -15,15 +15,35 @@
 
     <template name="card">
       <div class="card addon">
         <img class="card-heading-icon addon-icon"/>
         <div class="card-contents">
           <span class="addon-name"></span>
           <span class="addon-description"></span>
         </div>
-        <div class="card-actions">
-          <button action="toggle-disabled"></button><button action="remove" data-l10n-id="remove-addon-button"></button>
+        <div class="more-options-menu">
+          <button action="more-options"></button>
+          <panel-list>
+            <panel-item action="toggle-disabled"></panel-item>
+            <panel-item data-l10n-id="remove-addon-button" action="remove"></panel-item>
+            <panel-item-separator></panel-item-separator>
+            <panel-item data-l10n-id="expand-addon-button" action="expand"></panel-item>
+          </panel-list>
         </div>
       </div>
     </template>
+
+    <template name="panel-list">
+      <link rel="stylesheet" href="chrome://mozapps/content/extensions/panel-list.css"/>
+      <div class="arrow top"></div>
+      <div class="list">
+        <slot/>
+      </div>
+      <div class="arrow bottom"></div>
+    </template>
+
+    <template name="panel-item">
+      <link rel="stylesheet" href="chrome://mozapps/content/extensions/panel-item.css"/>
+      <button><slot/></button>
+    </template>
   </body>
 </html>
--- a/toolkit/mozapps/extensions/content/aboutaddons.js
+++ b/toolkit/mozapps/extensions/content/aboutaddons.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint max-len: ["error", 80] */
 /* exported initialize, hide, show */
 
 "use strict";
 
 const {XPCOMUtils} = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm");
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
 });
 
 const PLUGIN_ICON_URL = "chrome://global/skin/plugins/pluginGeneric.svg";
 const PERMISSION_MASKS = {
   enable: AddonManager.PERM_CAN_ENABLE,
@@ -35,16 +36,187 @@ function importTemplate(name) {
   }
   let template = _templates[name];
   if (template) {
     return document.importNode(template.content, true);
   }
   throw new Error(`Unknown template: ${name}`);
 }
 
+class PanelList extends HTMLElement {
+  static get observedAttributes() {
+    return ["open"];
+  }
+
+  constructor() {
+    super();
+    this.attachShadow({mode: "open"});
+    this.shadowRoot.appendChild(importTemplate("panel-list"));
+  }
+
+  attributeChangedCallback(name, oldVal, newVal) {
+    if (name == "open" && newVal != oldVal) {
+      if (this.open) {
+        this.onShow();
+      } else {
+        this.onHide();
+      }
+    }
+  }
+
+  get open() {
+    return this.hasAttribute("open");
+  }
+
+  set open(val) {
+    if (val) {
+      this.setAttribute("open", "true");
+    } else {
+      this.removeAttribute("open");
+    }
+  }
+
+  show(triggeringEvent) {
+    this.open = true;
+    this.triggeringEvent = triggeringEvent;
+  }
+
+  hide(triggeringEvent) {
+    this.open = false;
+    this.triggeringEvent = triggeringEvent;
+  }
+
+  toggle(triggeringEvent) {
+    if (this.open) {
+      this.hide(triggeringEvent);
+    } else {
+      this.show(triggeringEvent);
+    }
+  }
+
+  async setAlign() {
+    // Set the showing attribute to hide the panel until its alignment is set.
+    this.setAttribute("showing", "true");
+    // Tell the parent node to hide any overflow in case the panel extends off
+    // the page before the alignment is set.
+    this.parentNode.style.overflow = "hidden";
+
+    // Wait for a layout flush, then find the bounds.
+    let {height, width, y, left, right, winHeight, winWidth} =
+      await new Promise(resolve => {
+        requestAnimationFrame(() => setTimeout(() => {
+          // Use y since top is reserved.
+          let {y, left, right} =
+            window.windowUtils.getBoundsWithoutFlushing(this.parentNode);
+          let {height, width} =
+            window.windowUtils.getBoundsWithoutFlushing(this);
+          resolve({
+            height, width, y, left, right,
+            winHeight: innerHeight, winWidth: innerWidth,
+          });
+        }, 0));
+      });
+
+    // Calculate the left/right alignment.
+    let align;
+    if (Services.locale.isAppLocaleRTL) {
+      // Prefer aligning on the right.
+      align = right - width + 14 < 0 ? "left" : "right";
+    } else {
+      // Prefer aligning on the left.
+      align = left + width - 14 > winWidth ? "right" : "left";
+    }
+
+    // "bottom" style will move the panel down 30px from the top of the parent.
+    let valign = y + height + 30 > winHeight ? "top" : "bottom";
+
+    // Set the alignments and show the panel.
+    this.setAttribute("align", align);
+    this.setAttribute("valign", valign);
+    this.parentNode.style.overflow = "";
+    this.removeAttribute("showing");
+
+    // Send the shown event after the next paint.
+    requestAnimationFrame(() => this.sendEvent("shown"));
+  }
+
+  addHideListeners() {
+    // Hide when a panel-item is clicked in the list.
+    this.addEventListener("click", this);
+    // Hide when a click is initiated outside the panel.
+    document.addEventListener("mousedown", this);
+    // Hide if focus changes and the panel isn't in focus.
+    document.addEventListener("focusin", this);
+    // Hide on resize, scroll or losing window focus.
+    window.addEventListener("resize", this);
+    window.addEventListener("scroll", this);
+    window.addEventListener("blur", this);
+  }
+
+  removeHideListeners() {
+    this.removeEventListener("click", this);
+    document.removeEventListener("mousedown", this);
+    document.removeEventListener("focusin", this);
+    window.removeEventListener("resize", this);
+    window.removeEventListener("scroll", this);
+    window.removeEventListener("blur", this);
+  }
+
+  handleEvent(e) {
+    // Ignore the event if it caused the panel to open.
+    if (e == this.triggeringEvent) {
+      return;
+    }
+
+    switch (e.type) {
+      case "resize":
+      case "scroll":
+      case "blur":
+        this.hide();
+        break;
+      case "click":
+        if (e.target.tagName == "PANEL-ITEM") {
+          this.hide();
+        }
+        break;
+      case "mousedown":
+      case "focusin":
+        // If the target isn't in the panel, hide. This will close when focus
+        // moves out of the panel, or there's a click started outside the panel.
+        if (!e.target || e.target.closest("panel-list") != this) {
+          this.hide();
+        }
+        break;
+    }
+  }
+
+  onShow() {
+    this.setAlign();
+    this.addHideListeners();
+  }
+
+  onHide() {
+    this.removeHideListeners();
+  }
+
+  sendEvent(name, detail) {
+    this.dispatchEvent(new CustomEvent(name, {detail}));
+  }
+}
+customElements.define("panel-list", PanelList);
+
+class PanelItem extends HTMLElement {
+  constructor() {
+    super();
+    this.attachShadow({mode: "open"});
+    this.shadowRoot.appendChild(importTemplate("panel-item"));
+  }
+}
+customElements.define("panel-item", PanelItem);
+
 /**
  * A card component for managing an add-on. It should be initialized by setting
  * the add-on with `setAddon()` before being connected to the document.
  *
  *    let card = document.createElement("addon-card");
  *    card.setAddon(addon);
  *    document.body.appendChild(card);
  */
@@ -82,48 +254,71 @@ class AddonCard extends HTMLElement {
     if (addon.type == "plugin") {
       icon = PLUGIN_ICON_URL;
     } else {
       icon = AddonManager.getPreferredIconURL(addon, 32, window);
     }
     card.querySelector(".addon-icon").src = icon;
     card.querySelector(".addon-name").textContent = addon.name;
     card.querySelector(".addon-description").textContent = addon.description;
-    card.querySelector('[action="remove"]').hidden =
-      !hasPermission(addon, "uninstall");
+    let removeButton = card.querySelector('[action="remove"]');
+    removeButton.hidden = !hasPermission(addon, "uninstall");
 
     let disableButton = card.querySelector('[action="toggle-disabled"]');
     let disableAction = addon.userDisabled ? "enable" : "disable";
     document.l10n.setAttributes(
       disableButton, `${disableAction}-addon-button`);
     disableButton.hidden = !hasPermission(addon, disableAction);
+
+    // If disable and remove are hidden, we don't need the separator.
+    let separator = card.querySelector("panel-item-separator");
+    separator.hidden = removeButton.hidden && disableButton.hidden;
   }
 
   render() {
     this.textContent = "";
 
     let {addon} = this;
     if (!addon) {
       throw new Error("addon-card must be initialized with setAddon()");
     }
 
     this.card = importTemplate("card").firstElementChild;
     this.setAttribute("addon-id", addon.id);
 
     // Set the contents.
     this.update();
 
-    this.addEventListener("click", async (e) => {
-      switch (e.target.getAttribute("action")) {
+    let panel = this.card.querySelector("panel-list");
+
+    let moreOptionsButton = this.card.querySelector('[action="more-options"]');
+    // Open on mousedown when the mouse is used.
+    moreOptionsButton.addEventListener("mousedown", (e) => {
+      panel.toggle(e);
+    });
+    // Open when there's a click from the keyboard.
+    moreOptionsButton.addEventListener("click", (e) => {
+      if (e.mozInputSource == MouseEvent.MOZ_SOURCE_KEYBOARD) {
+        panel.toggle(e);
+      }
+    });
+
+    panel.addEventListener("click", async (e) => {
+      let action = e.target.getAttribute("action");
+      switch (action) {
         case "toggle-disabled":
           if (addon.userDisabled) {
             await addon.enable();
           } else {
             await addon.disable();
           }
+          if (e.mozInputSource == MouseEvent.MOZ_SOURCE_KEYBOARD) {
+            // Refocus the open menu button so it's clear where the focus is.
+            this.querySelector('[action="more-options"]').focus();
+          }
           break;
         case "remove":
           await addon.uninstall();
           break;
       }
     });
 
     this.appendChild(this.card);
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/panel-item.css
@@ -0,0 +1,23 @@
+button {
+  background-color: transparent;
+  background-image: var(--icon);
+  background-position: 16px center;
+  background-repeat: no-repeat;
+  background-size: 16px;
+  border: none;
+  display: block;
+  font-size: inherit;
+  padding: 4px 40px;
+  padding-inline-end: 12px;
+  text-align: start;
+  width: 100%;
+}
+
+button:focus,
+button:hover {
+  background-color: var(--in-content-button-background);
+}
+
+button:hover:active {
+  background-color: var(--in-content-button-background-hover);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/content/panel-list.css
@@ -0,0 +1,73 @@
+:host(:not([open])) {
+  display: none;
+}
+
+:host([showing]) {
+  visibility: hidden;
+}
+
+:host {
+  position: absolute;
+  background: var(--in-content-box-background);
+  border: 1px solid var(--panel-border-color);
+  border-radius: var(--panel-border-radius);
+  padding: 6px 0;
+  margin-bottom: 16px;
+  box-shadow: var(--shadow-30);
+  min-width: 12em;
+  z-index: 1;
+  white-space: nowrap;
+}
+
+:host([valign="top"]) {
+  bottom: 30px;
+}
+
+:host([valign="bottom"]) {
+  top: 30px;
+}
+
+:host([align="left"]) {
+  left: -14px;
+}
+
+:host([align="right"]) {
+  right: -14px;
+}
+
+.list {
+  margin: 0;
+  padding: 0;
+}
+
+.arrow {
+  width: 18px;
+  height: 9px;
+  -moz-context-properties: fill, stroke;
+  stroke: var(--panel-border-color);
+  fill: var(--in-content-box-background);
+  background: url("chrome://global/skin/arrow/panelarrow-vertical.svg");
+  position: absolute;
+}
+
+:host([valign="bottom"]) > .arrow.bottom,
+:host([valign="top"]) > .arrow.top {
+  display: none;
+}
+
+.arrow.top {
+  top: -9px;
+}
+
+.arrow.bottom {
+  bottom: -9px;
+  transform: scaleY(-1);
+}
+
+:host([align="left"]) > .arrow {
+  left: 16px;
+}
+
+:host([align="right"]) > .arrow {
+  right: 16px;
+}
--- a/toolkit/mozapps/extensions/jar.mn
+++ b/toolkit/mozapps/extensions/jar.mn
@@ -17,9 +17,11 @@ toolkit.jar:
   content/mozapps/extensions/blocklist.xul                      (content/blocklist.xul)
   content/mozapps/extensions/blocklist.js                       (content/blocklist.js)
   content/mozapps/extensions/pluginPrefs.xul                    (content/pluginPrefs.xul)
   content/mozapps/extensions/pluginPrefs.js                     (content/pluginPrefs.js)
   content/mozapps/extensions/OpenH264-license.txt               (content/OpenH264-license.txt)
   content/mozapps/extensions/aboutaddons.html                   (content/aboutaddons.html)
   content/mozapps/extensions/aboutaddons.js                     (content/aboutaddons.js)
   content/mozapps/extensions/aboutaddons.css                    (content/aboutaddons.css)
+  content/mozapps/extensions/panel-list.css                     (content/panel-list.css)
+  content/mozapps/extensions/panel-item.css                     (content/panel-item.css)
 #endif
--- a/toolkit/mozapps/extensions/test/browser/browser_html_list_view.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_html_list_view.js
@@ -128,16 +128,115 @@ add_task(async function testExtensionLis
 
   addon = await AddonManager.getAddonByID("test@mochi.test");
   ok(!addon, "The addon is not longer found");
 
   await extension.unload();
   await closeView(win);
 });
 
+add_task(async function testKeyboardSupport() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      name: "Test extension",
+      applications: {gecko: {id: "test@mochi.test"}},
+    },
+    useAddonManager: "temporary",
+  });
+  await extension.startup();
+
+  let win = await loadInitialView("extension");
+  let doc = win.document;
+
+  // Some helpers.
+  let tab = event => EventUtils.synthesizeKey("VK_TAB", event);
+  let space = () => EventUtils.synthesizeKey(" ", {});
+  let isFocused = (el, msg) => is(doc.activeElement, el, msg);
+
+  // Find the addon-list to listen for events.
+  let list = doc.querySelector("addon-list");
+  let enabledSection = getSection(doc, "enabled");
+  let disabledSection = getSection(doc, "disabled");
+
+  // Find the card.
+  let [card] = getTestCards(list);
+  is(card.addon.id, "test@mochi.test", "The right card is found");
+
+  // Focus the more options menu button.
+  let moreOptionsButton = card.querySelector('[action="more-options"]');
+  moreOptionsButton.focus();
+  isFocused(moreOptionsButton, "The more options button is focused");
+
+  // Test opening and closing the menu.
+  let moreOptionsMenu = card.querySelector("panel-list");
+  is(moreOptionsMenu.open, false, "The menu is closed");
+  space();
+  is(moreOptionsMenu.open, true, "The menu is open");
+  space();
+  is(moreOptionsMenu.open, false, "The menu is closed");
+
+  // Test tabbing out of the menu.
+  space();
+  is(moreOptionsMenu.open, true, "The menu is open");
+  tab({shiftKey: true});
+  is(moreOptionsMenu.open, false, "Tabbing away from the menu closes it");
+  tab();
+  isFocused(moreOptionsButton, "The button is focused again");
+  space();
+  is(moreOptionsMenu.open, true, "The menu is open");
+  tab();
+  tab();
+  tab();
+  isFocused(moreOptionsButton, "The last item is focused");
+  tab();
+  is(moreOptionsMenu.open, false, "Tabbing out of the menu closes it");
+
+  // Focus the button again, focus may have moved out of the browser.
+  moreOptionsButton.focus();
+  isFocused(moreOptionsButton, "The button is focused again");
+
+  // Open the menu to test contents.
+  let shown = BrowserTestUtils.waitForEvent(moreOptionsMenu, "shown");
+  space();
+  is(moreOptionsMenu.open, true, "The menu is open");
+  // Wait for the panel to be shown.
+  await shown;
+
+  // Disable the add-on.
+  let toggleDisableButton = card.querySelector('[action="toggle-disabled"]');
+  tab();
+  isFocused(toggleDisableButton, "The disable button is focused");
+  is(card.parentNode, enabledSection, "The card is in the enabled section");
+  let disabled = BrowserTestUtils.waitForEvent(list, "move");
+  space();
+  await disabled;
+  is(moreOptionsMenu.open, false, "The menu is closed");
+  is(card.parentNode, disabledSection,
+     "The card is now in the disabled section");
+
+  // Open the menu again.
+  shown = BrowserTestUtils.waitForEvent(moreOptionsMenu, "shown");
+  isFocused(moreOptionsButton, "The more options button is focused");
+  space();
+  await shown;
+
+  // Remove the add-on.
+  tab();
+  tab();
+  let removeButton = card.querySelector('[action="remove"]');
+  isFocused(removeButton, "The remove button is focused");
+  let removed = BrowserTestUtils.waitForEvent(list, "remove");
+  space();
+  await removed;
+  is(card.parentNode, null, "The card is no longer on the page");
+
+  await extension.unload();
+  await closeView(win);
+});
+
 add_task(async function testExtensionReordering() {
   let extensions = createExtensions([
     {name: "Extension One"},
     {name: "This is last"},
     {name: "An extension, is first"},
   ]);
 
   await Promise.all(extensions.map(extension => extension.startup()));
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/icons/delete.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
+  <path fill-rule="evenodd" fill="context-fill" fill-opacity="context-fill-opacity" d="M20 5h-5a3 3 0 1 0-6 0H4a1 1 0 0 0 0 2h1v12a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V7h1a1 1 0 1 0 0-2zm-8-2a2 2 0 0 1 2 2h-4a2 2 0 0 1 2-2zM7 7v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V7H7zm2.539 2.002a.5.5 0 0 0-.503.498l-.034 8a.5.5 0 0 0 1 .004l.034-8a.5.5 0 0 0-.497-.502zm4.497.498a.5.5 0 0 1 1 .004l-.034 8a.5.5 0 0 1-1-.004l.034-8zM12 9.002a.5.5 0 0 0-.502.498l-.034 8a.5.5 0 0 0 1 .004l.034-8A.5.5 0 0 0 12 9.002z"/>
+</svg>
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -43,16 +43,19 @@
   --in-content-button-background-active: var(--grey-90-a30);
   --in-content-primary-button-background: var(--blue-60);
   --in-content-primary-button-background-hover: var(--blue-70);
   --in-content-primary-button-background-active: var(--blue-80);
   --in-content-table-background: #ebebeb;
   --in-content-table-border-dark-color: #d1d1d1;
   --in-content-table-header-background: #0a84ff;
 
+  --panel-border-color: var(--grey-90-a20);
+  --panel-border-radius: 2px; /* This is overridden on Windows */
+
   /* The photon animation curve */
   --animation-curve: cubic-bezier(.07,.95,0,1);
 
   --blue-40: #45a1ff;
   --blue-40-a10: rgb(69, 161, 255, 0.1);
   --blue-50: #0a84ff;
   --blue-50-a30: rgba(10, 132, 255, 0.3);
   --blue-60: #0060df;
@@ -72,16 +75,18 @@
   --purple-70-a40: rgba(98, 0, 164, 0.4);
   --red-50: #ff0039;
   --red-50-a30: rgba(255, 0, 57, 0.3);
   --red-60: #d70022;
   --yellow-50: #ffe900;
   --yellow-90: #3e2800;
 
   --shadow-10: 0 1px 4px var(--grey-90-a10);
+  --shadow-30: 0 4px 16px var(--grey-90-a10);
+
   --card-padding: 16px;
   --card-shadow: var(--shadow-10);
   --card-outline-color: var(--grey-30);
   --card-shadow-hover: var(--card-shadow), 0 0 0 5px var(--card-outline-color);
   --card-shadow-focus: 0 0 0 2px var(--blue-50), 0 0 0 6px var(--blue-50-a30);
 }
 
 html|html,
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -28,16 +28,17 @@ toolkit.jar:
   skin/classic/global/icons/autoscroll-horizontal.svg      (../../shared/icons/autoscroll-horizontal.svg)
   skin/classic/global/icons/autoscroll-vertical.svg        (../../shared/icons/autoscroll-vertical.svg)
   skin/classic/global/icons/calendar-arrow-left.svg        (../../shared/icons/calendar-arrow-left.svg)
   skin/classic/global/icons/calendar-arrow-right.svg       (../../shared/icons/calendar-arrow-right.svg)
   skin/classic/global/icons/check.svg                      (../../shared/icons/check.svg)
   skin/classic/global/icons/check-partial.svg              (../../shared/icons/check-partial.svg)
   skin/classic/global/icons/close.svg                      (../../shared/icons/close.svg)
   skin/classic/global/icons/columnpicker.svg               (../../shared/icons/columnpicker.svg)
+  skin/classic/global/icons/delete.svg                     (../../shared/icons/delete.svg)
   skin/classic/global/icons/error.svg                      (../../shared/icons/error.svg)
   skin/classic/global/icons/find-previous-arrow.svg        (../../shared/icons/find-previous-arrow.svg)
   skin/classic/global/icons/find-next-arrow.svg            (../../shared/icons/find-next-arrow.svg)
   skin/classic/global/icons/heart.svg                      (../../shared/icons/heart.svg)
   skin/classic/global/icons/help.svg                       (../../shared/icons/help.svg)
   skin/classic/global/icons/info.svg                       (../../shared/incontent-icons/info.svg)
   skin/classic/global/icons/loading.png                    (../../shared/icons/loading.png)
   skin/classic/global/icons/loading@2x.png                 (../../shared/icons/loading@2x.png)
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -1,14 +1,18 @@
 /* - 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/. */
 
 %include ../../../shared/in-content/common.inc.css
 
+:root {
+  --panel-border-radius: 0;
+}
+
 xul|*.menulist-dropmarker {
   margin-top: 1px;
   margin-bottom: 1px;
 }
 
 xul|checkbox,
 xul|radio {
   padding-inline-start: 0;