/* 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 { html } from "chrome://global/content/vendor/lit.all.mjs";
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
const lazy = {};
const BROWSER_NEW_TAB_URL = "about:newtab";
const BROWSER_OPEN_TABS_URL = "about:opentabs";
ChromeUtils.defineESModuleGetters(lazy, {
OpenTabsController: "resource:///modules/OpenTabsController.sys.mjs",
NonPrivateTabs: "resource:///modules/OpenTabs.sys.mjs",
getTabsTargetForWindow: "resource:///modules/OpenTabs.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
});
/**
* A collection of open, unpinned, unsplit tabs for the current by window.
*/
class OpenTabsInSplitView extends MozLitElement {
currentWindow = null;
openTabsTarget = null;
constructor() {
super();
this.currentWindow =
this.ownerGlobal.top.browsingContext.embedderWindowGlobal.browsingContext.window;
if (lazy.PrivateBrowsingUtils.isWindowPrivate(this.currentWindow)) {
this.openTabsTarget = lazy.getTabsTargetForWindow(this.currentWindow);
} else {
this.openTabsTarget = lazy.NonPrivateTabs;
}
this.controller = new lazy.OpenTabsController(this, {
component: "splitview",
});
this.listenersAdded = false;
}
static queries = {
sidebarTabList: "sidebar-tab-list",
};
connectedCallback() {
super.connectedCallback();
this.addListeners(true);
this.currentWindow.addEventListener("TabSelect", this);
}
disconnectedCallback() {
super.disconnectedCallback();
this.removeListeners();
this.currentWindow.removeEventListener("TabSelect", this);
}
addListeners(skipUpdate) {
if (!this.listenersAdded) {
this.openTabsTarget.addEventListener("TabChange", this);
if (!skipUpdate) {
this.requestUpdate();
}
this.listenersAdded = true;
}
}
removeListeners() {
if (this.listenersAdded) {
this.openTabsTarget.removeEventListener("TabChange", this);
this.listenersAdded = false;
}
}
handleEvent(e) {
switch (e.type) {
case "TabChange":
this.requestUpdate();
break;
case "TabSelect":
if (this.currentSplitView) {
this.addListeners();
} else {
this.removeListeners();
}
break;
}
}
getWindow() {
return window.browsingContext.embedderWindowGlobal.browsingContext.window;
}
get currentSplitView() {
const { gBrowser } = this.getWindow();
return gBrowser.selectedTab.splitview;
}
onTabListRowClick(event) {
const { gBrowser } = this.getWindow();
const tab = event.originalTarget.tabElement;
if (this.currentSplitView) {
this.currentSplitView.replaceTab(gBrowser.selectedTab, tab);
}
}
get nonSplitViewUnpinnedTabs() {
const { gBrowser } = this.getWindow();
return gBrowser.tabs.filter(tab => {
return (
!tab.hidden &&
!tab.pinned &&
!tab.splitview &&
tab?.linkedBrowser?.currentURI?.spec !== BROWSER_OPEN_TABS_URL
);
});
}
render() {
const { gBrowser } = this.getWindow();
let tabs = this.nonSplitViewUnpinnedTabs;
if (
!tabs.length ||
(gBrowser.selectedTab.linkedBrowser.currentURI.spec ===
BROWSER_OPEN_TABS_URL &&
!this.currentSplitView)
) {
// If there are no unpinned, unsplit tabs to display or about:opentabs
// is opened outside of a split view, open about:newtab instead
this.getWindow().openTrustedLinkIn(BROWSER_NEW_TAB_URL, "current");
}
return html`
`;
}
}
customElements.define("splitview-opentabs", OpenTabsInSplitView);
window.addEventListener(
"unload",
() => {
// Clear out the document so the disconnectedCallback will trigger
// properly
document.body.textContent = "";
},
{ once: true }
);