/* 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/. */

/* import-globals-from ../../../base/content/globalOverlay.js */
/* import-globals-from ../../../base/content/mailCore.js */
/* import-globals-from ../../../base/content/mailWindowOverlay.js */
/* import-globals-from ../../../base/content/messenger.js */
/* import-globals-from ../../../extensions/mailviews/content/msgViewPickerOverlay.js */

var { ExtensionParent } = ChromeUtils.importESModule(
  "resource://gre/modules/ExtensionParent.sys.mjs"
);
var { ExtensionSupport } = ChromeUtils.import(
  "resource:///modules/ExtensionSupport.jsm"
);
var { ShortcutUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/ShortcutUtils.sys.mjs"
);
var { AppConstants } = ChromeUtils.importESModule(
  "resource://gre/modules/AppConstants.sys.mjs"
);
var { UIDensity } = ChromeUtils.import("resource:///modules/UIDensity.jsm");
var { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);

ChromeUtils.defineESModuleGetters(this, {
  AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.sys.mjs",
  CustomizableUI: "resource:///modules/CustomizableUI.sys.mjs",
  PanelMultiView: "resource:///modules/PanelMultiView.sys.mjs",
});
ChromeUtils.defineModuleGetter(
  this,
  "ExtensionsUI",
  "resource:///modules/ExtensionsUI.jsm"
);

/**
 * Maintains the state and dispatches events for the main menu panel.
 */
