Bug 1683865 - Replace xul:image usage in email address headers. r=mkmelin
authorHenry Wilkes <henry@thunderbird.net>
Mon, 17 May 2021 13:18:00 +0300
changeset 42445 b296c12d6577ce6f682ff28e4a9b30c3a331e338
parent 42444 a04cc2fc06d04d14d1d7aa44a3a7920fc1d4301e
child 42446 1934a66dc57ecc66bdd1ce6d3a706ae60641994c
push id3121
push usertbbld-merge
push dateMon, 31 May 2021 21:07:04 +0000
treeherdercomm-beta@a02c59e58d79 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin
bugs1683865
Bug 1683865 - Replace xul:image usage in email address headers. r=mkmelin Also drop the chat status icon since it is no longer particularly useful and the implementation only worked in some cases, and was broken for others because there was a typo that existed since its implementation [In "Object.prototype.hasOwnProperty.call(onlineContacts, chatAddresses)", "chatAddresses" should have been "chatAddress", but this goes unnoticed when the chatAddresses Array coerces to the same string value as chatAddress when it contains exactly one element, but this was not the case if the contact had jabber or gtalk chat ids]. Differential Revision: https://phabricator.services.mozilla.com/D114994
mail/base/content/mailWidgets.js
mail/base/content/messenger.css
mail/base/content/msgHdrView.js
mail/locales/en-US/messenger/messenger.ftl
mail/test/browser/message-header/browser_messageHeader.js
mail/themes/osx/mail/messageHeader.css
mail/themes/shared/mail/messageHeader.css
--- a/mail/base/content/mailWidgets.js
+++ b/mail/base/content/mailWidgets.js
@@ -4,18 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* import-globals-from ../../components/compose/content/addressingWidgetOverlay.js */
 
 /* global MozElements */
 /* global MozXULElement */
 /* global openUILink */
 /* global MessageIdClick */
-/* global onClickEmailStar */
-/* global onClickEmailPresence */
+/* global EditContact */
+/* global AddContact */
 /* global gFolderDisplay */
 /* global UpdateEmailNodeDetails */
 /* global PluralForm */
 /* global UpdateExtraAddressProcessing */
 /* global onRecipientsChanged */
 
 // Wrap in a block to prevent leaking to window scope.
 {
@@ -277,61 +277,74 @@
         return;
       }
 
       this.setAttribute("context", "messageIdsHeaderfieldContext");
 
       this.mMessageIds = [];
       this.showFullMessageIds = false;
 
-      this.toggleIcon = document.createXULElement("image");
+      this.toggleButton = document.createElement("button");
+      this.toggleButton.classList.add("emailActionIconButton");
+      // FIXME: Is the twisty icon the best representation since toggling the
+      // twisty icon does not expand hidden content vertically?
+      // A list of <details> elements may be more appropriate to capture this,
+      // and would be more accessible.
+      // NOTE: We currently style the toggle button as a twisty icon, which
+      // relies on the CSS -moz-locale-dir(rtl) selector to choose the image.
+      // Therefore, we use a <div> rather than an <img> for convenience.
+      // However, this means we cannot set alt text that describes the behaviour
+      // of the button to screen readers. We use aria-expanded to hint that the
+      // behaviour is _similar_ to tree expansion.
+      this.toggleButton.setAttribute("aria-expanded", this.showFullMessageIds);
+      this.toggleIcon = document.createElement("div");
       this.toggleIcon.classList.add("emailToggleHeaderfield");
