tor-browser

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

browser-allTabsMenu.js (8226B)


      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 ChromeUtils.defineESModuleGetters(this, {
      6  BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.sys.mjs",
      7  GroupsPanel: "moz-src:///browser/components/tabbrowser/GroupsList.sys.mjs",
      8  NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
      9  TabsPanel: "moz-src:///browser/components/tabbrowser/TabsList.sys.mjs",
     10 });
     11 
     12 var gTabsPanel = {
     13  kElements: {
     14    allTabsButton: "alltabs-button",
     15    allTabsView: "allTabsMenu-allTabsView",
     16    allTabsViewTabs: "allTabsMenu-allTabsView-tabs",
     17    dropIndicator: "allTabsMenu-dropIndicator",
     18    containerTabsView: "allTabsMenu-containerTabsView",
     19    hiddenTabsButton: "allTabsMenu-hiddenTabsButton",
     20    hiddenTabsView: "allTabsMenu-hiddenTabsView",
     21    hiddenTabsViewTabs: "allTabsMenu-hiddenTabsView-tabs",
     22    hiddenAudioTabs: "allTabsMenu-allTabsView-hiddenAudio-tabs",
     23    groupsView: "allTabsMenu-groupsView",
     24    groupsSubView: "allTabsMenu-groupsSubView",
     25  },
     26  _initialized: false,
     27  _initializedElements: false,
     28 
     29  initElements() {
     30    if (this._initializedElements) {
     31      return;
     32    }
     33    let template = document.getElementById("allTabsMenu-container");
     34    template.replaceWith(template.content);
     35 
     36    for (let [name, id] of Object.entries(this.kElements)) {
     37      this[name] = document.getElementById(id);
     38    }
     39    this._initializedElements = true;
     40  },
     41 
     42  hasHiddenTabsExcludingFxView() {
     43    // Exclude Firefox View, see Bug 1880138.
     44    return gBrowser.tabs.some(
     45      tab => tab.hidden && tab != FirefoxViewHandler.tab
     46    );
     47  },
     48 
     49  init() {
     50    if (this._initialized) {
     51      return;
     52    }
     53 
     54    this.initElements();
     55 
     56    this.hiddenAudioTabsPopup = new TabsPanel({
     57      view: this.allTabsView,
     58      containerNode: this.hiddenAudioTabs,
     59      filterFn: tab => tab.soundPlaying,
     60      onlyHiddenTabs: true,
     61    });
     62    this.allTabsPanel = new TabsPanel({
     63      view: this.allTabsView,
     64      containerNode: this.allTabsViewTabs,
     65      filterFn: tab => !tab.hidden,
     66      dropIndicator: this.dropIndicator,
     67      onlyHiddenTabs: false,
     68    });
     69    this.groupsPanel = new GroupsPanel({
     70      view: this.allTabsView,
     71      containerNode: this.groupsView,
     72    });
     73    this.showAllGroupsPanel = new GroupsPanel({
     74      view: this.groupsSubView,
     75      containerNode: document.getElementById("allTabsMenu-groupsSubView-body"),
     76      showAll: true,
     77    });
     78 
     79    this.allTabsView.addEventListener("ViewShowing", () => {
     80      PanelUI._ensureShortcutsShown(this.allTabsView);
     81 
     82      let containersEnabled =
     83        Services.prefs.getBoolPref("privacy.userContext.enabled") &&
     84        !PrivateBrowsingUtils.isWindowPrivate(window);
     85      document.getElementById("allTabsMenu-containerTabsButton").hidden =
     86        !containersEnabled;
     87 
     88      const hasHiddenTabs = this.hasHiddenTabsExcludingFxView();
     89      document.getElementById("allTabsMenu-hiddenTabsButton").hidden =
     90        !hasHiddenTabs;
     91      document.getElementById("allTabsMenu-hiddenTabsSeparator").hidden =
     92        !hasHiddenTabs;
     93 
     94      let closeDuplicateEnabled = Services.prefs.getBoolPref(
     95        "browser.tabs.context.close-duplicate.enabled"
     96      );
     97      let closeDuplicateTabsItem = document.getElementById(
     98        "allTabsMenu-closeDuplicateTabs"
     99      );
    100      closeDuplicateTabsItem.hidden = !closeDuplicateEnabled;
    101      closeDuplicateTabsItem.disabled =
    102        !closeDuplicateEnabled || !gBrowser.getAllDuplicateTabsToClose().length;
    103 
    104      let syncedTabs = document.getElementById("allTabsMenu-syncedTabs");
    105      syncedTabs.hidden =
    106        !PlacesUIUtils.shouldShowTabsFromOtherComputersMenuitem();
    107    });
    108 
    109    this.allTabsView.addEventListener("ViewShown", () =>
    110      this.allTabsView
    111        .querySelector(".all-tabs-item[selected]")
    112        ?.scrollIntoView({ block: "center" })
    113    );
    114 
    115    this.allTabsView.addEventListener("command", event => {
    116      let { target } = event;
    117      let { PanelUI } = target.ownerGlobal;
    118      switch (target.id) {
    119        case "allTabsMenu-searchTabs":
    120          Glean.browserUiInteraction.listAllTabsAction.search_tabs.add(1);
    121          this.searchTabs();
    122          break;
    123        case "allTabsMenu-closeDuplicateTabs":
    124          Glean.browserUiInteraction.listAllTabsAction.close_all_duplicates.add(
    125            1
    126          );
    127          gBrowser.removeAllDuplicateTabs();
    128          break;
    129        case "allTabsMenu-containerTabsButton":
    130          PanelUI.showSubView(this.kElements.containerTabsView, target);
    131          break;
    132        case "allTabsMenu-hiddenTabsButton":
    133          PanelUI.showSubView(this.kElements.hiddenTabsView, target);
    134          break;
    135        case "allTabsMenu-syncedTabs":
    136          Glean.browserUiInteraction.listAllTabsAction.tabs_from_devices.add(1);
    137          SidebarController.show("viewTabsSidebar");
    138          break;
    139        case "allTabsMenu-groupsViewShowMore":
    140          PanelUI.showSubView(this.kElements.groupsSubView, target);
    141          break;
    142      }
    143    });
    144 
    145    let containerTabsMenuSeparator =
    146      this.containerTabsView.querySelector("toolbarseparator");
    147    this.containerTabsView.addEventListener("ViewShowing", e => {
    148      let elements = [];
    149      let frag = document.createDocumentFragment();
    150 
    151      ContextualIdentityService.getPublicIdentities().forEach(identity => {
    152        let menuitem = document.createXULElement("toolbarbutton");
    153        menuitem.setAttribute("class", "subviewbutton subviewbutton-iconic");
    154        if (identity.name) {
    155          menuitem.setAttribute("label", identity.name);
    156        } else {
    157          document.l10n.setAttributes(menuitem, identity.l10nId);
    158        }
    159        // The styles depend on this.
    160        menuitem.setAttribute("usercontextid", identity.userContextId);
    161        // The command handler depends on this.
    162        menuitem.setAttribute("data-usercontextid", identity.userContextId);
    163        menuitem.classList.add("identity-icon-" + identity.icon);
    164        menuitem.classList.add("identity-color-" + identity.color);
    165 
    166        menuitem.setAttribute("command", "Browser:NewUserContextTab");
    167 
    168        frag.appendChild(menuitem);
    169        elements.push(menuitem);
    170      });
    171 
    172      e.target.addEventListener(
    173        "ViewHiding",
    174        () => {
    175          for (let element of elements) {
    176            element.remove();
    177          }
    178        },
    179        { once: true }
    180      );
    181      containerTabsMenuSeparator.parentNode.insertBefore(
    182        frag,
    183        containerTabsMenuSeparator
    184      );
    185    });
    186 
    187    this.hiddenTabsPopup = new TabsPanel({
    188      view: this.hiddenTabsView,
    189      containerNode: this.hiddenTabsViewTabs,
    190      filterFn: tab => tab != FirefoxViewHandler.tab,
    191      onlyHiddenTabs: true,
    192    });
    193 
    194    this._initialized = true;
    195  },
    196 
    197  get canOpen() {
    198    this.initElements();
    199    return isElementVisible(this.allTabsButton);
    200  },
    201 
    202  showAllTabsPanel(event, entrypoint = "unknown") {
    203    // Note that event may be null.
    204 
    205    // Only space and enter should open the popup, ignore other keypresses:
    206    if (event?.type == "keypress" && event.key != "Enter" && event.key != " ") {
    207      return;
    208    }
    209    this.init();
    210    if (this.canOpen) {
    211      Glean.browserUiInteraction.allTabsPanelEntrypoint[entrypoint].add(1);
    212      BrowserUsageTelemetry.recordInteractionEvent(
    213        entrypoint,
    214        "all-tabs-panel-entrypoint"
    215      );
    216      PanelUI.showSubView(
    217        this.kElements.allTabsView,
    218        this.allTabsButton,
    219        event
    220      );
    221    }
    222  },
    223 
    224  hideAllTabsPanel() {
    225    let panel = this.allTabsView?.closest("panel");
    226    if (panel) {
    227      PanelMultiView.hidePopup(panel);
    228    }
    229  },
    230 
    231  showHiddenTabsPanel(event, entrypoint = "unknown") {
    232    this.init();
    233    if (!this.canOpen) {
    234      return;
    235    }
    236    this.allTabsView.addEventListener(
    237      "ViewShown",
    238      () => {
    239        PanelUI.showSubView(
    240          this.kElements.hiddenTabsView,
    241          this.hiddenTabsButton
    242        );
    243      },
    244      { once: true }
    245    );
    246    this.showAllTabsPanel(event, entrypoint);
    247  },
    248 
    249  searchTabs() {
    250    gURLBar.search(UrlbarTokenizer.RESTRICT.OPENPAGE, {
    251      searchModeEntry: "tabmenu",
    252    });
    253  },
    254 };