opentabs-splitview.mjs (4574B)
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/. */ 4 5 import { html } from "chrome://global/content/vendor/lit.all.mjs"; 6 import { MozLitElement } from "chrome://global/content/lit-utils.mjs"; 7 8 const lazy = {}; 9 const BROWSER_NEW_TAB_URL = "about:newtab"; 10 const BROWSER_OPEN_TABS_URL = "about:opentabs"; 11 12 ChromeUtils.defineESModuleGetters(lazy, { 13 OpenTabsController: "resource:///modules/OpenTabsController.sys.mjs", 14 NonPrivateTabs: "resource:///modules/OpenTabs.sys.mjs", 15 getTabsTargetForWindow: "resource:///modules/OpenTabs.sys.mjs", 16 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", 17 }); 18 19 /** 20 * A collection of open, unpinned, unsplit tabs for the current by window. 21 */ 22 class OpenTabsInSplitView extends MozLitElement { 23 currentWindow = null; 24 openTabsTarget = null; 25 26 constructor() { 27 super(); 28 this.currentWindow = 29 this.ownerGlobal.top.browsingContext.embedderWindowGlobal.browsingContext.window; 30 if (lazy.PrivateBrowsingUtils.isWindowPrivate(this.currentWindow)) { 31 this.openTabsTarget = lazy.getTabsTargetForWindow(this.currentWindow); 32 } else { 33 this.openTabsTarget = lazy.NonPrivateTabs; 34 } 35 this.controller = new lazy.OpenTabsController(this, { 36 component: "splitview", 37 }); 38 this.listenersAdded = false; 39 } 40 41 static queries = { 42 sidebarTabList: "sidebar-tab-list", 43 }; 44 45 connectedCallback() { 46 super.connectedCallback(); 47 this.addListeners(true); 48 this.currentWindow.addEventListener("TabSelect", this); 49 } 50 51 disconnectedCallback() { 52 super.disconnectedCallback(); 53 this.removeListeners(); 54 this.currentWindow.removeEventListener("TabSelect", this); 55 } 56 57 addListeners(skipUpdate) { 58 if (!this.listenersAdded) { 59 this.openTabsTarget.addEventListener("TabChange", this); 60 if (!skipUpdate) { 61 this.requestUpdate(); 62 } 63 this.listenersAdded = true; 64 } 65 } 66 67 removeListeners() { 68 if (this.listenersAdded) { 69 this.openTabsTarget.removeEventListener("TabChange", this); 70 this.listenersAdded = false; 71 } 72 } 73 74 handleEvent(e) { 75 switch (e.type) { 76 case "TabChange": 77 this.requestUpdate(); 78 break; 79 case "TabSelect": 80 if (this.currentSplitView) { 81 this.addListeners(); 82 this.requestUpdate(); 83 } else { 84 this.removeListeners(); 85 } 86 break; 87 } 88 } 89 90 getWindow() { 91 return window.browsingContext.embedderWindowGlobal.browsingContext.window; 92 } 93 94 get currentSplitView() { 95 const { gBrowser } = this.getWindow(); 96 return gBrowser.selectedTab.splitview; 97 } 98 99 onTabListRowClick(event) { 100 const { gBrowser } = this.getWindow(); 101 const tab = event.originalTarget.tabElement; 102 if (this.currentSplitView) { 103 this.currentSplitView.replaceTab(gBrowser.selectedTab, tab); 104 } 105 } 106 107 get nonSplitViewUnpinnedTabs() { 108 const { gBrowser } = this.getWindow(); 109 return gBrowser.tabs.filter(tab => { 110 return ( 111 !tab.hidden && 112 !tab.pinned && 113 !tab.splitview && 114 tab?.linkedBrowser?.currentURI?.spec !== BROWSER_OPEN_TABS_URL 115 ); 116 }); 117 } 118 119 render() { 120 const { gBrowser } = this.getWindow(); 121 let tabs = this.nonSplitViewUnpinnedTabs; 122 if ( 123 !tabs.length || 124 (gBrowser.selectedTab.linkedBrowser.currentURI.spec === 125 BROWSER_OPEN_TABS_URL && 126 !this.currentSplitView) 127 ) { 128 // If there are no unpinned, unsplit tabs to display or about:opentabs 129 // is opened outside of a split view, open about:newtab instead 130 this.getWindow().openTrustedLinkIn(BROWSER_NEW_TAB_URL, "current"); 131 } 132 return html` 133 <link 134 rel="stylesheet" 135 href="chrome://browser/content/tabbrowser/opentabs-splitview.css" 136 /> 137 <link 138 rel="stylesheet" 139 href="chrome://browser/content/firefoxview/firefoxview.css" 140 /> 141 <moz-card> 142 <sidebar-tab-list 143 maxTabsLength="-1" 144 .tabItems=${this.controller.getTabListItems(tabs)} 145 @fxview-tab-list-primary-action=${this.onTabListRowClick} 146 > 147 </sidebar-tab-list> 148 </moz-card> 149 `; 150 } 151 } 152 customElements.define("splitview-opentabs", OpenTabsInSplitView); 153 154 window.addEventListener( 155 "unload", 156 () => { 157 // Clear out the document so the disconnectedCallback will trigger 158 // properly 159 document.body.textContent = ""; 160 }, 161 { once: true } 162 );