forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			259 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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 { MozLitElement } from "chrome://global/content/lit-utils.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-empty-state.mjs";
 | |
| // eslint-disable-next-line import/no-unassigned-import
 | |
| import "chrome://browser/content/firefoxview/fxview-search-textbox.mjs";
 | |
| // eslint-disable-next-line import/no-unassigned-import
 | |
| import "chrome://browser/content/firefoxview/fxview-tab-list.mjs";
 | |
| 
 | |
| import { placeLinkOnClipboard } from "./helpers.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
 | |
| });
 | |
| 
 | |
| const WIN_RESIZE_DEBOUNCE_RATE_MS = 500;
 | |
| const WIN_RESIZE_DEBOUNCE_TIMEOUT_MS = 1000;
 | |
| 
 | |
| /**
 | |
|  * A base class for content container views displayed on firefox-view.
 | |
|  *
 | |
|  * @property {boolean} recentBrowsing
 | |
|  *   Is part of the recentbrowsing page view
 | |
|  * @property {boolean} paused
 | |
|  *   No content will be updated and rendered while paused
 | |
|  */
 | |
| export class ViewPageContent extends MozLitElement {
 | |
|   static get properties() {
 | |
|     return {
 | |
|       recentBrowsing: { type: Boolean },
 | |
|       paused: { type: Boolean },
 | |
|     };
 | |
|   }
 | |
|   constructor() {
 | |
|     super();
 | |
|     // don't update or render until explicitly un-paused
 | |
|     this.paused = true;
 | |
|   }
 | |
| 
 | |
|   get ownerViewPage() {
 | |
|     return this.closest("[type='page']") || this;
 | |
|   }
 | |
| 
 | |
|   get isVisible() {
 | |
|     if (!this.isConnected || this.ownerDocument.visibilityState != "visible") {
 | |
|       return false;
 | |
|     }
 | |
|     return this.ownerViewPage.selectedTab;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Override this function to run a callback whenever this content is visible.
 | |
|    */
 | |
|   viewVisibleCallback() {}
 | |
| 
 | |
|   /**
 | |
|    * Override this function to run a callback whenever this content is hidden.
 | |
|    */
 | |
|   viewHiddenCallback() {}
 | |
| 
 | |
|   getWindow() {
 | |
|     return window.browsingContext.embedderWindowGlobal.browsingContext.window;
 | |
|   }
 | |
| 
 | |
|   get isSelectedBrowserTab() {
 | |
|     const { gBrowser } = this.getWindow();
 | |
|     return gBrowser.selectedBrowser.browsingContext == window.browsingContext;
 | |
|   }
 | |
| 
 | |
|   copyLink(e) {
 | |
|     placeLinkOnClipboard(this.triggerNode.title, this.triggerNode.url);
 | |
|     this.recordContextMenuTelemetry("copy-link", e);
 | |
|   }
 | |
| 
 | |
|   openInNewWindow(e) {
 | |
|     this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
 | |
|       private: false,
 | |
|     });
 | |
|     this.recordContextMenuTelemetry("open-in-new-window", e);
 | |
|   }
 | |
| 
 | |
|   openInNewPrivateWindow(e) {
 | |
|     this.getWindow().openTrustedLinkIn(this.triggerNode.url, "window", {
 | |
|       private: true,
 | |
|     });
 | |
|     this.recordContextMenuTelemetry("open-in-private-window", e);
 | |
|   }
 | |
| 
 | |
|   recordContextMenuTelemetry(menuAction, event) {
 | |
|     Services.telemetry.recordEvent(
 | |
|       "firefoxview_next",
 | |
|       "context_menu",
 | |
|       "tabs",
 | |
|       null,
 | |
|       {
 | |
|         menu_action: menuAction,
 | |
|         data_type: event.target.panel.dataset.tabType,
 | |
|       }
 | |
|     );
 | |
|   }
 | |
| 
 | |
