Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
var { XPCOMUtils } = ChromeUtils.importESModule(
);
const POPUP_OPTIONS = {
position: "bottomleft topleft",
x: 0,
y: -2,
};
/**
* Detailed preview card that displays when hovering a tab
*/
export default class TabPreviewPanel {
constructor(panel) {
this._panel = panel;
this._win = panel.ownerGlobal;
this._tab = null;
this._thumbnailElement = null;
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_prefDisableAutohide",
"ui.popup.disable_autohide",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_prefPreviewDelay",
"ui.tooltip.delay_ms"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_prefDisplayThumbnail",
"browser.tabs.cardPreview.showThumbnails",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_prefShowPidAndActiveness",
"browser.tabs.tooltipsShowPidAndActiveness",
false
);
this._timer = null;
}
getPrettyURI(uri) {
try {
const url = new URL(uri);
return `${url.hostname}`.replace(/^w{3}\./, "");
} catch {
return uri;
}
}
_needsThumbnailFor(tab) {
return !tab.selected;
}
_maybeRequestThumbnail() {
if (!this._prefDisplayThumbnail) {
return;
}
if (!this._needsThumbnailFor(this._tab)) {
return;
}
let tab = this._tab;
this._win.tabPreviews.get(tab).then(el => {
if (this._tab == tab && this._needsThumbnailFor(tab)) {
this._thumbnailElement = el;
this._updatePreview();
}
});
}
activate(tab) {
this._tab = tab;
this._thumbnailElement = null;
this._maybeRequestThumbnail();
if (this._panel.state == "open") {
this._updatePreview();
}
if (this._timer) {
return;
}
this._timer = this._win.setTimeout(() => {
this._timer = null;
this._panel.openPopup(this._tab, POPUP_OPTIONS);
}, this._prefPreviewDelay);
this._win.addEventListener("TabSelect", this);
this._panel.addEventListener("popupshowing", this);
}
deactivate(leavingTab = null) {
if (leavingTab) {
if (this._tab != leavingTab) {
return;
}
this._win.requestAnimationFrame(() => {
if (this._tab == leavingTab) {
this.deactivate();
}
});
return;
}
this._tab = null;
this._thumbnailElement = null;
this._panel.removeEventListener("popupshowing", this);
this._win.removeEventListener("TabSelect", this);
if (!this._prefDisableAutohide) {
this._panel.hidePopup();
}
if (this._timer) {
this._win.clearTimeout(this._timer);
this._timer = null;
}
}
handleEvent(e) {
switch (e.type) {
case "popupshowing":
this._updatePreview();
break;
case "TabSelect":
if (this._thumbnailElement && !this._needsThumbnailFor(this._tab)) {
this._thumbnailElement.remove();
this._thumbnailElement = null;
}
break;
}
}
_updatePreview() {
this._panel.querySelector(".tab-preview-title").textContent =
this._displayTitle;
this._panel.querySelector(".tab-preview-uri").textContent =
this._displayURI;
if (this._prefShowPidAndActiveness) {
this._panel.querySelector(".tab-preview-pid").textContent =
this._displayPids;
this._panel.querySelector(".tab-preview-activeness").textContent =
this._displayActiveness;
} else {
this._panel.querySelector(".tab-preview-pid").textContent = "";
this._panel.querySelector(".tab-preview-activeness").textContent = "";
}
let thumbnailContainer = this._panel.querySelector(
".tab-preview-thumbnail-container"
);
if (thumbnailContainer.firstChild != this._thumbnailElement) {
thumbnailContainer.replaceChildren();
if (this._thumbnailElement) {
thumbnailContainer.appendChild(this._thumbnailElement);
}
this._panel.dispatchEvent(
new CustomEvent("previewThumbnailUpdated", {
detail: {
thumbnail: this._thumbnailElement,
},
})
);
}
if (this._tab && this._panel.state == "open") {
this._panel.moveToAnchor(
this._tab,
POPUP_OPTIONS.position,
POPUP_OPTIONS.x,
POPUP_OPTIONS.y
);
}
}
get _displayTitle() {
if (!this._tab) {
return "";
}
return this._tab.textLabel.textContent;
}
get _displayURI() {
if (!this._tab) {
return "";
}
return this.getPrettyURI(this._tab.linkedBrowser.currentURI.spec);
}
get _displayPids() {
const pids = this._win.gBrowser.getTabPids(this._tab);
if (!pids.length) {
return "";
}
let pidLabel = pids.length > 1 ? "pids" : "pid";
return `${pidLabel}: ${pids.join(", ")}`;
}
get _displayActiveness() {
return this._tab.linkedBrowser.docShellIsActive ? "[A]" : "";
}
}