const PanelUI = {
  /** Panel events that we listen for. */
  get kEvents() {
    return [
      "popupshowing",
      "popupshown",
      "popuphiding",
      "popuphidden",
      "ViewShowing",
    ];
  },
  /**
   * Used for lazily getting and memoizing elements from the document. Lazy
   * getters are set in init, and memoizing happens after the first retrieval.
   */
  get kElements() {
    return {
      mainView: "appMenu-mainView",
      multiView: "appMenu-multiView",
      menuButton: "button-appmenu",
      panel: "appMenu-popup",
      addonNotificationContainer: "appMenu-addon-banners",
      navbar: "mail-bar3",
    };
  },

  kAppMenuButtons: new Set(),

  _initialized: false,
  _notifications: null,

  init() {
    this._initElements();
    this.initAppMenuButton("button-appmenu", "mail-toolbox");

    this.menuButton = this.menuButtonMail;

    Services.obs.addObserver(this, "fullscreen-nav-toolbox");
    Services.obs.addObserver(this, "appMenu-notifications");

    XPCOMUtils.defineLazyPreferenceGetter(
      this,
      "autoHideToolbarInFullScreen",
      "browser.fullscreen.autohide",
      false,
      (pref, previousValue, newValue) => {
        // On OSX, or with autohide preffed off, MozDOMFullscreen is the only
        // event we care about, since fullscreen should behave just like non
        // fullscreen. Otherwise, we don't want to listen to these because
        // we'd just be spamming ourselves with both of them whenever a user
        // opened a video.
        if (newValue) {
          window.removeEventListener("MozDOMFullscreen:Entered", this);
          window.removeEventListener("MozDOMFullscreen:Exited", this);
          window.addEventListener("fullscreen", this);
        } else {
          window.addEventListener("MozDOMFullscreen:Entered", this);
          window.addEventListener("MozDOMFullscreen:Exited", this);
          window.removeEventListener("fullscreen", this);
        }

        this._updateNotifications(false);
      },
      autoHidePref => autoHidePref && Services.appinfo.OS !== "Darwin"
    );

    if (this.autoHideToolbarInFullScreen) {
      window.addEventListener("fullscreen", this);
    } else {
      window.addEventListener("MozDOMFullscreen:Entered", this);
      window.addEventListener("MozDOMFullscreen:Exited", this);
    }

    window.addEventListener("activate", this);

    Services.obs.notifyObservers(
      null,
      "appMenu-notifications-request",
      "refresh"
    );

    this._initialized = true;
  },

  _initElements() {
    for (let [k, v] of Object.entries(this.kElements)) {
      // Need to do fresh let-bindings per iteration
      let getKey = k;
      let id = v;
      this.__defineGetter__(getKey, function () {
        delete this[getKey];
        // eslint-disable-next-line consistent-return
        return (this[getKey] = document.getElementById(id));
      });
    }
  },

  initAppMenuButton(id, toolboxId) {
    let button = document.getElementById(id);
    if (!button) {
      // If not in the document, the button should be in the toolbox palette,
      // which isn't part of the document.
      let toolbox = document.getElementById(toolboxId);
      if (toolbox) {
        button = toolbox.palette.querySelector(`#${id}`);
      }
    }

    if (button) {
      button.addEventListener("mousedown", PanelUI);
      button.addEventListener("keypress", PanelUI);

      this.kAppMenuButtons.add(button);
    }
  },

  _eventListenersAdded: false,
  _ensureEventListenersAdded() {
    if (this._eventListenersAdded) {
      return;
    }
    this._addEventListeners();
  },

  _addEventListeners() {
    for (let event of this.kEvents) {
      this.panel.addEventListener(event, this);
    }
    this._eventListenersAdded = true;
  },

  _removeEventListeners() {
    for (let event of this.kEvents) {
      this.panel.removeEventListener(event, this);
    }
    this._eventListenersAdded = false;
  },

  uninit() {
    this._removeEventListeners();

    Services.obs.removeObserver(this, "fullscreen-nav-toolbox");
    Services.obs.removeObserver(this, "appMenu-notifications");

    window.removeEventListener("MozDOMFullscreen:Entered", this);
    window.removeEventListener("MozDOMFullscreen:Exited", this);
    window.removeEventListener("fullscreen", this);
    window.removeEventListener("activate", this);

    [this.menuButtonMail, this.menuButtonChat].forEach(button => {
      // There's no chat button in the messageWindow.xhtml context.
      if (button) {
        button.removeEventListener("mousedown", this);
        button.removeEventListener("keypress", this);
      }
    });
  },

  /**
   * Opens the menu panel if it's closed, or closes it if it's open.
   *
   * @param event the event that triggers the toggle.
   */
  toggle(event) {
    // Don't show the panel if the window is in customization mode,
    // since this button doubles as an exit path for the user in this case.
    if (document.documentElement.hasAttribute("customizing")) {
      return;
    }

    // Since we have several menu buttons, make sure the current one is used.
    // This works for now, but in the long run, if we're showing badges etc.
    // then the current menuButton needs to be set when the app's view/tab
    // changes, not just when the menu is toggled.
    this.menuButton = event.target;

    this._ensureEventListenersAdded();
    if (this.panel.state == "open") {
      this.hide();
    } else if (this.panel.state == "closed") {
      this.show(event);
    }
  },

  /**
   * Opens the menu panel. If the event target has a child with the
   * toolbarbutton-icon attribute, the panel will be anchored on that child.
   * Otherwise, the panel is anchored on the event target itself.
   *
   * @param aEvent the event (if any) that triggers showing the menu.
   */
  show(aEvent) {
    this._ensureShortcutsShown();
    (async () => {
      await this.ensureReady();

      if (
        this.panel.state == "open" ||
        document.documentElement.hasAttribute("customizing")
      ) {
        return;
      }

      let domEvent = null;
      if (aEvent && aEvent.type != "command") {
        domEvent = aEvent;
      }

      // We try to use the event.target to account for clicks triggered
      // from the #button-chat-appmenu. In case the opening of the menu isn't
      // triggered by a click event, fallback to the main menu button as anchor.
      let anchor = this._getPanelAnchor(
        aEvent ? aEvent.target : this.menuButton
      );
      await PanelMultiView.openPopup(this.panel, anchor, {
        triggerEvent: domEvent,
      });
    })().catch(console.error);
  },

  /**
   * If the menu panel is being shown, hide it.
   */
  hide() {
    if (document.documentElement.hasAttribute("customizing")) {
      return;
    }

    PanelMultiView.hidePopup(this.panel);
  },

  observe(subject, topic, status) {
    switch (topic) {
      case "fullscreen-nav-toolbox":
        if (this._notifications) {
          this._updateNotifications(false);
        }
        break;
      case "appMenu-notifications":
        // Don't initialize twice.
        if (status == "init" && this._notifications) {
          break;
        }
        this._notifications = AppMenuNotifications.notifications;
        this._updateNotifications(true);
        break;
    }
  },

  handleEvent(event) {
    // Ignore context menus and menu button menus showing and hiding:
    if (event.type.startsWith("popup") && event.target != this.panel) {
      return;
    }
    switch (event.type) {
      case "popupshowing":
        initAppMenuPopup();
      // Fall through
      case "popupshown":
        if (event.type == "popupshown") {
          CustomizableUI.addPanelCloseListeners(this.panel);
        }
      // Fall through
      case "popuphiding":
      // Fall through
      case "popuphidden":
        this._updateNotifications();
        this._updatePanelButton(event.target);
        if (event.type == "popuphidden") {
          CustomizableUI.removePanelCloseListeners(this.panel);
        }
        break;
      case "mousedown":
        if (event.button == 0) {
          this.toggle(event);
        }
        break;
      case "keypress":
        if (event.key == " " || event.key == "Enter") {
          this.toggle(event);
          event.stopPropagation();
        }
        break;
      case "MozDOMFullscreen:Entered":
      case "MozDOMFullscreen:Exited":
      case "fullscreen":
      case "activate":
        this._updateNotifications();
        break;
      case "ViewShowing":
        PanelUI._handleViewShowingEvent(event);
        break;
    }
  },

  /**
   * When a ViewShowing event happens when a <panelview> element is shown,
   * do any required set up for that particular view.
   *
   * @param {ViewShowingEvent} event - ViewShowing event.
   */
  _handleViewShowingEvent(event) {
    // Typically event.target for "ViewShowing" is a <panelview> element.
    PanelUI._ensureShortcutsShown(event.target);

    switch (event.target.id) {
      case "appMenu-foldersView":
        this._onFoldersViewShow(event);
        break;
      case "appMenu-addonsView":
        initAddonPrefsMenu(
          event.target.querySelector(".panel-subview-body"),
          "toolbarbutton",
          "subviewbutton subviewbutton-iconic",
          "subviewbutton subviewbutton-iconic"
        );
        break;
      case "appMenu-toolbarsView":
        onViewToolbarsPopupShowing(
          event,
          "mail-toolbox",
          document.getElementById("appmenu_quickFilterBar"),
          "toolbarbutton",
          "subviewbutton subviewbutton-iconic",
          true
        );
        break;
      case "appMenu-preferencesLayoutView":
        PanelUI._onPreferencesLayoutViewShow(event);
        break;
      // View
      case "appMenu-viewMessagesTagsView":
        PanelUI._refreshDynamicView(event, RefreshTagsPopup);
        break;
      case "appMenu-viewMessagesCustomViewsView":
        PanelUI._refreshDynamicView(event, RefreshCustomViewsPopup);
        break;
    }
  },

  /**
   * Refreshes some views that are dynamically populated. Typically called by
   * event listeners responding to a ViewShowing event. It calls a given refresh
   * function (that populates the view), passing appmenu-specific arguments.
   *
   * @param {ViewShowingEvent} event - ViewShowing event.
   * @param {Function} refreshFunction - Function that refreshes a particular view.
   */
  _refreshDynamicView(event, refreshFunction) {
    refreshFunction(
      event.target.querySelector(".panel-subview-body"),
      "toolbarbutton",
      "subviewbutton subviewbutton-iconic",
      "toolbarseparator"
    );
  },

  get isReady() {
    return !!this._isReady;
  },

  /**
   * Registering the menu panel is done lazily for performance reasons. This
   * method is exposed so that CustomizationMode can force panel-readyness in the
   * event that customization mode is started before the panel has been opened
   * by the user.
   *
   * @param aCustomizing (optional) set to true if this was called while entering
   *        customization mode. If that's the case, we trust that customization
   *        mode will handle calling beginBatchUpdate and endBatchUpdate.
   *
   * @returns a Promise that resolves once the panel is ready to roll.
   */
  async ensureReady() {
    if (this._isReady) {
      return;
    }

    await window.delayedStartupPromise;
    this._ensureEventListenersAdded();
    this.panel.hidden = false;
    this._isReady = true;
  },

  /**
   * Shows a subview in the panel with a given ID.
   *
   * @param aViewId the ID of the subview to show.
   * @param aAnchor the element that spawned the subview.
   */
  async showSubView(aViewId, aAnchor) {
    this._ensureEventListenersAdded();
    let viewNode = document.getElementById(aViewId);
    if (!viewNode) {
      console.error("Could not show panel subview with id: " + aViewId);
      return;
    }

    if (!aAnchor) {
      console.error(
        "Expected an anchor when opening subview with id: " + aViewId
      );
      return;
    }

    let container = aAnchor.closest("panelmultiview");
    if (container) {
      container.showSubView(aViewId, aAnchor);
    }
  },

  /**
   * NB: The enable- and disableSingleSubviewPanelAnimations methods only
   * affect the hiding/showing animations of single-subview panels (tempPanel
   * in the showSubView method).
   */
  disableSingleSubviewPanelAnimations() {
    this._disableAnimations = true;
  },

  enableSingleSubviewPanelAnimations() {
    this._disableAnimations = false;
  },

  /**
   * Sets the anchor node into the open or closed state, depending
   * on the state of the panel.
   */
  _updatePanelButton() {
    this.menuButton.open =
      this.panel.state == "open" || this.panel.state == "showing";
  },

  /**
   * Event handler for showing the Preferences/Layout view. Removes "checked"
   * from all layout menu items and then checks the current layout menu item.
   *
   * @param {ViewShowingEvent} event - ViewShowing event.
   */
  _onPreferencesLayoutViewShow(event) {
    event.target
      .querySelectorAll("[name='viewlayoutgroup']")
      .forEach(item => item.removeAttribute("checked"));

    InitViewLayoutStyleMenu(event, true);
  },

  /**
   * Event listener for showing the Folders view.
   *
   * @param {ViewShowingEvent} event - ViewShowing event.
   */
  _onFoldersViewShow(event) {
    let about3Pane = document.getElementById("tabmail").currentAbout3Pane;
    let folder = about3Pane.gFolder;

    const paneHeaderMenuitem = event.target.querySelector(
      '[name="paneheader"]'
    );
    if (about3Pane.folderPane.isFolderPaneHeaderHidden()) {
      paneHeaderMenuitem.removeAttribute("checked");
    } else {
      paneHeaderMenuitem.setAttribute("checked", "true");
    }

    let { activeModes, canBeCompact, isCompact } = about3Pane.folderPane;
    if (isCompact) {
      activeModes.push("compact");
    }

    for (let item of event.target.querySelectorAll('[name="viewmessages"]')) {
      let mode = item.getAttribute("value");
      if (activeModes.includes(mode)) {
        item.setAttribute("checked", "true");
        if (mode == "all") {
          item.disabled = activeModes.length == 1;
        }
      } else {
        item.removeAttribute("checked");
      }
      if (mode == "compact") {
        item.disabled = !canBeCompact;
      }
    }

    goUpdateCommand("cmd_properties");
    let propertiesMenuItem = document.getElementById("appmenu_properties");
    if (folder?.server.type == "nntp") {
      document.l10n.setAttributes(
        propertiesMenuItem,
        "menu-edit-newsgroup-properties"
      );
    } else {
      document.l10n.setAttributes(
        propertiesMenuItem,
        "menu-edit-folder-properties"
      );
    }

    let favoriteFolderMenu = document.getElementById("appmenu_favoriteFolder");
    if (folder?.getFlag(Ci.nsMsgFolderFlags.Favorite)) {
      favoriteFolderMenu.setAttribute("checked", "true");
    } else {
      favoriteFolderMenu.removeAttribute("checked");
    }
  },

  _onToolsMenuShown(event) {
    let noAccounts = MailServices.accounts.accounts.length == 0;
    event.target.querySelector("#appmenu_searchCmd").disabled = noAccounts;
    event.target.querySelector("#appmenu_filtersCmd").disabled = noAccounts;
  },

  _updateNotifications(notificationsChanged) {
    let notifications = this._notifications;
    if (!notifications || !notifications.length) {
      if (notificationsChanged) {
        this._clearAllNotifications();
      }
      return;
    }

    let doorhangers = notifications.filter(
      n => !n.dismissed && !n.options.badgeOnly
    );

    if (this.panel.state == "showing" || this.panel.state == "open") {
      // If the menu is already showing, then we need to dismiss all notifications
      // since we don't want their doorhangers competing for attention
      doorhangers.forEach(n => {
        n.dismissed = true;
        if (n.options.onDismissed) {
          n.options.onDismissed(window);
        }
      });
      this._clearBadge();
      if (!notifications[0].options.badgeOnly) {
        this._showBannerItem(notifications[0]);
      }
    } else if (doorhangers.length > 0) {
      // Only show the doorhanger if the window is focused and not fullscreen
      if (
        (window.fullScreen && this.autoHideToolbarInFullScreen) ||
        Services.focus.activeWindow !== window
      ) {
        this._showBadge(doorhangers[0]);
        this._showBannerItem(doorhangers[0]);
      } else {
        this._clearBadge();
      }
    } else {
      this._showBadge(notifications[0]);
      this._showBannerItem(notifications[0]);
    }
  },

  _clearAllNotifications() {
    this._clearBadge();
    this._clearBannerItem();
  },

  _formatDescriptionMessage(n) {
    let text = {};
    let array = n.options.message.split("<>");
    text.start = array[0] || "";
    text.name = n.options.name || "";
    text.end = array[1] || "";
    return text;
  },

  _showBadge(notification) {
    let badgeStatus = this._getBadgeStatus(notification);
    for (let menuButton of this.kAppMenuButtons) {
      menuButton.setAttribute("badge-status", badgeStatus);
    }
  },

  // "Banner item" here refers to an item in the hamburger panel menu. They will
  // typically show up as a colored row in the panel.
  _showBannerItem(notification) {
    const supportedIds = [
      "update-downloading",
      "update-available",
      "update-manual",
      "update-unsupported",
      "update-restart",
    ];
    if (!supportedIds.includes(notification.id)) {
      return;
    }

    if (!this._panelBannerItem) {
      this._panelBannerItem = this.mainView.querySelector(".panel-banner-item");
    }

    let l10nId = "appmenuitem-banner-" + notification.id;
    document.l10n.setAttributes(this._panelBannerItem, l10nId);

    this._panelBannerItem.setAttribute("notificationid", notification.id);
    this._panelBannerItem.hidden = false;
    this._panelBannerItem.notification = notification;
  },

  _clearBadge() {
    for (let menuButton of this.kAppMenuButtons) {
      menuButton.removeAttribute("badge-status");
    }
  },

  _clearBannerItem() {
    if (this._panelBannerItem) {
      this._panelBannerItem.notification = null;
      this._panelBannerItem.hidden = true;
    }
  },

  _onNotificationButtonEvent(event, type) {
    let notificationEl = getNotificationFromElement(event.target);

    if (!notificationEl) {
      throw new Error(
        "PanelUI._onNotificationButtonEvent: couldn't find notification element"
      );
    }

    if (!notificationEl.notification) {
      throw new Error(
        "PanelUI._onNotificationButtonEvent: couldn't find notification"
      );
    }

    let notification = notificationEl.notification;

    if (type == "secondarybuttoncommand") {
      AppMenuNotifications.callSecondaryAction(window, notification);
    } else {
      AppMenuNotifications.callMainAction(window, notification, true);
    }
  },

  _onBannerItemSelected(event) {
    let target = event.target;
    if (!target.notification) {
      throw new Error(
        "menucommand target has no associated action/notification"
      );
    }

    event.stopPropagation();
    AppMenuNotifications.callMainAction(window, target.notification, false);
  },

  _getPopupId(notification) {
    return "appMenu-" + notification.id + "-notification";
  },

  _getBadgeStatus(notification) {
    return notification.id;
  },

  _getPanelAnchor(candidate) {
    let iconAnchor = candidate.badgeStack || candidate.icon;
    return iconAnchor || candidate;
  },

  _ensureShortcutsShown(view = this.mainView) {
    if (view.hasAttribute("added-shortcuts")) {
      return;
    }
    view.setAttribute("added-shortcuts", "true");
    for (let button of view.querySelectorAll("toolbarbutton[key]")) {
      let keyId = button.getAttribute("key");
      let key = document.getElementById(keyId);
      if (!key) {
        continue;
      }
      button.setAttribute("shortcut", ShortcutUtils.prettifyShortcut(key));
    }
  },

  folderViewMenuOnCommand(event) {
    let about3Pane = document.getElementById("tabmail").currentAbout3Pane;
    if (!about3Pane) {
      return;
    }

    let mode = event.target.getAttribute("value");
    if (mode == "toggle-header") {
      about3Pane.folderPane.toggleHeader(event.target.hasAttribute("checked"));
      return;
    }

    let activeModes = about3Pane.folderPane.activeModes;
    let index = activeModes.indexOf(mode);
    if (event.target.hasAttribute("checked")) {
      if (index == -1) {
        activeModes.push(mode);
      }
    } else if (index >= 0) {
      activeModes.splice(index, 1);
    }
    about3Pane.folderPane.activeModes = activeModes;

    this._onFoldersViewShow({ target: event.target.parentNode });
  },

  folderCompactMenuOnCommand(event) {
    let about3Pane = document.getElementById("tabmail").currentAbout3Pane;
    if (!about3Pane) {
      return;
    }

    about3Pane.folderPane.isCompact = event.target.hasAttribute("checked");
  },

  setUIDensity(event) {
    // Loops through all available options and uncheck them. This is necessary
    // since the toolbarbuttons don't uncheck themselves even if they're radio.
    for (let item of event.originalTarget
      .closest(".panel-subview-body")
      .querySelectorAll("toolbarbutton")) {
      // Skip this item if it's the one clicked.
      if (item == event.originalTarget) {
        continue;
      }

      item.removeAttribute("checked");
    }
    // Update the UI density.
    UIDensity.setMode(event.originalTarget.mode);
  },
};

