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 { html } from "chrome://global/content/vendor/lit.all.mjs";
6 import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
8 var { XPCOMUtils } = ChromeUtils.importESModule(
9 "resource://gre/modules/XPCOMUtils.sys.mjs"
12 const TAB_PREVIEW_USE_THUMBNAILS_PREF =
13 "browser.tabs.cardPreview.showThumbnails";
14 const TAB_PREVIEW_DELAY_PREF = "browser.tabs.cardPreview.delayMs";
17 * Detailed preview card that displays when hovering a tab
19 * @property {MozTabbrowserTab} tab - the tab to preview
20 * @fires TabPreview#previewhidden
21 * @fires TabPreview#previewshown
22 * @fires TabPreview#previewThumbnailUpdated
24 export default class TabPreview extends MozLitElement {
26 tab: { type: Object },
28 _previewIsActive: { type: Boolean, state: true },
29 _previewDelayTimeout: { type: Number, state: true },
30 _displayTitle: { type: String, state: true },
31 _displayURI: { type: String, state: true },
32 _displayImg: { type: Object, state: true },
37 XPCOMUtils.defineLazyPreferenceGetter(
40 TAB_PREVIEW_DELAY_PREF,
43 XPCOMUtils.defineLazyPreferenceGetter(
45 "_prefDisplayThumbnail",
46 TAB_PREVIEW_USE_THUMBNAILS_PREF,
51 // render this inside a <panel>
53 if (!document.createXULElement) {
55 "Unable to create panel: document.createXULElement is not available"
57 return super.createRenderRoot();
59 this.attachShadow({ mode: "open" });
60 this.panel = document.createXULElement("panel");
61 this.panel.setAttribute("id", "tabPreviewPanel");
62 this.panel.setAttribute("noautofocus", true);
63 this.panel.setAttribute("norolluponanchor", true);
64 this.panel.setAttribute("consumeoutsideclicks", "never");
65 this.panel.setAttribute("level", "parent");
66 this.panel.setAttribute("type", "arrow");
67 this.shadowRoot.append(this.panel);
71 get previewCanShow() {
72 return this._previewIsActive && this.tab;
75 get thumbnailCanShow() {
77 this.previewCanShow &&
78 this._prefDisplayThumbnail &&
86 const url = new URL(uri);
87 return `${url.hostname}${url.pathname}`.replace(/\/+$/, "");
103 case "popuphidden": {
104 this.previewHidden();
111 this.panel.openPopup(this.tab, {
112 position: "bottomleft topleft",
114 isContextMenu: false,
116 window.addEventListener("wheel", this, {
120 window.addEventListener("TabSelect", this);
121 this.panel.addEventListener("popuphidden", this);
125 this.panel.hidePopup();
126 this.updateComplete.then(() => {
128 * @event TabPreview#previewhidden
129 * @type {CustomEvent}
131 this.dispatchEvent(new CustomEvent("previewhidden"));
136 window.removeEventListener("wheel", this, { capture: true, passive: true });
137 window.removeEventListener("TabSelect", this);
138 this.panel.removeEventListener("popuphidden", this);
141 // compute values derived from tab element
142 willUpdate(changedProperties) {
143 if (!changedProperties.has("tab")) {
147 this._displayTitle = "";
148 this._displayURI = "";
149 this._displayImg = null;
152 this._displayTitle = this.tab.textLabel.textContent;
153 this._displayURI = this.getPrettyURI(
154 this.tab.linkedBrowser.currentURI.spec
156 this._displayImg = null;
158 window.tabPreviews.get(this.tab).then(el => {
159 if (this.tab == tab) {
160 this._displayImg = el;
165 updated(changedProperties) {
166 if (changedProperties.has("tab")) {
167 // handle preview delay
169 clearTimeout(this._previewDelayTimeout);
170 this._previewIsActive = false;
172 let lastTabVal = changedProperties.get("tab");
174 // tab was set from an empty state,
175 // so wait for the delay duration before showing
176 this._previewDelayTimeout = setTimeout(() => {
177 this._previewIsActive = true;
178 }, this._prefPreviewDelay);
182 if (changedProperties.has("_previewIsActive")) {
183 if (!this._previewIsActive) {
188 (changedProperties.has("tab") ||
189 changedProperties.has("_previewIsActive")) &&
192 this.updateComplete.then(() => {
193 if (this.panel.state == "open" || this.panel.state == "showing") {
194 this.panel.moveToAnchor(this.tab, "bottomleft topleft", 0, -2);
201 * @event TabPreview#previewshown
202 * @type {CustomEvent}
203 * @property {object} detail
204 * @property {MozTabbrowserTab} detail.tab - the tab being previewed
206 new CustomEvent("previewshown", {
207 detail: { tab: this.tab },
212 if (changedProperties.has("_displayImg")) {
213 this.updateComplete.then(() => {
215 * fires when the thumbnail for a preview is loaded
216 * and added to the document.
218 * @event TabPreview#previewThumbnailUpdated
219 * @type {CustomEvent}
221 this.dispatchEvent(new CustomEvent("previewThumbnailUpdated"));
231 href="chrome://browser/content/tabpreview/tabpreview.css"
233 <div class="tab-preview-container">
234 <div class="tab-preview-text-container">
235 <div class="tab-preview-title">${this._displayTitle}</div>
236 <div class="tab-preview-uri">${this._displayURI}</div>
238 ${this.thumbnailCanShow
240 <div class="tab-preview-thumbnail-container">
249 customElements.define("tab-preview", TabPreview);