forked from mirrors/gecko-dev
This also fixes bug 1875551, while I'm here. Differential Revision: https://phabricator.services.mozilla.com/D199125
249 lines
6.7 KiB
JavaScript
249 lines
6.7 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 { html } from "chrome://global/content/vendor/lit.all.mjs";
|
|
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
|
|
|
var { XPCOMUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/XPCOMUtils.sys.mjs"
|
|
);
|
|
|
|
const TAB_PREVIEW_USE_THUMBNAILS_PREF =
|
|
"browser.tabs.cardPreview.showThumbnails";
|
|
const TAB_PREVIEW_DELAY_PREF = "browser.tabs.cardPreview.delayMs";
|
|
|
|
/**
|
|
* Detailed preview card that displays when hovering a tab
|
|
*
|
|
* @property {MozTabbrowserTab} tab - the tab to preview
|
|
* @fires TabPreview#previewhidden
|
|
* @fires TabPreview#previewshown
|
|
* @fires TabPreview#previewThumbnailUpdated
|
|
*/
|
|
export default class TabPreview extends MozLitElement {
|
|
static properties = {
|
|
tab: { type: Object },
|
|
|
|
_previewIsActive: { type: Boolean, state: true },
|
|
_previewDelayTimeout: { type: Number, state: true },
|
|
_displayTitle: { type: String, state: true },
|
|
_displayURI: { type: String, state: true },
|
|
_displayImg: { type: Object, state: true },
|
|
};
|
|
|
|
constructor() {
|
|
super();
|
|
XPCOMUtils.defineLazyPreferenceGetter(
|
|
this,
|
|
"_prefPreviewDelay",
|
|
TAB_PREVIEW_DELAY_PREF,
|
|
1000
|
|
);
|
|
XPCOMUtils.defineLazyPreferenceGetter(
|
|
this,
|
|
"_prefDisplayThumbnail",
|
|
TAB_PREVIEW_USE_THUMBNAILS_PREF,
|
|
false
|
|
);
|
|
}
|
|
|
|
// render this inside a <panel>
|
|
createRenderRoot() {
|
|
if (!document.createXULElement) {
|
|
console.error(
|
|
"Unable to create panel: document.createXULElement is not available"
|
|
);
|
|
return super.createRenderRoot();
|
|
}
|
|
this.attachShadow({ mode: "open" });
|
|
this.panel = document.createXULElement("panel");
|
|
this.panel.setAttribute("id", "tabPreviewPanel");
|
|
this.panel.setAttribute("noautofocus", true);
|
|
this.panel.setAttribute("norolluponanchor", true);
|
|
this.panel.setAttribute("consumeoutsideclicks", "never");
|
|
this.panel.setAttribute("level", "parent");
|
|
this.panel.setAttribute("type", "arrow");
|
|
this.shadowRoot.append(this.panel);
|
|
return this.panel;
|
|
}
|
|
|
|
get previewCanShow() {
|
|
return this._previewIsActive && this.tab;
|
|
}
|
|
|
|
get thumbnailCanShow() {
|
|
return (
|
|
this.previewCanShow &&
|
|
this._prefDisplayThumbnail &&
|
|
!this.tab.selected &&
|
|
this._displayImg
|
|
);
|
|
}
|
|
|
|
getPrettyURI(uri) {
|
|
try {
|
|
const url = new URL(uri);
|
|
return `${url.hostname}${url.pathname}`.replace(/\/+$/, "");
|
|
} catch {
|
|
return this.pageURI;
|
|
}
|
|
}
|
|
|
|
handleEvent(e) {
|
|
switch (e.type) {
|
|
case "TabSelect": {
|
|
this.requestUpdate();
|
|
break;
|
|
}
|
|
case "wheel": {
|
|
this.hidePreview();
|
|
break;
|
|
}
|
|
case "popuphidden": {
|
|
this.previewHidden();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
showPreview() {
|
|
this.panel.openPopup(this.tab, {
|
|
position: "bottomleft topleft",
|
|
y: -2,
|
|
isContextMenu: false,
|
|
});
|
|
window.addEventListener("wheel", this, {
|
|
capture: true,
|
|
passive: true,
|
|
});
|
|
window.addEventListener("TabSelect", this);
|
|
this.panel.addEventListener("popuphidden", this);
|
|
}
|
|
|
|
hidePreview() {
|
|
this.panel.hidePopup();
|
|
this.updateComplete.then(() => {
|
|
/**
|
|
* @event TabPreview#previewhidden
|
|
* @type {CustomEvent}
|
|
*/
|
|
this.dispatchEvent(new CustomEvent("previewhidden"));
|
|
});
|
|
}
|
|
|
|
previewHidden() {
|
|
window.removeEventListener("wheel", this, { capture: true, passive: true });
|
|
window.removeEventListener("TabSelect", this);
|
|
this.panel.removeEventListener("popuphidden", this);
|
|
}
|
|
|
|
// compute values derived from tab element
|
|
willUpdate(changedProperties) {
|
|
if (!changedProperties.has("tab")) {
|
|
return;
|
|
}
|
|
if (!this.tab) {
|
|
this._displayTitle = "";
|
|
this._displayURI = "";
|
|
this._displayImg = null;
|
|
return;
|
|
}
|
|
this._displayTitle = this.tab.textLabel.textContent;
|
|
this._displayURI = this.getPrettyURI(
|
|
this.tab.linkedBrowser.currentURI.spec
|
|
);
|
|
this._displayImg = null;
|
|
let { tab } = this;
|
|
window.tabPreviews.get(this.tab).then(el => {
|
|
if (this.tab == tab) {
|
|
this._displayImg = el;
|
|
}
|
|
});
|
|
}
|
|
|
|
updated(changedProperties) {
|
|
if (changedProperties.has("tab")) {
|
|
// handle preview delay
|
|
if (!this.tab) {
|
|
clearTimeout(this._previewDelayTimeout);
|
|
this._previewIsActive = false;
|
|
} else {
|
|
let lastTabVal = changedProperties.get("tab");
|
|
if (!lastTabVal) {
|
|
// tab was set from an empty state,
|
|
// so wait for the delay duration before showing
|
|
this._previewDelayTimeout = setTimeout(() => {
|
|
this._previewIsActive = true;
|
|
}, this._prefPreviewDelay);
|
|
}
|
|
}
|
|
}
|
|
if (changedProperties.has("_previewIsActive")) {
|
|
if (!this._previewIsActive) {
|
|
this.hidePreview();
|
|
}
|
|
}
|
|
if (
|
|
(changedProperties.has("tab") ||
|
|
changedProperties.has("_previewIsActive")) &&
|
|
this.previewCanShow
|
|
) {
|
|
this.updateComplete.then(() => {
|
|
if (this.panel.state == "open" || this.panel.state == "showing") {
|
|
this.panel.moveToAnchor(this.tab, "bottomleft topleft", 0, -2);
|
|
} else {
|
|
this.showPreview();
|
|
}
|
|
|
|
this.dispatchEvent(
|
|
/**
|
|
* @event TabPreview#previewshown
|
|
* @type {CustomEvent}
|
|
* @property {object} detail
|
|
* @property {MozTabbrowserTab} detail.tab - the tab being previewed
|
|
*/
|
|
new CustomEvent("previewshown", {
|
|
detail: { tab: this.tab },
|
|
})
|
|
);
|
|
});
|
|
}
|
|
if (changedProperties.has("_displayImg")) {
|
|
this.updateComplete.then(() => {
|
|
/**
|
|
* fires when the thumbnail for a preview is loaded
|
|
* and added to the document.
|
|
*
|
|
* @event TabPreview#previewThumbnailUpdated
|
|
* @type {CustomEvent}
|
|
*/
|
|
this.dispatchEvent(new CustomEvent("previewThumbnailUpdated"));
|
|
});
|
|
}
|
|
}
|
|
|
|
render() {
|
|
return html`
|
|
<link
|
|
rel="stylesheet"
|
|
type="text/css"
|
|
href="chrome://browser/content/tabpreview/tabpreview.css"
|
|
/>
|
|
<div class="tab-preview-container">
|
|
<div class="tab-preview-text-container">
|
|
<div class="tab-preview-title">${this._displayTitle}</div>
|
|
<div class="tab-preview-uri">${this._displayURI}</div>
|
|
</div>
|
|
${this.thumbnailCanShow
|
|
? html`
|
|
<div class="tab-preview-thumbnail-container">
|
|
${this._displayImg}
|
|
</div>
|
|
`
|
|
: ""}
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
customElements.define("tab-preview", TabPreview);
|