XPCOMUtils.defineConstant(this, "PanelUI", PanelUI);

/**
 * Gets the currently selected locale for display.
 *
 * @returns the selected locale
 */
function getLocale() {
  return Services.locale.appLocaleAsBCP47;
}

/**
 * Given a DOM node inside a <popupnotification>, return the parent <popupnotification>.
 */
function getNotificationFromElement(aElement) {
  return aElement.closest("popupnotification");
}

/**
 * This object is Thunderbird's version of the same object in
 * browser/base/content/browser-addons.js.
 */
var gExtensionsNotifications = {
  initialized: false,
  init() {
    this.updateAlerts();
    this.boundUpdate = this.updateAlerts.bind(this);
    ExtensionsUI.on("change", this.boundUpdate);
    this.initialized = true;
  },

  uninit() {
    // uninit() can race ahead of init() in some cases, if that happens,
    // we have no handler to remove.
    if (!this.initialized) {
      return;
    }
    ExtensionsUI.off("change", this.boundUpdate);
  },

  get l10n() {
    if (this._l10n) {
      return this._l10n;
    }
    return (this._l10n = new Localization(
      ["messenger/addonNotifications.ftl", "branding/brand.ftl"],
      true
    ));
  },

  _createAddonButton(l10nId, addon, callback) {
    let text = this.l10n.formatValueSync(l10nId, { addonName: addon.name });
    let button = document.createXULElement("toolbarbutton");
    button.setAttribute("wrap", "true");
    button.setAttribute("label", text);
    button.setAttribute("tooltiptext", text);
    const DEFAULT_EXTENSION_ICON =
      "chrome://messenger/skin/icons/new/compact/extension.svg";
    button.setAttribute("image", addon.iconURL || DEFAULT_EXTENSION_ICON);
    button.className = "addon-banner-item subviewbutton";

    button.addEventListener("command", callback);
    PanelUI.addonNotificationContainer.appendChild(button);
  },

  updateAlerts() {
    let gBrowser = document.getElementById("tabmail");
    let sideloaded = ExtensionsUI.sideloaded;
    let updates = ExtensionsUI.updates;

    let container = PanelUI.addonNotificationContainer;

    while (container.firstChild) {
      container.firstChild.remove();
    }

    let items = 0;
    for (let update of updates) {
      if (++items > 4) {
        break;
      }
      this._createAddonButton(
        "webext-perms-update-menu-item",
        update.addon,
        evt => {
          ExtensionsUI.showUpdate(gBrowser, update);
        }
      );
    }

    for (let addon of sideloaded) {
      if (++items > 4) {
        break;
      }
      this._createAddonButton("webext-perms-sideload-menu-item", addon, evt => {
        // We need to hide the main menu manually because the toolbarbutton is
        // removed immediately while processing this event, and PanelUI is
        // unable to identify which panel should be closed automatically.
        PanelUI.hide();
        ExtensionsUI.showSideloaded(gBrowser, addon);
      });
    }
  },
};

addEventListener("unload", () => gExtensionsNotifications.uninit(), {
  once: true,
});
