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/. */
5 import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
7 // eslint-disable-next-line import/no-unassigned-import
8 import "chrome://browser/content/firefoxview/card-container.mjs";
9 // eslint-disable-next-line import/no-unassigned-import
10 import "chrome://browser/content/firefoxview/fxview-empty-state.mjs";
11 // eslint-disable-next-line import/no-unassigned-import
12 import "chrome://browser/content/firefoxview/fxview-search-textbox.mjs";
13 // eslint-disable-next-line import/no-unassigned-import
14 import "chrome://browser/content/firefoxview/fxview-tab-list.mjs";
16 import { placeLinkOnClipboard } from "./helpers.mjs";
20 ChromeUtils.defineESModuleGetters(lazy, {
21 DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
24 const WIN_RESIZE_DEBOUNCE_RATE_MS = 500;
25 const WIN_RESIZE_DEBOUNCE_TIMEOUT_MS = 1000;
28 * A base class for content container views displayed on firefox-view.
30 * @property {boolean} recentBrowsing
31 * Is part of the recentbrowsing page view
32 * @property {boolean} paused
33 * No content will be updated and rendered while paused
35 export class ViewPageContent extends MozLitElement {
36 static get properties() {
38 recentBrowsing: { type: Boolean },
39 paused: { type: Boolean },
44 // don't update or render until explicitly un-paused
49 return this.closest("[type='page']") || this;
53 if (!this.isConnected || this.ownerDocument.visibilityState != "visible") {
56 return this.ownerViewPage.selectedTab;
60 * Override this function to run a callback whenever this content is visible.
62 viewVisibleCallback() {}
65 * Override this function to run a callback whenever this content is hidden.
67 viewHiddenCallback() {}
70 return window.browsingContext.embedderWindowGlobal.browsingContext.window;
73 get isSelectedBrowserTab() {
74 const { gBrowser } = this.getWindow();
75 return gBrowser.selectedBrowser.browsingContext == window.browsingContext;
79 placeLinkOnClipboard(this.triggerNode.title, this.triggerNode.url);
80 this.recordContextMenuTelemetry("copy-link", e);
84 this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
87 this.recordContextMenuTelemetry("open-in-new-window", e);
90 openInNewPrivateWindow(e) {
91 this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
94 this.recordContextMenuTelemetry("open-in-private-window", e);
97 recordContextMenuTelemetry(menuAction, event) {
98 Glean.firefoxviewNext.contextMenuTabs.record({
99 menu_action: menuAction,
100 data_type: event.target.panel.dataset.tabType,
104 shouldUpdate(changedProperties) {
105 return !this.paused && super.shouldUpdate(changedProperties);
110 * A "page" in firefox view, which may be hidden or shown by the named-deck container or
111 * via the owner document's visibility
113 * @property {boolean} selectedTab
114 * Is this page the selected view in the named-deck container
116 export class ViewPage extends ViewPageContent {
117 static get properties() {
119 selectedTab: { type: Boolean },
120 searchTextboxSize: { type: Number },
126 this.selectedTab = false;
127 this.recentBrowsing = Boolean(this.recentBrowsingElement);
128 this.onTabSelect = this.onTabSelect.bind(this);
129 this.onResize = this.onResize.bind(this);
132 get recentBrowsingElement() {
133 return this.closest("VIEW-RECENTBROWSING");
137 this.windowResizeTask = new lazy.DeferredTask(
138 () => this.updateAllVirtualLists(),
139 WIN_RESIZE_DEBOUNCE_RATE_MS,
140 WIN_RESIZE_DEBOUNCE_TIMEOUT_MS
142 this.windowResizeTask?.arm();
145 onTabSelect({ target }) {
146 const win = target.ownerGlobal;
148 let selfBrowser = window.docShell?.chromeEventHandler;
149 const { gBrowser } = this.getWindow();
150 let isForegroundTab = gBrowser.selectedBrowser == selfBrowser;
152 if (win.FirefoxViewHandler.tab?.selected && isForegroundTab) {
154 this.viewVisibleCallback();
157 this.viewHiddenCallback();
161 connectedCallback() {
162 super.connectedCallback();
165 disconnectedCallback() {
166 super.disconnectedCallback();
167 this.getWindow().removeEventListener("resize", this.onResize);
168 this.getWindow().removeEventListener("TabSelect", this.onTabSelect);
171 updateAllVirtualLists() {
174 if (this.recentBrowsing) {
175 let viewComponents = this.querySelectorAll("[slot]");
176 viewComponents.forEach(viewComponent => {
177 let currentTabLists = [];
178 if (viewComponent.nodeName.includes("OPENTABS")) {
179 viewComponent.viewCards.forEach(viewCard => {
180 currentTabLists.push(viewCard.tabList);
184 viewComponent.shadowRoot.querySelectorAll("fxview-tab-list");
186 tabLists.push(...currentTabLists);
189 tabLists = this.shadowRoot.querySelectorAll("fxview-tab-list");
191 tabLists.forEach(tabList => {
192 if (!tabList.updatesPaused && tabList.rootVirtualListEl?.isVisible) {
193 tabList.rootVirtualListEl.recalculateAfterWindowResize();
199 toggleVisibilityInCardContainer(isOpenTabs) {
203 cards = this.shadowRoot.querySelectorAll("card-container");
204 tabLists = this.shadowRoot.querySelectorAll(
205 "fxview-tab-list, syncedtabs-tab-list"
208 this.viewCards.forEach(viewCard => {
209 if (viewCard.cardEl) {
210 cards.push(viewCard.cardEl);
211 tabLists.push(viewCard.tabList);
215 if (tabLists.length && cards.length) {
216 cards.forEach(cardEl => {
217 if (cardEl.visible !== !this.paused) {
218 cardEl.visible = !this.paused;
221 Array.from(tabLists).some(
222 tabList => tabList.updatesPaused !== this.paused
225 // If card is already visible and expanded but tab-list has updatesPaused,
226 // update the tab-list updatesPaused prop from here instead of card-container
227 tabLists.forEach(tabList => {
228 tabList.updatesPaused = this.paused;
236 this.selectedTab = true;
237 if (this.isVisible) {
239 this.viewVisibleCallback();
240 this.getWindow().addEventListener("resize", this.onResize);
241 this.getWindow().addEventListener("TabSelect", this.onTabSelect);
246 this.selectedTab = false;
248 this.viewHiddenCallback();
249 if (!this.windowResizeTask?.isFinalized) {
250 this.windowResizeTask?.finalize();
252 this.getWindow().removeEventListener("resize", this.onResize);
253 this.getWindow().removeEventListener("TabSelect", this.onTabSelect);