diff options
Diffstat (limited to 'browser/components/firefoxview/opentabs.mjs')
-rw-r--r-- | browser/components/firefoxview/opentabs.mjs | 252 |
1 files changed, 224 insertions, 28 deletions
diff --git a/browser/components/firefoxview/opentabs.mjs b/browser/components/firefoxview/opentabs.mjs index 6ac63a4b3f..8d7723e931 100644 --- a/browser/components/firefoxview/opentabs.mjs +++ b/browser/components/firefoxview/opentabs.mjs @@ -21,8 +21,10 @@ import { ViewPage, ViewPageContent } from "./viewpage.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { + BookmarkList: "resource://gre/modules/BookmarkList.sys.mjs", ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.sys.mjs", + NewTabUtils: "resource://gre/modules/NewTabUtils.sys.mjs", NonPrivateTabs: "resource:///modules/OpenTabs.sys.mjs", getTabsTargetForWindow: "resource:///modules/OpenTabs.sys.mjs", PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", @@ -106,6 +108,10 @@ class OpenTabsInView extends ViewPage { this ); } + + this.bookmarkList = new lazy.BookmarkList(this.#getAllTabUrls(), () => + this.viewCards.forEach(card => card.requestUpdate()) + ); } shouldUpdate(changedProperties) { @@ -141,6 +147,8 @@ class OpenTabsInView extends ViewPage { this ); } + + this.bookmarkList.removeListeners(); } viewVisibleCallback() { @@ -161,6 +169,13 @@ class OpenTabsInView extends ViewPage { } } + #getAllTabUrls() { + return this.openTabsTarget + .getAllTabs() + .map(({ linkedBrowser }) => linkedBrowser?.currentURI?.spec) + .filter(Boolean); + } + render() { if (this.recentBrowsing) { return this.getRecentBrowsingTemplate(); @@ -268,6 +283,7 @@ class OpenTabsInView extends ViewPage { winID: currentWindowIndex, })}" .searchQuery=${this.searchQuery} + .bookmarkList=${this.bookmarkList} ></view-opentabs-card> ` )} @@ -282,6 +298,7 @@ class OpenTabsInView extends ViewPage { data-l10n-id="firefoxview-opentabs-window-header" data-l10n-args="${JSON.stringify({ winID })}" .searchQuery=${this.searchQuery} + .bookmarkList=${this.bookmarkList} ></view-opentabs-card> ` )} @@ -318,6 +335,7 @@ class OpenTabsInView extends ViewPage { .recentBrowsing=${true} .paused=${this.paused} .searchQuery=${this.searchQuery} + .bookmarkList=${this.bookmarkList} ></view-opentabs-card>`; } @@ -330,13 +348,9 @@ class OpenTabsInView extends ViewPage { switch (type) { case "TabRecencyChange": case "TabChange": - // if we're switching away from our tab, we can halt any updates immediately - if (!this.isSelectedBrowserTab) { - this.stop(); - return; - } windowIds = detail.windowIds; this._updateWindowList(); + this.bookmarkList.setTrackedUrls(this.#getAllTabUrls()); break; } if (this.recentBrowsing) { @@ -390,6 +404,7 @@ class OpenTabsInViewCard extends ViewPageContent { searchResults: { type: Array }, showAll: { type: Boolean }, cumulativeSearches: { type: Number }, + bookmarkList: { type: Object }, }; static MAX_TABS_FOR_COMPACT_HEIGHT = 7; @@ -470,6 +485,14 @@ class OpenTabsInViewCard extends ViewPageContent { } onTabListRowClick(event) { + // Don't open pinned tab if mute/unmute indicator button selected + if ( + Array.from(event.explicitOriginalTarget.classList).includes( + "fxview-tab-row-pinned-media-button" + ) + ) { + return; + } const tab = event.originalTarget.tabElement; const browserWindow = tab.ownerGlobal; browserWindow.focus(); @@ -497,6 +520,18 @@ class OpenTabsInViewCard extends ViewPageContent { } } + closeTab(event) { + const tab = event.originalTarget.tabElement; + tab?.ownerGlobal.gBrowser.removeTab(tab); + + Services.telemetry.recordEvent( + "firefoxview_next", + "close_open_tab", + "tabs", + null + ); + } + viewVisibleCallback() { this.getRootNode().host.toggleVisibilityInCardContainer(true); } @@ -531,15 +566,18 @@ class OpenTabsInViewCard extends ViewPageContent { )} <div class="fxview-tab-list-container" slot="main"> <fxview-tab-list - class="with-context-menu" .hasPopup=${"menu"} ?compactRows=${this.classList.contains("width-limited")} @fxview-tab-list-primary-action=${this.onTabListRowClick} @fxview-tab-list-secondary-action=${this.openContextMenu} + @fxview-tab-list-tertiary-action=${this.closeTab} + secondaryActionClass="options-button" + tertiaryActionClass="dismiss-button" .maxTabsLength=${this.getMaxTabsLength()} - .tabItems=${this.searchResults || getTabListItems(this.tabs)} + .tabItems=${this.searchResults || + getTabListItems(this.tabs, this.recentBrowsing)} .searchQuery=${this.searchQuery} - .showTabIndicators=${true} + .pinnedTabsGridView=${!this.recentBrowsing} ><view-opentabs-contextmenu slot="menu"></view-opentabs-contextmenu> </fxview-tab-list> </div> @@ -590,6 +628,28 @@ class OpenTabsInViewCard extends ViewPageContent { ? searchTabList(this.searchQuery, getTabListItems(this.tabs)) : null; } + + updated() { + this.updateBookmarkStars(); + } + + async updateBookmarkStars() { + const tabItems = [...this.tabList.tabItems]; + for (const row of tabItems) { + const isBookmark = await this.bookmarkList.isBookmark(row.url); + if (isBookmark && !row.indicators.includes("bookmark")) { + row.indicators.push("bookmark"); + } + if (!isBookmark && row.indicators.includes("bookmark")) { + row.indicators = row.indicators.filter(i => i !== "bookmark"); + } + row.primaryL10nId = getPrimaryL10nId( + this.isRecentBrowsing, + row.indicators + ); + } + this.tabList.tabItems = tabItems; + } } customElements.define("view-opentabs-card", OpenTabsInViewCard); @@ -655,6 +715,29 @@ class OpenTabsContextMenu extends MozLitElement { this.ownerViewPage.recordContextMenuTelemetry("close-tab", e); } + pinTab(e) { + const tab = this.triggerNode.tabElement; + tab?.ownerGlobal.gBrowser.pinTab(tab); + this.ownerViewPage.recordContextMenuTelemetry("pin-tab", e); + } + + unpinTab(e) { + const tab = this.triggerNode.tabElement; + tab?.ownerGlobal.gBrowser.unpinTab(tab); + this.ownerViewPage.recordContextMenuTelemetry("unpin-tab", e); + } + + toggleAudio(e) { + const tab = this.triggerNode.tabElement; + tab.toggleMuteAudio(); + this.ownerViewPage.recordContextMenuTelemetry( + `${ + this.triggerNode.indicators.includes("muted") ? "unmute" : "mute" + }-tab`, + e + ); + } + moveTabsToStart(e) { const tab = this.triggerNode.tabElement; tab?.ownerGlobal.gBrowser.moveTabsToStart(tab); @@ -749,16 +832,25 @@ class OpenTabsContextMenu extends MozLitElement { /> <panel-list data-tab-type="opentabs"> <panel-item - data-l10n-id="fxviewtabrow-close-tab" - data-l10n-attrs="accesskey" - @click=${this.closeTab} - ></panel-item> - <panel-item data-l10n-id="fxviewtabrow-move-tab" data-l10n-attrs="accesskey" submenu="move-tab-menu" >${this.moveMenuTemplate()}</panel-item > + <panel-item + data-l10n-id=${tab.pinned + ? "fxviewtabrow-unpin-tab" + : "fxviewtabrow-pin-tab"} + data-l10n-attrs="accesskey" + @click=${tab.pinned ? this.unpinTab : this.pinTab} + ></panel-item> + <panel-item + data-l10n-id=${tab.hasAttribute("muted") + ? "fxviewtabrow-unmute-tab" + : "fxviewtabrow-mute-tab"} + data-l10n-attrs="accesskey" + @click=${this.toggleAudio} + ></panel-item> <hr /> <panel-item data-l10n-id="fxviewtabrow-copy-link" @@ -798,36 +890,140 @@ function getContainerObj(tab) { } /** + * Gets an array of tab indicators (if any) when normalizing for fxview-tab-list + * + * @param {MozTabbrowserTab[]} tab + * Tab to fetch container info on. + * @returns {Array[]} + * Array of named tab indicators + */ +function getIndicatorsForTab(tab) { + const url = tab.linkedBrowser?.currentURI?.spec || ""; + let tabIndicators = []; + let hasAttention = + (tab.pinned && + (tab.hasAttribute("attention") || tab.hasAttribute("titlechanged"))) || + (!tab.pinned && tab.hasAttribute("attention")); + + if (tab.pinned) { + tabIndicators.push("pinned"); + } + if (getContainerObj(tab)) { + tabIndicators.push("container"); + } + if (hasAttention) { + tabIndicators.push("attention"); + } + if (tab.hasAttribute("soundplaying") && !tab.hasAttribute("muted")) { + tabIndicators.push("soundplaying"); + } + if (tab.hasAttribute("muted")) { + tabIndicators.push("muted"); + } + if (checkIfPinnedNewTab(url)) { + tabIndicators.push("pinnedOnNewTab"); + } + + return tabIndicators; +} +/** + * Gets the primary l10n id for a tab when normalizing for fxview-tab-list + * + * @param {boolean} isRecentBrowsing + * Whether the tabs are going to be displayed on the Recent Browsing page or not + * @param {Array[]} tabIndicators + * Array of tab indicators for the given tab + * @returns {string} + * L10n ID string + */ +function getPrimaryL10nId(isRecentBrowsing, tabIndicators) { + let indicatorL10nId = null; + if (!isRecentBrowsing) { + if ( + tabIndicators?.includes("pinned") && + tabIndicators?.includes("bookmark") + ) { + indicatorL10nId = "firefoxview-opentabs-bookmarked-pinned-tab"; + } else if (tabIndicators?.includes("pinned")) { + indicatorL10nId = "firefoxview-opentabs-pinned-tab"; + } else if (tabIndicators?.includes("bookmark")) { + indicatorL10nId = "firefoxview-opentabs-bookmarked-tab"; + } + } + return indicatorL10nId; +} + +/** + * Gets the primary l10n args for a tab when normalizing for fxview-tab-list + * + * @param {MozTabbrowserTab[]} tab + * Tab to fetch container info on. + * @param {boolean} isRecentBrowsing + * Whether the tabs are going to be displayed on the Recent Browsing page or not + * @param {string} url + * URL for the given tab + * @returns {string} + * L10n ID args + */ +function getPrimaryL10nArgs(tab, isRecentBrowsing, url) { + return JSON.stringify({ tabTitle: tab.label, url }); +} + +/** + * Check if a given url is pinned on the new tab page + * + * @param {string} url + * url to check + * @returns {boolean} + * is tabbed pinned on new tab page + */ +function checkIfPinnedNewTab(url) { + return url && lazy.NewTabUtils.pinnedLinks.isPinned({ url }); +} + +/** * Convert a list of tabs into the format expected by the fxview-tab-list * component. * * @param {MozTabbrowserTab[]} tabs * Tabs to format. + * @param {boolean} isRecentBrowsing + * Whether the tabs are going to be displayed on the Recent Browsing page or not * @returns {object[]} * Formatted objects. */ -function getTabListItems(tabs) { - let filtered = tabs?.filter( - tab => !tab.closing && !tab.hidden && !tab.pinned - ); +function getTabListItems(tabs, isRecentBrowsing) { + let filtered = tabs?.filter(tab => !tab.closing && !tab.hidden); return filtered.map(tab => { - const url = tab.linkedBrowser?.currentURI?.spec || ""; + let tabIndicators = getIndicatorsForTab(tab); + let containerObj = getContainerObj(tab); + const url = tab?.linkedBrowser?.currentURI?.spec || ""; return { - attention: tab.hasAttribute("attention"), - containerObj: getContainerObj(tab), + containerObj, + indicators: tabIndicators, icon: tab.getAttribute("image"), - muted: tab.hasAttribute("muted"), - pinned: tab.pinned, - primaryL10nId: "firefoxview-opentabs-tab-row", - primaryL10nArgs: JSON.stringify({ url }), - secondaryL10nId: "fxviewtabrow-options-menu-button", - secondaryL10nArgs: JSON.stringify({ tabTitle: tab.label }), - soundPlaying: tab.hasAttribute("soundplaying"), + primaryL10nId: getPrimaryL10nId(isRecentBrowsing, tabIndicators), + primaryL10nArgs: getPrimaryL10nArgs(tab, isRecentBrowsing, url), + secondaryL10nId: + isRecentBrowsing || (!isRecentBrowsing && !tab.pinned) + ? "fxviewtabrow-options-menu-button" + : null, + secondaryL10nArgs: + isRecentBrowsing || (!isRecentBrowsing && !tab.pinned) + ? JSON.stringify({ tabTitle: tab.label }) + : null, + tertiaryL10nId: + isRecentBrowsing || (!isRecentBrowsing && !tab.pinned) + ? "fxviewtabrow-close-tab-button" + : null, + tertiaryL10nArgs: + isRecentBrowsing || (!isRecentBrowsing && !tab.pinned) + ? JSON.stringify({ tabTitle: tab.label }) + : null, tabElement: tab, time: tab.lastAccessed, title: tab.label, - titleChanged: tab.hasAttribute("titlechanged"), url, }; }); |