tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

CustomizableWidgets.sys.mjs (21670B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      6 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
      7 import { PrivateBrowsingUtils } from "resource://gre/modules/PrivateBrowsingUtils.sys.mjs";
      8 
      9 const lazy = {};
     10 
     11 ChromeUtils.defineESModuleGetters(lazy, {
     12  CustomizableUI:
     13    "moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs",
     14  LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs",
     15  PanelMultiView:
     16    "moz-src:///browser/components/customizableui/PanelMultiView.sys.mjs",
     17  RecentlyClosedTabsAndWindowsMenuUtils:
     18    "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.sys.mjs",
     19  Sanitizer: "resource:///modules/Sanitizer.sys.mjs",
     20  SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
     21  ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs",
     22 });
     23 
     24 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
     25 
     26 ChromeUtils.defineLazyGetter(lazy, "log", () => {
     27  let { ConsoleAPI } = ChromeUtils.importESModule(
     28    "resource://gre/modules/Console.sys.mjs"
     29  );
     30  let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
     31  let consoleOptions = {
     32    maxLogLevel: debug ? "all" : "log",
     33    prefix: "CustomizableWidgets",
     34  };
     35  return new ConsoleAPI(consoleOptions);
     36 });
     37 
     38 XPCOMUtils.defineLazyPreferenceGetter(
     39  lazy,
     40  "sidebarRevampEnabled",
     41  "sidebar.revamp",
     42  false
     43 );
     44 
     45 /**
     46 * A helper method to synchronize aNode's DOM attributes with the properties and
     47 * values in aAttrs. If aNode has an attribute that is false-y in aAttrs,
     48 * then this attribute is removed.
     49 *
     50 * If aAttrs includes "shortcutId", the value is never set on aNode, but is
     51 * instead used when setting the "label" or "tooltiptext" attributes to include
     52 * the shortcut key combo. shortcutId should refer to the ID of the XUL <key>
     53 * element that acts as the shortcut.
     54 *
     55 * @param {Element} aNode
     56 *   The element to change the attributes of.
     57 * @param {object} aAttrs
     58 *   A set of key-value pairs where the key is set as the attribute name, and
     59 *   the value is set as the attribute value.
     60 */
     61 function setAttributes(aNode, aAttrs) {
     62  let doc = aNode.ownerDocument;
     63  for (let [name, value] of Object.entries(aAttrs)) {
     64    if (!value) {
     65      if (aNode.hasAttribute(name)) {
     66        aNode.removeAttribute(name);
     67      }
     68    } else {
     69      if (name == "shortcutId") {
     70        continue;
     71      }
     72      if (name == "label" || name == "tooltiptext") {
     73        let stringId = typeof value == "string" ? value : name;
     74        let additionalArgs = [];
     75        if (aAttrs.shortcutId) {
     76          let shortcut = doc.getElementById(aAttrs.shortcutId);
     77          if (shortcut) {
     78            additionalArgs.push(lazy.ShortcutUtils.prettifyShortcut(shortcut));
     79          }
     80        }
     81        value = lazy.CustomizableUI.getLocalizedProperty(
     82          { id: aAttrs.id },
     83          stringId,
     84          additionalArgs
     85        );
     86      }
     87      aNode.setAttribute(name, value);
     88    }
     89  }
     90 }
     91 
     92 /**
     93 * The array of built-in CustomizableUICreateWidgetProperties that are
     94 * registered as widgets upon browser start.
     95 *
     96 * @type {CustomizableUICreateWidgetProperties[]}
     97 */
     98 export const CustomizableWidgets = [
     99  {
    100    id: "history-panelmenu",
    101    type: "view",
    102    viewId: "PanelUI-history",
    103    shortcutId: "key_gotoHistory",
    104    tooltiptext: "history-panelmenu.tooltiptext2",
    105    recentlyClosedTabsPanel: "appMenu-library-recentlyClosedTabs",
    106    recentlyClosedWindowsPanel: "appMenu-library-recentlyClosedWindows",
    107    handleEvent(event) {
    108      switch (event.type) {
    109        case "PanelMultiViewHidden":
    110          this.onPanelMultiViewHidden(event);
    111          break;
    112        case "ViewShowing":
    113          this.onSubViewShowing(event);
    114          break;
    115        case "unload":
    116          this.onWindowUnload(event);
    117          break;
    118        case "command": {
    119          let { target } = event;
    120          let { PanelUI, PlacesCommandHook } = target.ownerGlobal;
    121          if (target.id == "appMenuRecentlyClosedTabs") {
    122            PanelUI.showSubView(this.recentlyClosedTabsPanel, target);
    123          } else if (target.id == "appMenuRecentlyClosedWindows") {
    124            PanelUI.showSubView(this.recentlyClosedWindowsPanel, target);
    125          } else if (target.id == "appMenuSearchHistory") {
    126            PlacesCommandHook.searchHistory();
    127          }
    128          break;
    129        }
    130        default:
    131          throw new Error(`Unsupported event for '${this.id}'`);
    132      }
    133    },
    134    onViewShowing(event) {
    135      if (this._panelMenuView) {
    136        return;
    137      }
    138 
    139      let panelview = event.target;
    140      let document = panelview.ownerDocument;
    141      let window = document.defaultView;
    142      const closedTabCount = lazy.SessionStore.getClosedTabCount();
    143 
    144      lazy.PanelMultiView.getViewNode(
    145        document,
    146        "appMenuRecentlyClosedTabs"
    147      ).disabled = closedTabCount == 0;
    148      lazy.PanelMultiView.getViewNode(
    149        document,
    150        "appMenuRecentlyClosedWindows"
    151      ).disabled = lazy.SessionStore.getClosedWindowCount(window) == 0;
    152 
    153      lazy.PanelMultiView.getViewNode(
    154        document,
    155        "appMenu-restoreSession"
    156      ).hidden = !lazy.SessionStore.canRestoreLastSession;
    157 
    158      // We restrict the amount of results to 42. Not 50, but 42. Why? Because 42.
    159      let query =
    160        "place:queryType=" +
    161        Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY +
    162        "&sort=" +
    163        Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING +
    164        "&maxResults=42&excludeQueries=1";
    165 
    166      this._panelMenuView = new window.PlacesPanelview(
    167        query,
    168        document.getElementById("appMenu_historyMenu"),
    169        panelview
    170      );
    171      // When either of these sub-subviews show, populate them with recently closed
    172      // objects data.
    173      lazy.PanelMultiView.getViewNode(
    174        document,
    175        this.recentlyClosedTabsPanel
    176      ).addEventListener("ViewShowing", this);
    177      lazy.PanelMultiView.getViewNode(
    178        document,
    179        this.recentlyClosedWindowsPanel
    180      ).addEventListener("ViewShowing", this);
    181      // When the popup is hidden (thus the panelmultiview node as well), make
    182      // sure to stop listening to PlacesDatabase updates.
    183      panelview.panelMultiView.addEventListener("PanelMultiViewHidden", this);
    184      panelview.addEventListener("command", this);
    185      window.addEventListener("unload", this);
    186    },
    187    onViewHiding() {
    188      lazy.log.debug("History view is being hidden!");
    189    },
    190    onPanelMultiViewHidden(event) {
    191      let panelMultiView = event.target;
    192      let document = panelMultiView.ownerDocument;
    193      if (this._panelMenuView) {
    194        this._panelMenuView.uninit();
    195        delete this._panelMenuView;
    196        lazy.PanelMultiView.getViewNode(
    197          document,
    198          this.recentlyClosedTabsPanel
    199        ).removeEventListener("ViewShowing", this);
    200        lazy.PanelMultiView.getViewNode(
    201          document,
    202          this.recentlyClosedWindowsPanel
    203        ).removeEventListener("ViewShowing", this);
    204        lazy.PanelMultiView.getViewNode(
    205          document,
    206          this.viewId
    207        ).removeEventListener("command", this);
    208      }
    209      panelMultiView.removeEventListener("PanelMultiViewHidden", this);
    210    },
    211    onWindowUnload() {
    212      if (this._panelMenuView) {
    213        delete this._panelMenuView;
    214      }
    215    },
    216    onSubViewShowing(event) {
    217      let panelview = event.target;
    218      let document = event.target.ownerDocument;
    219      let window = document.defaultView;
    220 
    221      this._panelMenuView.clearAllContents(panelview);
    222 
    223      const utils = lazy.RecentlyClosedTabsAndWindowsMenuUtils;
    224      const fragment =
    225        panelview.id == this.recentlyClosedTabsPanel
    226          ? utils.getTabsFragment(window, "toolbarbutton")
    227          : utils.getWindowsFragment(window, "toolbarbutton");
    228      let elementCount = fragment.childElementCount;
    229      this._panelMenuView._setEmptyPopupStatus(panelview, !elementCount);
    230      if (!elementCount) {
    231        return;
    232      }
    233 
    234      let body = document.createXULElement("vbox");
    235      body.className = "panel-subview-body";
    236      body.appendChild(fragment);
    237      let separator = document.createXULElement("toolbarseparator");
    238      let footer;
    239      while (--elementCount >= 0) {
    240        let element = body.children[elementCount];
    241        if (element.tagName != "toolbarbutton") {
    242          continue;
    243        }
    244        lazy.CustomizableUI.addShortcut(element);
    245        if (element.classList.contains("restoreallitem")) {
    246          footer = element;
    247        }
    248      }
    249      panelview.appendChild(body);
    250      panelview.appendChild(separator);
    251      panelview.appendChild(footer);
    252    },
    253  },
    254  {
    255    id: "save-page-button",
    256    l10nId: "toolbar-button-save-page",
    257    shortcutId: "key_savePage",
    258    onCreated(aNode) {
    259      aNode.setAttribute("command", "Browser:SavePage");
    260    },
    261  },
    262  {
    263    id: "print-button",
    264    l10nId: "navbar-print",
    265    shortcutId: "printKb",
    266    keepBroadcastAttributesWhenCustomizing: true,
    267    onCreated(aNode) {
    268      aNode.setAttribute("command", "cmd_printPreviewToggle");
    269    },
    270  },
    271  {
    272    id: "find-button",
    273    shortcutId: "key_find",
    274    tooltiptext: "find-button.tooltiptext3",
    275    onCommand(aEvent) {
    276      let win = aEvent.target.ownerGlobal;
    277      if (win.gLazyFindCommand) {
    278        win.gLazyFindCommand("onFindCommand");
    279      }
    280    },
    281  },
    282  {
    283    id: "open-file-button",
    284    l10nId: "toolbar-button-open-file",
    285    shortcutId: "openFileKb",
    286    onCreated(aNode) {
    287      aNode.setAttribute("command", "Browser:OpenFile");
    288    },
    289  },
    290  {
    291    id: "sidebar-button",
    292    l10nId: "show-sidebars",
    293    defaultArea: "nav-bar",
    294    _introducedByPref: "sidebar.revamp",
    295    onCommand(aEvent) {
    296      const { SidebarController } = aEvent.target.ownerGlobal;
    297      if (lazy.sidebarRevampEnabled) {
    298        SidebarController.handleToolbarButtonClick();
    299      } else {
    300        SidebarController.toggle();
    301      }
    302    },
    303    onCreated(aNode) {
    304      if (lazy.sidebarRevampEnabled) {
    305        const { SidebarController } = aNode.ownerGlobal;
    306        SidebarController.updateToolbarButton(aNode);
    307        aNode.setAttribute("overflows", "false");
    308        // Show the toolbar button badge by setting the badged attribute.
    309        // This activates badge styling by adding feature-callout class to the toolbarbutton-badge element.
    310        aNode.setAttribute("badged", true);
    311      } else {
    312        // Add an observer so the button is checked while the sidebar is open
    313        let doc = aNode.ownerDocument;
    314        let obChecked = doc.createXULElement("observes");
    315        obChecked.setAttribute("element", "sidebar-box");
    316        obChecked.setAttribute("attribute", "checked");
    317        let obPosition = doc.createXULElement("observes");
    318        obPosition.setAttribute("element", "sidebar-box");
    319        obPosition.setAttribute("attribute", "positionend");
    320        aNode.appendChild(obChecked);
    321        aNode.appendChild(obPosition);
    322      }
    323    },
    324  },
    325  {
    326    id: "zoom-controls",
    327    type: "custom",
    328    tooltiptext: "zoom-controls.tooltiptext2",
    329    onBuild(aDocument) {
    330      let buttons = [
    331        {
    332          id: "zoom-out-button",
    333          command: "cmd_fullZoomReduce",
    334          label: true,
    335          closemenu: "none",
    336          tooltiptext: "tooltiptext2",
    337          shortcutId: "key_fullZoomReduce",
    338          class: "toolbarbutton-1 toolbarbutton-combined",
    339        },
    340        {
    341          id: "zoom-reset-button",
    342          command: "cmd_fullZoomReset",
    343          closemenu: "none",
    344          tooltiptext: "tooltiptext2",
    345          shortcutId: "key_fullZoomReset",
    346          class: "toolbarbutton-1 toolbarbutton-combined",
    347        },
    348        {
    349          id: "zoom-in-button",
    350          command: "cmd_fullZoomEnlarge",
    351          closemenu: "none",
    352          label: true,
    353          tooltiptext: "tooltiptext2",
    354          shortcutId: "key_fullZoomEnlarge",
    355          class: "toolbarbutton-1 toolbarbutton-combined",
    356        },
    357      ];
    358 
    359      let node = aDocument.createXULElement("toolbaritem");
    360      node.setAttribute("id", "zoom-controls");
    361      node.setAttribute(
    362        "label",
    363        lazy.CustomizableUI.getLocalizedProperty(this, "label")
    364      );
    365      node.setAttribute(
    366        "title",
    367        lazy.CustomizableUI.getLocalizedProperty(this, "tooltiptext")
    368      );
    369      // Set this as an attribute in addition to the property to make sure we can style correctly.
    370      node.setAttribute("removable", "true");
    371      node.classList.add("chromeclass-toolbar-additional");
    372      node.classList.add("toolbaritem-combined-buttons");
    373 
    374      buttons.forEach(function (aButton, aIndex) {
    375        if (aIndex != 0) {
    376          node.appendChild(aDocument.createXULElement("separator"));
    377        }
    378        let btnNode = aDocument.createXULElement("toolbarbutton");
    379        setAttributes(btnNode, aButton);
    380        node.appendChild(btnNode);
    381      });
    382      return node;
    383    },
    384  },
    385  {
    386    id: "edit-controls",
    387    type: "custom",
    388    tooltiptext: "edit-controls.tooltiptext2",
    389    onBuild(aDocument) {
    390      let buttons = [
    391        {
    392          id: "cut-button",
    393          command: "cmd_cut",
    394          label: true,
    395          tooltiptext: "tooltiptext2",
    396          shortcutId: "key_cut",
    397          class: "toolbarbutton-1 toolbarbutton-combined",
    398        },
    399        {
    400          id: "copy-button",
    401          command: "cmd_copy",
    402          label: true,
    403          tooltiptext: "tooltiptext2",
    404          shortcutId: "key_copy",
    405          class: "toolbarbutton-1 toolbarbutton-combined",
    406        },
    407        {
    408          id: "paste-button",
    409          command: "cmd_paste",
    410          label: true,
    411          tooltiptext: "tooltiptext2",
    412          shortcutId: "key_paste",
    413          class: "toolbarbutton-1 toolbarbutton-combined",
    414        },
    415      ];
    416 
    417      let node = aDocument.createXULElement("toolbaritem");
    418      node.setAttribute("id", "edit-controls");
    419      node.setAttribute(
    420        "label",
    421        lazy.CustomizableUI.getLocalizedProperty(this, "label")
    422      );
    423      node.setAttribute(
    424        "title",
    425        lazy.CustomizableUI.getLocalizedProperty(this, "tooltiptext")
    426      );
    427      // Set this as an attribute in addition to the property to make sure we can style correctly.
    428      node.setAttribute("removable", "true");
    429      node.classList.add("chromeclass-toolbar-additional");
    430      node.classList.add("toolbaritem-combined-buttons");
    431 
    432      buttons.forEach(function (aButton, aIndex) {
    433        if (aIndex != 0) {
    434          node.appendChild(aDocument.createXULElement("separator"));
    435        }
    436        let btnNode = aDocument.createXULElement("toolbarbutton");
    437        setAttributes(btnNode, aButton);
    438        node.appendChild(btnNode);
    439      });
    440 
    441      let listener = {
    442        onWidgetInstanceRemoved: (aWidgetId, aDoc) => {
    443          if (aWidgetId != this.id || aDoc != aDocument) {
    444            return;
    445          }
    446          lazy.CustomizableUI.removeListener(listener);
    447        },
    448        onWidgetOverflow(aWidgetNode) {
    449          if (aWidgetNode == node) {
    450            node.ownerGlobal.updateEditUIVisibility();
    451          }
    452        },
    453        onWidgetUnderflow(aWidgetNode) {
    454          if (aWidgetNode == node) {
    455            node.ownerGlobal.updateEditUIVisibility();
    456          }
    457        },
    458      };
    459      lazy.CustomizableUI.addListener(listener);
    460 
    461      return node;
    462    },
    463  },
    464  {
    465    id: "characterencoding-button",
    466    l10nId: "repair-text-encoding-button",
    467    onCommand(aEvent) {
    468      aEvent.view.BrowserCommands.forceEncodingDetection();
    469    },
    470  },
    471  {
    472    id: "email-link-button",
    473    l10nId: "toolbar-button-email-link",
    474    onCommand(aEvent) {
    475      let win = aEvent.view;
    476      win.MailIntegration.sendLinkForBrowser(win.gBrowser.selectedBrowser);
    477    },
    478  },
    479  {
    480    id: "logins-button",
    481    l10nId: "toolbar-button-logins",
    482    onCommand(aEvent) {
    483      let window = aEvent.view;
    484      lazy.LoginHelper.openPasswordManager(window, { entryPoint: "Toolbar" });
    485    },
    486  },
    487 ];
    488 
    489 if (Services.prefs.getBoolPref("identity.fxaccounts.enabled")) {
    490  CustomizableWidgets.push({
    491    id: "sync-button",
    492    l10nId: "toolbar-button-synced-tabs",
    493    type: "view",
    494    viewId: "PanelUI-remotetabs",
    495    onViewShowing(aEvent) {
    496      let panelview = aEvent.target;
    497      let doc = panelview.ownerDocument;
    498 
    499      let syncNowBtn = panelview.querySelector(".syncnow-label");
    500      let l10nId = syncNowBtn.getAttribute(
    501        panelview.ownerGlobal.gSync._isCurrentlySyncing
    502          ? "syncing-data-l10n-id"
    503          : "sync-now-data-l10n-id"
    504      );
    505      doc.l10n.setAttributes(syncNowBtn, l10nId);
    506 
    507      let SyncedTabsPanelList = doc.defaultView.SyncedTabsPanelList;
    508      panelview.syncedTabsPanelList = new SyncedTabsPanelList(
    509        panelview,
    510        lazy.PanelMultiView.getViewNode(doc, "PanelUI-remotetabs-deck"),
    511        lazy.PanelMultiView.getViewNode(doc, "PanelUI-remotetabs-tabslist")
    512      );
    513      panelview.addEventListener("command", this);
    514      let syncNowButton = lazy.PanelMultiView.getViewNode(
    515        aEvent.target.ownerDocument,
    516        "PanelUI-remotetabs-syncnow"
    517      );
    518      syncNowButton.addEventListener("mouseover", this);
    519    },
    520    onViewHiding(aEvent) {
    521      let panelview = aEvent.target;
    522      panelview.syncedTabsPanelList.destroy();
    523      panelview.syncedTabsPanelList = null;
    524      panelview.removeEventListener("command", this);
    525      let syncNowButton = lazy.PanelMultiView.getViewNode(
    526        aEvent.target.ownerDocument,
    527        "PanelUI-remotetabs-syncnow"
    528      );
    529      syncNowButton.removeEventListener("mouseover", this);
    530    },
    531    handleEvent(aEvent) {
    532      let button = aEvent.target;
    533      let { gSync } = button.ownerGlobal;
    534      switch (aEvent.type) {
    535        case "mouseover":
    536          gSync.refreshSyncButtonsTooltip();
    537          break;
    538        case "command": {
    539          switch (button.id) {
    540            case "PanelUI-remotetabs-syncnow":
    541              gSync.doSync();
    542              break;
    543            case "PanelUI-remotetabs-view-managedevices":
    544              gSync.openDevicesManagementPage("syncedtabs-menupanel");
    545              break;
    546            case "PanelUI-remotetabs-tabsdisabledpane-button":
    547            case "PanelUI-remotetabs-setupsync-button":
    548            case "PanelUI-remotetabs-syncdisabled-button":
    549            case "PanelUI-remotetabs-reauthsync-button":
    550            case "PanelUI-remotetabs-unverified-button":
    551              gSync.openPrefs("synced-tabs");
    552              break;
    553            case "PanelUI-remotetabs-connect-device-button":
    554              gSync.openConnectAnotherDevice("synced-tabs");
    555              break;
    556          }
    557        }
    558      }
    559    },
    560  });
    561 }
    562 
    563 let preferencesButton = {
    564  id: "preferences-button",
    565  l10nId: "toolbar-settings-button",
    566  onCommand(aEvent) {
    567    let win = aEvent.target.ownerGlobal;
    568    win.openPreferences(undefined);
    569  },
    570 };
    571 if (AppConstants.platform == "macosx") {
    572  preferencesButton.shortcutId = "key_preferencesCmdMac";
    573 }
    574 CustomizableWidgets.push(preferencesButton);
    575 
    576 if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
    577  CustomizableWidgets.push({
    578    id: "panic-button",
    579    type: "view",
    580    viewId: "PanelUI-panicView",
    581 
    582    forgetButtonCalled(aEvent) {
    583      let doc = aEvent.target.ownerDocument;
    584      let group = doc.getElementById("PanelUI-panic-timeSpan");
    585      let itemsToClear = [
    586        "cookies",
    587        "history",
    588        "openWindows",
    589        "formdata",
    590        "sessions",
    591        "cache",
    592        "downloads",
    593        "offlineApps",
    594      ];
    595      let newWindowPrivateState = PrivateBrowsingUtils.isWindowPrivate(
    596        doc.defaultView
    597      )
    598        ? "private"
    599        : "non-private";
    600      let promise = lazy.Sanitizer.sanitize(itemsToClear, {
    601        ignoreTimespan: false,
    602        range: lazy.Sanitizer.getClearRange(+group.value),
    603        privateStateForNewWindow: newWindowPrivateState,
    604      });
    605      promise.then(function () {
    606        let otherWindow = Services.wm.getMostRecentWindow("navigator:browser");
    607        if (otherWindow.closed) {
    608          console.error("Got a closed window!");
    609        }
    610        if (otherWindow.PanicButtonNotifier) {
    611          otherWindow.PanicButtonNotifier.notify();
    612        } else {
    613          otherWindow.PanicButtonNotifierShouldNotify = true;
    614        }
    615      });
    616    },
    617    handleEvent(aEvent) {
    618      switch (aEvent.type) {
    619        case "command":
    620          this.forgetButtonCalled(aEvent);
    621          break;
    622      }
    623    },
    624    onViewShowing(aEvent) {
    625      let win = aEvent.target.ownerGlobal;
    626      let doc = win.document;
    627      let eventBlocker = null;
    628      eventBlocker = doc.l10n.translateElements([aEvent.target]);
    629 
    630      let forgetButton = aEvent.target.querySelector(
    631        "#PanelUI-panic-view-button"
    632      );
    633      let group = doc.getElementById("PanelUI-panic-timeSpan");
    634      group.selectedItem = doc.getElementById("PanelUI-panic-5min");
    635      forgetButton.addEventListener("command", this);
    636 
    637      if (eventBlocker) {
    638        aEvent.detail.addBlocker(eventBlocker);
    639      }
    640    },
    641    onViewHiding(aEvent) {
    642      let forgetButton = aEvent.target.querySelector(
    643        "#PanelUI-panic-view-button"
    644      );
    645      forgetButton.removeEventListener("command", this);
    646    },
    647  });
    648 }
    649 
    650 if (PrivateBrowsingUtils.enabled) {
    651  CustomizableWidgets.push({
    652    id: "privatebrowsing-button",
    653    l10nId: "toolbar-button-new-private-window",
    654    shortcutId: "key_privatebrowsing",
    655    onCommand(e) {
    656      let win = e.target.ownerGlobal;
    657      win.OpenBrowserWindow({ private: true });
    658    },
    659  });
    660 }