From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- browser/components/firefoxview/recentlyclosed.mjs | 473 ++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 browser/components/firefoxview/recentlyclosed.mjs (limited to 'browser/components/firefoxview/recentlyclosed.mjs') diff --git a/browser/components/firefoxview/recentlyclosed.mjs b/browser/components/firefoxview/recentlyclosed.mjs new file mode 100644 index 0000000000..6e7e06c1f4 --- /dev/null +++ b/browser/components/firefoxview/recentlyclosed.mjs @@ -0,0 +1,473 @@ +/* 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 { + classMap, + html, + ifDefined, + when, +} from "chrome://global/content/vendor/lit.all.mjs"; +import { + isSearchEnabled, + searchTabList, + MAX_TABS_FOR_RECENT_BROWSING, +} from "./helpers.mjs"; +import { ViewPage } from "./viewpage.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "chrome://browser/content/firefoxview/card-container.mjs"; +// eslint-disable-next-line import/no-unassigned-import +import "chrome://browser/content/firefoxview/fxview-tab-list.mjs"; + +const lazy = {}; +ChromeUtils.defineESModuleGetters(lazy, { + SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs", +}); + +const SS_NOTIFY_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed"; +const SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH = "sessionstore-browser-shutdown-flush"; +const NEVER_REMEMBER_HISTORY_PREF = "browser.privatebrowsing.autostart"; +const INCLUDE_CLOSED_TABS_FROM_CLOSED_WINDOWS = + "browser.sessionstore.closedTabsFromClosedWindows"; + +function getWindow() { + return window.browsingContext.embedderWindowGlobal.browsingContext.window; +} + +class RecentlyClosedTabsInView extends ViewPage { + constructor() { + super(); + this._started = false; + this.boundObserve = (...args) => this.observe(...args); + this.firstUpdateComplete = false; + this.fullyUpdated = false; + this.maxTabsLength = this.recentBrowsing + ? MAX_TABS_FOR_RECENT_BROWSING + : -1; + this.recentlyClosedTabs = []; + this.searchQuery = ""; + this.searchResults = null; + this.showAll = false; + this.cumulativeSearches = 0; + } + + static properties = { + ...ViewPage.properties, + searchResults: { type: Array }, + showAll: { type: Boolean }, + cumulativeSearches: { type: Number }, + }; + + static queries = { + cardEl: "card-container", + emptyState: "fxview-empty-state", + searchTextbox: "fxview-search-textbox", + tabList: "fxview-tab-list", + }; + + observe(subject, topic, data) { + if ( + topic == SS_NOTIFY_CLOSED_OBJECTS_CHANGED || + (topic == SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH && + subject.ownerGlobal == getWindow()) + ) { + this.updateRecentlyClosedTabs(); + } + } + + start() { + if (this._started) { + return; + } + this._started = true; + this.paused = false; + this.updateRecentlyClosedTabs(); + + Services.obs.addObserver( + this.boundObserve, + SS_NOTIFY_CLOSED_OBJECTS_CHANGED + ); + Services.obs.addObserver( + this.boundObserve, + SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH + ); + + if (this.recentBrowsing) { + this.recentBrowsingElement.addEventListener( + "fxview-search-textbox-query", + this + ); + } + + this.toggleVisibilityInCardContainer(); + } + + stop() { + if (!this._started) { + return; + } + this._started = false; + + Services.obs.removeObserver( + this.boundObserve, + SS_NOTIFY_CLOSED_OBJECTS_CHANGED + ); + Services.obs.removeObserver( + this.boundObserve, + SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH + ); + + if (this.recentBrowsing) { + this.recentBrowsingElement.removeEventListener( + "fxview-search-textbox-query", + this + ); + } + + this.toggleVisibilityInCardContainer(); + } + + disconnectedCallback() { + super.disconnectedCallback(); + this.stop(); + } + + handleEvent(event) { + if (this.recentBrowsing && event.type === "fxview-search-textbox-query") { + this.onSearchQuery(event); + } + } + + // We remove all the observers when the instance is not visible to the user + viewHiddenCallback() { + this.stop(); + } + + // We add observers and check for changes to the session store once the user return to this tab. + // or the instance becomes visible to the user + viewVisibleCallback() { + this.start(); + } + + firstUpdated() { + this.firstUpdateComplete = true; + } + + getTabStateValue(tab, key) { + let value = ""; + const tabEntries = tab.state.entries; + const activeIndex = tab.state.index - 1; + + if (activeIndex >= 0 && tabEntries[activeIndex]) { + value = tabEntries[activeIndex][key]; + } + + return value; + } + + updateRecentlyClosedTabs() { + let recentlyClosedTabsData = lazy.SessionStore.getClosedTabData( + getWindow() + ); + if (Services.prefs.getBoolPref(INCLUDE_CLOSED_TABS_FROM_CLOSED_WINDOWS)) { + recentlyClosedTabsData.push( + ...lazy.SessionStore.getClosedTabDataFromClosedWindows() + ); + } + // sort the aggregated list to most-recently-closed first + recentlyClosedTabsData.sort((a, b) => a.closedAt < b.closedAt); + this.recentlyClosedTabs = recentlyClosedTabsData; + this.normalizeRecentlyClosedData(); + if (this.searchQuery) { + this.#updateSearchResults(); + } + this.requestUpdate(); + } + + normalizeRecentlyClosedData() { + // Normalize data for fxview-tabs-list + this.recentlyClosedTabs.forEach(recentlyClosedItem => { + const targetURI = this.getTabStateValue(recentlyClosedItem, "url"); + recentlyClosedItem.time = recentlyClosedItem.closedAt; + recentlyClosedItem.icon = recentlyClosedItem.image; + recentlyClosedItem.primaryL10nId = "fxviewtabrow-tabs-list-tab"; + recentlyClosedItem.primaryL10nArgs = JSON.stringify({ + targetURI: typeof targetURI === "string" ? targetURI : "", + }); + recentlyClosedItem.secondaryL10nId = + "firefoxview-closed-tabs-dismiss-tab"; + recentlyClosedItem.secondaryL10nArgs = JSON.stringify({ + tabTitle: recentlyClosedItem.title, + }); + recentlyClosedItem.url = targetURI; + }); + } + + onReopenTab(e) { + const closedId = parseInt(e.originalTarget.closedId, 10); + const sourceClosedId = parseInt(e.originalTarget.sourceClosedId, 10); + if (isNaN(sourceClosedId)) { + lazy.SessionStore.undoCloseById(closedId, getWindow()); + } else { + lazy.SessionStore.undoClosedTabFromClosedWindow( + { sourceClosedId }, + closedId, + getWindow() + ); + } + + // Record telemetry + let tabClosedAt = parseInt(e.originalTarget.time); + const position = + Array.from(this.tabList.rowEls).indexOf(e.originalTarget) + 1; + + let now = Date.now(); + let deltaSeconds = (now - tabClosedAt) / 1000; + Services.telemetry.recordEvent( + "firefoxview_next", + "recently_closed", + "tabs", + null, + { + position: position.toString(), + delta: deltaSeconds.toString(), + page: this.recentBrowsing ? "recentbrowsing" : "recentlyclosed", + } + ); + if (this.searchQuery) { + const searchesHistogram = Services.telemetry.getKeyedHistogramById( + "FIREFOX_VIEW_CUMULATIVE_SEARCHES" + ); + searchesHistogram.add( + this.recentBrowsing ? "recentbrowsing" : "recentlyclosed", + this.cumulativeSearches + ); + this.cumulativeSearches = 0; + } + } + + onDismissTab(e) { + const closedId = parseInt(e.originalTarget.closedId, 10); + const sourceClosedId = parseInt(e.originalTarget.sourceClosedId, 10); + const sourceWindowId = e.originalTarget.souceWindowId; + if (sourceWindowId || !isNaN(sourceClosedId)) { + lazy.SessionStore.forgetClosedTabById(closedId, { + sourceClosedId, + sourceWindowId, + }); + } else { + lazy.SessionStore.forgetClosedTabById(closedId); + } + + // Record telemetry + let tabClosedAt = parseInt(e.originalTarget.time); + const position = + Array.from(this.tabList.rowEls).indexOf(e.originalTarget) + 1; + + let now = Date.now(); + let deltaSeconds = (now - tabClosedAt) / 1000; + Services.telemetry.recordEvent( + "firefoxview_next", + "dismiss_closed_tab", + "tabs", + null, + { + position: position.toString(), + delta: deltaSeconds.toString(), + page: this.recentBrowsing ? "recentbrowsing" : "recentlyclosed", + } + ); + } + + willUpdate() { + this.fullyUpdated = false; + } + + updated() { + this.fullyUpdated = true; + this.toggleVisibilityInCardContainer(); + } + + async scheduleUpdate() { + // Only defer initial update + if (!this.firstUpdateComplete) { + await new Promise(resolve => setTimeout(resolve)); + } + super.scheduleUpdate(); + } + + emptyMessageTemplate() { + let descriptionHeader; + let descriptionLabels; + let descriptionLink; + if (Services.prefs.getBoolPref(NEVER_REMEMBER_HISTORY_PREF, false)) { + // History pref set to never remember history + descriptionHeader = "firefoxview-dont-remember-history-empty-header"; + descriptionLabels = [ + "firefoxview-dont-remember-history-empty-description", + "firefoxview-dont-remember-history-empty-description-two", + ]; + descriptionLink = { + url: "about:preferences#privacy", + name: "history-settings-url-two", + }; + } else { + descriptionHeader = "firefoxview-recentlyclosed-empty-header"; + descriptionLabels = [ + "firefoxview-recentlyclosed-empty-description", + "firefoxview-recentlyclosed-empty-description-two", + ]; + descriptionLink = { + url: "about:firefoxview#history", + name: "history-url", + sameTarget: "true", + }; + } + return html` + + + `; + } + + render() { + return html` + + ${when( + !this.recentBrowsing, + () => html`
+ + ${when( + isSearchEnabled(), + () => html`
+ +
` + )} +
` + )} +
+ +

+ ${when( + this.recentlyClosedTabs.length, + () => + html` + + ` + )} + ${when( + this.recentBrowsing && !this.recentlyClosedTabs.length, + () => html`
${this.emptyMessageTemplate()}
` + )} + ${when( + this.isShowAllLinkVisible(), + () => html`
` + )} +
+ ${when( + this.selectedTab && !this.recentlyClosedTabs.length, + () => html`
${this.emptyMessageTemplate()}
` + )} +
+ `; + } + + onSearchQuery(e) { + this.searchQuery = e.detail.query; + this.showAll = false; + this.cumulativeSearches = this.searchQuery + ? this.cumulativeSearches + 1 + : 0; + this.#updateSearchResults(); + } + + #updateSearchResults() { + this.searchResults = this.searchQuery + ? searchTabList(this.searchQuery, this.recentlyClosedTabs) + : null; + } + + isShowAllLinkVisible() { + return ( + this.recentBrowsing && + this.searchQuery && + this.searchResults.length > MAX_TABS_FOR_RECENT_BROWSING && + !this.showAll + ); + } + + enableShowAll(event) { + if ( + event.type == "click" || + (event.type == "keydown" && event.code == "Enter") || + (event.type == "keydown" && event.code == "Space") + ) { + event.preventDefault(); + this.showAll = true; + Services.telemetry.recordEvent( + "firefoxview_next", + "search_show_all", + "showallbutton", + null, + { + section: "recentlyclosed", + } + ); + } + } +} +customElements.define("view-recentlyclosed", RecentlyClosedTabsInView); -- cgit v1.2.3