summaryrefslogtreecommitdiffstats
path: root/browser/components/firefoxview/opentabs.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/firefoxview/opentabs.mjs')
-rw-r--r--browser/components/firefoxview/opentabs.mjs252
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,
};
});