|   shouldUpdate(changedProperties) {
 | |
|     return !this.paused && super.shouldUpdate(changedProperties);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * A "page" in firefox view, which may be hidden or shown by the named-deck container or
 | |
|  * via the owner document's visibility
 | |
|  *
 | |
|  * @property {boolean} selectedTab
 | |
|  *   Is this page the selected view in the named-deck container
 | |
|  */
 | |
| export class ViewPage extends ViewPageContent {
 | |
|   static get properties() {
 | |
|     return {
 | |
|       selectedTab: { type: Boolean },
 | |
|       searchTextboxSize: { type: Number },
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   constructor() {
 | |
|     super();
 | |
|     this.selectedTab = false;
 | |
|     this.recentBrowsing = Boolean(this.recentBrowsingElement);
 | |
|     this.onTabSelect = this.onTabSelect.bind(this);
 | |
|     this.onResize = this.onResize.bind(this);
 | |
|   }
 | |
| 
 | |
|   get recentBrowsingElement() {
 | |
|     return this.closest("VIEW-RECENTBROWSING");
 | |
|   }
 | |
| 
 | |
|   onResize() {
 | |
|     this.windowResizeTask = new lazy.DeferredTask(
 | |
|       () => this.updateAllVirtualLists(),
 | |
|       WIN_RESIZE_DEBOUNCE_RATE_MS,
 | |
|       WIN_RESIZE_DEBOUNCE_TIMEOUT_MS
 | |
|     );
 | |
|     this.windowResizeTask?.arm();
 | |
|   }
 | |
| 
 | |
|   onTabSelect({ target }) {
 | |
|     const win = target.ownerGlobal;
 | |
| 
 | |
|     let selfBrowser = window.docShell?.chromeEventHandler;
 | |
|     const { gBrowser } = this.getWindow();
 | |
|     let isForegroundTab = gBrowser.selectedBrowser == selfBrowser;
 | |
| 
 | |
|     if (win.FirefoxViewHandler.tab?.selected && isForegroundTab) {
 | |
|       this.paused = false;
 | |
|       this.viewVisibleCallback();
 | |
|     } else {
 | |
|       this.paused = true;
 | |
|       this.viewHiddenCallback();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   connectedCallback() {
 | |
|     super.connectedCallback();
 | |
|   }
 | |
| 
 | |
|   disconnectedCallback() {
 | |
|     super.disconnectedCallback();
 | |
|     this.getWindow().removeEventListener("resize", this.onResize);
 | |
|     this.getWindow().removeEventListener("TabSelect", this.onTabSelect);
 | |
|   }
 | |
| 
 | |
|   updateAllVirtualLists() {
 | |
|     if (!this.paused) {
 | |
|       let tabLists = [];
 | |
|       if (this.recentBrowsing) {
 | |
|         let viewComponents = this.querySelectorAll("[slot]");
 | |
|         viewComponents.forEach(viewComponent => {
 | |
|           let currentTabLists = [];
 | |
|           if (viewComponent.nodeName.includes("OPENTABS")) {
 | |
|             viewComponent.viewCards.forEach(viewCard => {
 | |
|               currentTabLists.push(viewCard.tabList);
 | |
|             });
 | |
|           } else {
 | |
|             currentTabLists =
 | |
|               viewComponent.shadowRoot.querySelectorAll("fxview-tab-list");
 | |
|           }
 | |
|           tabLists.push(...currentTabLists);
 | |
|         });
 | |
|       } else {
 | |
|         tabLists = this.shadowRoot.querySelectorAll("fxview-tab-list");
 | |
|       }
 | |
|       tabLists.forEach(tabList => {
 | |
|         if (!tabList.updatesPaused && tabList.rootVirtualListEl?.isVisible) {
 | |
|           tabList.rootVirtualListEl.recalculateAfterWindowResize();
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   toggleVisibilityInCardContainer(isOpenTabs) {
 | |
|     let cards = [];
 | |
|     let tabLists = [];
 | |
|     if (!isOpenTabs) {
 | |
|       cards = this.shadowRoot.querySelectorAll("card-container");
 | |
|       tabLists = this.shadowRoot.querySelectorAll("fxview-tab-list");
 | |
|     } else {
 | |
|       this.viewCards.forEach(viewCard => {
 | |
|         if (viewCard.cardEl) {
 | |
|           cards.push(viewCard.cardEl);
 | |
|           tabLists.push(viewCard.tabList);
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|     if (tabLists.length && cards.length) {
 | |
|       cards.forEach(cardEl => {
 | |
|         if (cardEl.visible !== !this.paused) {
 | |
|           cardEl.visible = !this.paused;
 | |
|         } else if (
 | |
|           cardEl.isExpanded &&
 | |
|           Array.from(tabLists).some(
 | |
|             tabList => tabList.updatesPaused !== this.paused
 | |
|           )
 | |
|         ) {
 | |
|           // If card is already visible and expanded but tab-list has updatesPaused,
 | |
|           // update the tab-list updatesPaused prop from here instead of card-container
 | |
|           tabLists.forEach(tabList => {
 | |
|             tabList.updatesPaused = this.paused;
 | |
|           });
 | |
|         }
 | |
|       });
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   enter() {
 | |
|     this.selectedTab = true;
 | |
|     if (this.isVisible) {
 | |
|       this.paused = false;
 | |
|       this.viewVisibleCallback();
 | |
|       this.getWindow().addEventListener("resize", this.onResize);
 | |
|       this.getWindow().addEventListener("TabSelect", this.onTabSelect);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   exit() {
 | |
|     this.selectedTab = false;
 | |
|     this.paused = true;
 | |
|     this.viewHiddenCallback();
 | |
|     if (!this.windowResizeTask?.isFinalized) {
 | |
|       this.windowResizeTask?.finalize();
 | |
|     }
 | |
|     this.getWindow().removeEventListener("resize", this.onResize);
 | |
|     this.getWindow().removeEventListener("TabSelect", this.onTabSelect);
 | |
|   }
 | |
| }
 | 