-      this.toggleIcon.addEventListener("click", () => {
+      this.toggleButton.appendChild(this.toggleIcon);
+
+      this.toggleButton.addEventListener("click", () => {
         this._toggleWrap();
       });
-      this.appendChild(this.toggleIcon);
+      this.appendChild(this.toggleButton);
 
       this.headerValue = document.createXULElement("hbox");
       this.headerValue.classList.add("headerValue");
       this.headerValue.setAttribute("flex", "1");
       this.appendChild(this.headerValue);
     }
 
     _toggleWrap() {
+      this.showFullMessageIds = !this.showFullMessageIds;
+      this.toggleButton.setAttribute("aria-expanded", this.showFullMessageIds);
+      this.toggleIcon.classList.toggle("open", this.showFullMessageIds);
       for (let i = 0; i < this.headerValue.children.length; i += 2) {
-        if (!this.showFullMessageIds) {
-          this.toggleIcon.classList.add("open");
+        if (this.showFullMessageIds) {
           this.headerValue.children[i].setAttribute(
             "label",
             this.mMessageIds[i / 2]
           );
           this.headerValue.children[i].removeAttribute("tooltiptext");
-          this.headerValue.removeAttribute("singleline");
         } else {
-          this.toggleIcon.classList.remove("open");
           this.headerValue.children[i].setAttribute("label", i / 2 + 1);
           this.headerValue.children[i].setAttribute(
             "tooltiptext",
             this.mMessageIds[i / 2]
           );
         }
       }
-
-      this.showFullMessageIds = !this.showFullMessageIds;
     }
 
     fillMessageIdNodes() {
       while (
         this.headerValue.children.length >
         this.mMessageIds.length * 2 - 1
       ) {
         this.headerValue.lastElementChild.remove();
       }
 
-      this.toggleIcon.hidden = this.mMessageIds.length <= 1;
+      this.toggleButton.hidden = this.mMessageIds.length <= 1;
 
       for (let i = 0; i < this.mMessageIds.length; i++) {
         if (i * 2 <= this.headerValue.children.length - 1) {
           this._updateMessageIdNode(
             this.headerValue.children[i * 2],
             i + 1,
             this.mMessageIds[i],
             this.mMessageIds.length
@@ -383,113 +396,141 @@
   }
   customElements.define(
     "mail-messageids-headerfield",
     MozMailMessageidsHeaderfield
   );
 
   class MozMailEmailaddress extends MozXULElement {
     static get observedAttributes() {
-      return [
-        "hascard",
-        "label",
-        "crop",
-        "tooltipstar",
-        "chatStatus",
-        "presenceTooltip",
-      ];
+      return ["label", "crop"];
     }
 
     connectedCallback() {
       if (this.hasChildNodes() || this.delayConnectedCallback()) {
         return;
       }
       this.classList.add("emailDisplayButton");
       this.setAttribute("context", "emailAddressPopup");
+      // FIXME: popup is not accessible to keyboard users.
       this.setAttribute("popup", "emailAddressPopup");
       this.setAttribute("align", "center");
 
       const label = document.createXULElement("label");
       label.classList.add("emaillabel");
 
-      const emailStarImage = document.createXULElement("image");
-      emailStarImage.classList.add("emailStar");
-      emailStarImage.setAttribute("context", "emailAddressPopup");
-
-      const emailPresenceImage = document.createXULElement("image");
-      emailPresenceImage.classList.add("emailPresence");
+      // FIXME: The star button uses "title" to describe its action, but the
+      // tooltip is not currently accessible to keyboard users and doesn't
+      // appear as a node in the accessibility tree.
+      this.starButton = document.createElement("button");
+      this.starButton.classList.add("emailActionIconButton");
+      this.starButton.setAttribute("contextmenu", "emailAddressPopup");
+      this.starIcon = document.createElement("img");
+      this.starIcon.classList.add("emailStar");
+      this.starButton.appendChild(this.starIcon);
+
+      this.starButton.addEventListener("mousedown", event => {
+        // Don't trigger popup.
+        event.preventDefault();
+      });
+      this.starButton.addEventListener("click", this.onClickStar.bind(this));
 
       this.appendChild(label);
-      this.appendChild(emailStarImage);
-      this.appendChild(emailPresenceImage);
-
+      this.appendChild(this.starButton);
+
+      this.createdStarButton = true;
+
+      this._updateStarButton();
       this._update();
-      this._setupEventListeners();
+    }
+
+    onClickStar(event) {
+      // Only care about left-click events
+      if (event.button != 0) {
+        return;
+      }
+
+      // FIXME: both methods use properties set outside of this class in
+      // msgHdrView.js. Would be cleaner if the logic could be brought within
+      // this class since they are currently quite interdependent.
+      if (this.hasCard) {
+        EditContact(this);
+      } else {
+        AddContact(this);
+      }
+    }
+
+    _updateStarButton() {
+      let src;
+      let title;
+      if (this.hasCard) {
+        src = "chrome://messenger/skin/icons/starred.svg";
+        // Set the alt text.
+        document.l10n.setAttributes(
+          this.starIcon,
+          "message-header-address-in-address-book-icon"
+        );
+        title = document.getElementById("editContactItem").label;
+      } else {
+        src = "chrome://messenger/skin/icons/star.svg";
+        // Set the alt text.
+        document.l10n.setAttributes(
+          this.starIcon,
+          "message-header-address-not-in-address-book-icon"
+        );
+        title = document.getElementById("addToAddressBookItem").label;
+      }
+      this.starIcon.setAttribute("src", src);
+      this.starIcon.classList.toggle("starredFill", this.hasCard);
+      this.starButton.setAttribute("title", title);
+    }
+
+    /**
+     * Set the address book action for the star button depending on whether the
+     * shown address exists in the address book.
+     *
+     * @param {boolean} hasCard - Whether the shown address is already in the
+     *   address book.
+     */
+    setAddressBookState(hasCard) {
+      if (hasCard === this.hasCard) {
+        return;
+      }
+      this.hasCard = hasCard;
+      if (this.createdStarButton) {
+        this._updateStarButton();
+      }
     }
 
     attributeChangedCallback() {
       if (!this.isConnectedAndReady) {
         return;
       }
       this._update();
     }
 
     _update() {
       const emailLabel = this.querySelector(".emaillabel");
-      const emailStarImage = this.querySelector(".emailStar");
-      const emailPresenceImage = this.querySelector(".emailPresence");
 
       this._updateNodeAttributes(emailLabel, "crop");
       this._updateNodeAttributes(emailLabel, "value", "label");
-
-      this._updateNodeAttributes(emailStarImage, "hascard");
-      this._updateNodeAttributes(emailStarImage, "chatStatus");
-      this._updateNodeAttributes(emailStarImage, "tooltiptext", "tooltipstar");
-
-      this._updateNodeAttributes(emailPresenceImage, "chatStatus");
-      this._updateNodeAttributes(
-        emailPresenceImage,
-        "tooltiptext",
-        "presenceTooltip"
-      );
     }
 
     _updateNodeAttributes(attrNode, attr, mappedAttr) {
       mappedAttr = mappedAttr || attr;
 
       if (
         this.hasAttribute(mappedAttr) &&
         this.getAttribute(mappedAttr) != null
       ) {
         attrNode.setAttribute(attr, this.getAttribute(mappedAttr));
       } else {
         attrNode.removeAttribute(attr);
       }
     }
-
-    _setupEventListeners() {
-      const emailStarImage = this.querySelector(".emailStar");
-      const emailPresenceImage = this.querySelector(".emailPresence");
-
-      emailStarImage.addEventListener("mousedown", event => {
-        event.preventDefault();
-      });
-
-      emailStarImage.addEventListener("click", event => {
-        onClickEmailStar(event, this);
-      });
-
-      emailPresenceImage.addEventListener("mousedown", event => {
-        event.preventDefault();
-      });
-
-      emailPresenceImage.addEventListener("click", event => {
-        onClickEmailPresence(event, this);
-      });
-    }
   }
   customElements.define("mail-emailaddress", MozMailEmailaddress);
 
   class MozMailEmailheaderfield extends MozXULElement {
     connectedCallback() {
       if (this.hasChildNodes() || this.delayConnectedCallback()) {
         return;
       }
--- a/mail/base/content/messenger.css
+++ b/mail/base/content/messenger.css
@@ -20,20 +20,16 @@
   --panelui-subview-transition-duration: 150ms;
 }
 
 /* message header widgets */
 .emailDisplayButton {
   -moz-user-focus: normal;
 }
 
-.emailStar {
-  -moz-user-focus: normal;
-}
-
 mail-tagfield[collapsed="true"] {
   display: none;
 }
 
 .remote-gloda-search-container {
   min-width: 10em;
 }
 
--- a/mail/base/content/msgHdrView.js
+++ b/mail/base/content/msgHdrView.js
@@ -25,19 +25,16 @@ var { DisplayNameUtils } = ChromeUtils.i
   "resource:///modules/DisplayNameUtils.jsm"
 );
 var { MailServices } = ChromeUtils.import(
   "resource:///modules/MailServices.jsm"
 );
 var { GlodaUtils } = ChromeUtils.import(
   "resource:///modules/gloda/GlodaUtils.jsm"
 );
-var { Status: statusUtils } = ChromeUtils.import(
-  "resource:///modules/imStatusUtils.jsm"
-);
 
 XPCOMUtils.defineLazyServiceGetter(
   this,
   "gMIMEService",
   "@mozilla.org/mime;1",
   "nsIMIMEService"
 );
 
@@ -157,28 +154,21 @@ var currentAttachments = [];
  *
  * @param prefix  the name of the view (e.g. "expanded")
  * @param headerListInfo  entry from a header list.
  */
 function createHeaderEntry(prefix, headerListInfo) {
   var partialIDName = prefix + headerListInfo.name;
   this.enclosingBox = document.getElementById(partialIDName + "Box");
   this.enclosingRow = document.getElementById(partialIDName + "Row");
-  this.textNode = document.getElementById(partialIDName + "Value");
   this.isNewHeader = false;
   this.valid = false;
 
   if ("useToggle" in headerListInfo) {
     this.useToggle = headerListInfo.useToggle;
-    if (this.useToggle) {
-      // find the toggle icon in the document
-      this.toggleIcon = this.enclosingBox.toggleIcon;
-      this.longTextNode = this.enclosingBox.longEmailAddresses;
-      this.textNode = this.enclosingBox.emailAddresses;
-    }
   } else {
     this.useToggle = false;
   }
 
   if ("outputFunction" in headerListInfo) {
     this.outputFunction = headerListInfo.outputFunction;
   } else {
     this.outputFunction = updateHeaderValue;
@@ -1408,75 +1398,22 @@ function updateEmailAddressNode(emailAdd
     UpdateEmailNodeDetails(address.emailAddress, emailAddressNode);
   }
 }
 
 function UpdateEmailNodeDetails(aEmailAddress, aDocumentNode, aCardDetails) {
   // If we haven't been given specific details, search for a card.
   var cardDetails =
     aCardDetails || DisplayNameUtils.getCardForEmail(aEmailAddress);
+  // FIXME: It would be useful and cleaner to move the handling of the
+  // mail-emailaddress elements to the element's class itself. That way the
+  // logic wouldn't be spread between two separate scripts.
   aDocumentNode.cardDetails = cardDetails;
 
-  if (!cardDetails.card) {
-    aDocumentNode.setAttribute("hascard", "false");
-    aDocumentNode.setAttribute(
-      "tooltipstar",
-      document.getElementById("addToAddressBookItem").label
-    );
-  } else {
-    aDocumentNode.setAttribute("hascard", "true");
-    aDocumentNode.setAttribute(
-      "tooltipstar",
-      document.getElementById("editContactItem").label
-    );
-  }
-
-  let chatAddresses = [aEmailAddress];
-  let card = cardDetails.card;
-  if (card) {
-    let gTalk = card.getProperty("_GoogleTalk", null);
-    if (gTalk) {
-      chatAddresses.push(gTalk);
-    }
-    let jid = card.getProperty("_JabberId", null);
-    if (jid) {
-      chatAddresses.push(jid);
-    }
-  }
-  let { onlineContacts } = ChromeUtils.import(
-    "resource:///modules/chatHandler.jsm"
-  );
-  let chatContact;
-  for (let chatAddress of chatAddresses) {
-    if (Object.prototype.hasOwnProperty.call(onlineContacts, chatAddresses)) {
-      chatContact = onlineContacts[chatAddress];
-      break;
-    }
-  }
-  if (aDocumentNode.chatContact) {
-    aDocumentNode.chatContact.removeObserver(aDocumentNode.chatContactObserver);
-    delete aDocumentNode.chatContact;
-    delete aDocumentNode.chatContactObserver;
-  }
-  if (chatContact) {
-    aDocumentNode.chatContact = chatContact;
-    aDocumentNode.chatContactObserver = function(aSubject, aTopic, aData) {
-      if (aTopic == "contact-removed") {
-        this.chatContact.removeObserver(this.chatContactObserver);
-        delete this.chatContact;
-        delete this.chatContactObserver;
-        this.removeAttribute("chatStatus");
-        this.removeAttribute("presenceTooltip");
-      } else if (aTopic == "contact-status-changed") {
-        UpdateEmailPresenceDetails(this, this.chatContact);
-      }
-    }.bind(aDocumentNode);
-    chatContact.addObserver(aDocumentNode.chatContactObserver);
-  }
-  UpdateEmailPresenceDetails(aDocumentNode, chatContact);
+  aDocumentNode.setAddressBookState(!!cardDetails.card);
 
   // When we are adding cards, we don't want to move the display around if the
   // user has clicked on the star, therefore if it is locked, just exit and
   // leave the display updates until later.
   if (aDocumentNode.hasAttribute("updatingUI")) {
     return;
   }
 
@@ -1493,43 +1430,21 @@ function UpdateEmailNodeDetails(aEmailAd
     aDocumentNode.removeAttribute("tooltiptext");
     displayName =
       aDocumentNode.getAttribute("fullAddress") ||
       aDocumentNode.getAttribute("displayName");
   }
   aDocumentNode.setAttribute("label", displayName);
 }
 
-function UpdateEmailPresenceDetails(aDocumentNode, aChatContact) {
-  if (!aChatContact) {
-    aDocumentNode.removeAttribute("chatStatus");
-    aDocumentNode.removeAttribute("presenceTooltip");
-    return;
-  }
-
-  let statusType = aChatContact.statusType;
-  if (statusType < Ci.imIStatusInfo.STATUS_IDLE) {
-    aDocumentNode.removeAttribute("chatStatus");
-  } else if (statusType == Ci.imIStatusInfo.STATUS_AVAILABLE) {
-    aDocumentNode.setAttribute("chatStatus", "available");
-  } else {
-    aDocumentNode.setAttribute("chatStatus", "away");
-  }
-
-  let tooltipText =
-    aChatContact.preferredBuddy.protocol.name +
-    "\n" +
-    statusUtils.toLabel(aChatContact.statusType);
-  let statusText = aChatContact.statusText;
-  if (statusText) {
-    tooltipText += " - " + statusText;
-  }
-  aDocumentNode.setAttribute("presenceTooltip", tooltipText);
-}
-
+// FIXME: This method is only called in another file by
+// MozMailMultiEmailheaderfield.updateExtraAddressProcessing, which in turn
+// is only invoked by OnAddressBookDataChanged in this file. We should avoid
+// moving between files when this could all be handled by the element's class
+// itself.
 function UpdateExtraAddressProcessing(
   aAddressData,
   aDocumentNode,
   aAction,
   aParentDir,
   aItem
 ) {
   switch (aAction) {
@@ -1657,63 +1572,16 @@ function setupEmailAddressPopup(emailAdd
     }
   } else {
     document.getElementById("addToAddressBookItem").removeAttribute("hidden");
     document.getElementById("editContactItem").setAttribute("hidden", true);
     document.getElementById("viewContactItem").setAttribute("hidden", true);
   }
 }
 
-function onClickEmailStar(event, emailAddressNode) {
-  // Only care about left-click events
-  if (event.button != 0) {
-    return;
-  }
-
-  if (
-    emailAddressNode &&
-    emailAddressNode.cardDetails &&
-    emailAddressNode.cardDetails.card
-  ) {
-    EditContact(emailAddressNode);
-  } else {
-    AddContact(emailAddressNode);
-  }
-}
-
-function onClickEmailPresence(event, emailAddressNode) {
-  // Only care about left-click events
-  if (event.button != 0) {
-    return;
-  }
-
-  let prplConv = emailAddressNode.chatContact.createConversation();
-  let uiConv = Services.conversations.getUIConversation(prplConv);
-
-  let win = window;
-  if (!("focusConversation" in chatHandler)) {
-    win = Services.wm.getMostRecentWindow("mail:3pane");
-    if (win) {
-      win.focus();
-    } else {
-      window.openDialog(
-        "chrome://messenger/content/messenger.xhtml",
-        "_blank",
-        "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar",
-        null,
-        { tabType: "chat", tabParams: { convType: "focus", conv: uiConv } }
-      );
-      return;
-    }
-  }
-
-  win.showChatTab();
-  win.chatHandler.focusConversation(uiConv);
-}
-
 /**
  * Takes the email address node, adds a new contact from the node's
  * displayName and emailAddress attributes to the personal address book.
  *
  * @param emailAddressNode  a node with displayName and emailAddress attributes
  */
 function AddContact(emailAddressNode) {
   emailAddressNode = emailAddressNode.closest("mail-emailaddress");
--- a/mail/locales/en-US/messenger/messenger.ftl
+++ b/mail/locales/en-US/messenger/messenger.ftl
@@ -82,15 +82,23 @@ appmenu-help-more-troubleshooting-info =
 
 toolbar-context-menu-manage-extension =
     .label = Manage Extension
     .accesskey = E
 toolbar-context-menu-remove-extension =
     .label = Remove Extension
     .accesskey = v
 
+## Message headers
+
+message-header-address-in-address-book-icon =
+  .alt = Address is in the Address Book
+
+message-header-address-not-in-address-book-icon =
+  .alt = Address is not in the Address Book
+
 ## Add-on removal warning
 
 # Variables:
 #  $name (String): The name of the addon that will be removed.
 addon-removal-title = Remove { $name }?
 addon-removal-confirmation-button = Remove
 addon-removal-confirmation-message = Remove { $name } as well as its configuration and data from { -brand-short-name }?
--- a/mail/test/browser/message-header/browser_messageHeader.js
+++ b/mail/test/browser/message-header/browser_messageHeader.js
@@ -960,19 +960,21 @@ function subtest_change_to_all_header_mo
 function subtest_more_widget_star_click(toDescription) {
   let addrs = toDescription.getElementsByTagName("mail-emailaddress");
   let lastAddr = get_last_visible_address(addrs);
   ensure_no_card_exists(lastAddr.getAttribute("emailAddress"));
 
   // scroll to the bottom first so the address is in view
   let view = mc.e("expandedHeaderView");
   view.scrollTop = view.scrollHeight - view.clientHeight;
+  let star = lastAddr.querySelector(".emailStar");
+  let src = star.getAttribute("src");
 
-  mc.click(lastAddr.querySelector(".emailStar"));
-  if (lastAddr.getAttribute("hascard") == "false") {
+  mc.click(star);
+  if (star.getAttribute("src") === src) {
     throw new Error("address not updated after clicking star");
   }
 }
 
 /**
  * Make sure the (more) widget hidden pref actually works with a
  * non-default value.
  */
--- a/mail/themes/osx/mail/messageHeader.css
+++ b/mail/themes/osx/mail/messageHeader.css
@@ -28,17 +28,17 @@
   margin-top: 0;
 }
 
 .headerName,
 #attachmentSize {
   color: #a1a1a1; /* lower contrast */
 }
 
-.emailToggleHeaderfield {
+mail-messageids-headerfield .emailActionIconButton {
   margin-inline: 1px 3px;
 }
 
 /* ::::: collapsed view styles ::::: */
 
 .message-security-label {
   /* Necessary to not cut the background icon */
   min-height: 16px;
--- a/mail/themes/shared/mail/messageHeader.css
+++ b/mail/themes/shared/mail/messageHeader.css
@@ -428,69 +428,74 @@ mail-emailaddress[selected="true"] > .em
   margin: 0;
   margin-inline-start: -2px;
 }
 
 .messageIdSeparator {
   margin: 0;
 }
 
+.emailActionIconButton {
+  /* Only occupy the same space as its content. */
+  display: inline grid;
+  padding: 0;
+  margin: 0;
+  height: auto;
+  min-width: auto;
+  border: none;
+  border-radius: 0;
+  background: none;
+}
+
+.emailActionIconButton[hidden] {
+  display: none;
+}
+
+.emailActionIconButton:focus-visible:not(:hover) {
+  outline: 1px solid var(--focus-outline-color);
+}
+
+button.emailActionIconButton:hover {
+  background: none;
+}
+
+mail-emailaddress .emailActionIconButton {
+  margin-inline-start: 2px;
+}
+
 .emailStar {
   width: 1em;
   height: 1em;
-  margin-inline-start: 2px;
-  list-style-image: url("chrome://messenger/skin/icons/star.svg");
+}
+
+.emailStar {
   -moz-context-properties: fill;
   fill: currentColor;
 }
 
-.emailStar[hascard="true"] {
-  list-style-image: url("chrome://messenger/skin/icons/starred.svg");
+.emailStar.starredFill {
   fill: var(--toolbarbutton-icon-fill-attention);
 }
 
-.emailDisplayButton:hover > .emailStar,
-mail-emailaddress[selected="true"] > .emailDisplayButton > .emailStar {
+.emailDisplayButton:hover .emailStar,
+mail-emailaddress[selected="true"] > .emailDisplayButton .emailStar {
   fill: HighlightText;
 }
 
-.emailStar:focus-visible:not(:hover) {
-  outline: 1px solid var(--focus-outline-color);
-}
-
 .emaillabel {
   max-width: 60vw; /* for very long email fields */
 }
 
 .emaillabel,
 .newsgrouplabel {
   padding: 0;  /* override <label> defaults */
   margin: 0;  /* override <label> defaults */
   overflow: hidden;
 }
 
-.emailStar[chatStatus],
-.emailPresence:not([chatStatus]) {
-  display: none;
-}
-
-.emailPresence {
-  width: 1em;
-  height: 1em;
-  margin-inline-start: 2px;
-}
-
-.emailPresence[chatStatus="available"] {
-  list-style-image: url("chrome://messenger/skin/icons/status-online.svg");
-}
-
-.emailPresence[chatStatus="away"] {
-  list-style-image: url("chrome://messenger/skin/icons/status-away.svg");
-}
-
 /* ::::: email address twisty ::::: */
 
 .addresstwisty {
   padding-inline-end: 0;
   padding-top: 4px;
   list-style-image:url("chrome://messenger/skin/icons/arrow/arrow-right-dim.png");
 }
 
@@ -501,24 +506,28 @@ mail-emailaddress[selected="true"] > .em
 .addresstwisty[open] {
   list-style-image:url("chrome://messenger/skin/icons/arrow/arrow-down-dim.png");
 }
 
 .addresstwisty[open]:hover {
   list-style-image:url("chrome://messenger/skin/icons/arrow/arrow-down.png");
 }
 
+mail-messageids-headerfield .emailActionIconButton {
+  padding-block-start: 1px;
+  margin-inline: 4px 2px;
+}
+
 .emailToggleHeaderfield {
-  padding-top: 1px;
-  width: 10px;
-  margin-inline: 4px 2px;
   background: url("chrome://global/skin/icons/twisty-collapsed.svg") no-repeat center;
   background-size: contain;
   -moz-context-properties: fill;
   fill: currentColor;
+  width: 10px;
+  height: 10px;
 }
 
 .emailToggleHeaderfield:-moz-locale-dir(rtl) {
   background-image: url("chrome://global/skin/icons/twisty-collapsed-rtl.svg");
 }
 
 .emailToggleHeaderfield.open {
   background-image: url("chrome://global/skin/icons/twisty-expanded.svg");