Files
tubestation/browser/components/tabpreview/tabpreview.mjs
Dave Townsend dda3e121a8 Bug 1875831: Don't use arrow positioning for the tab preview panel. r=tabbrowser-reviewers,dao
Something about the arrow positioning logic causes background windows to raise.
The tab preview was only made an arrow panel because other panels consume wheel
events[1] meaning we couldn't detect them to close the panel but it turns out there
is a dedicated `rolluponmousewheel` attribute that allows us to automatically
close the panel anyway so this removes the dedicated wheel handling and adds
the correct attribute.

The original test added for closing on wheel events wasn't actually working as
it wasn't actually dispatching any wheel events, the panel was closing for other
reasons. This switches to sending native mouse wheel events.

[1] https://searchfox.org/mozilla-central/rev/098f910d0593b12a42089dd8f40dcd19d1121430/layout/xul/nsXULPopupManager.cpp#546-557

Differential Revision: https://phabricator.services.mozilla.com/D202709
2024-02-27 13:38:50 +00:00

238 lines
6.4 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";
/**
* 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",
"ui.tooltip.delay_ms"
);
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("rolluponmousewheel", "true");
this.panel.setAttribute("level", "parent");
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 "popuphidden": {
this.previewHidden();
break;
}
}
}
showPreview() {
this.panel.openPopup(this.tab, {
position: "bottomleft topleft",
y: -2,
isContextMenu: false,
});
window.addEventListener("TabSelect", this);
this.panel.addEventListener("popuphidden", this);
}
hidePreview() {
this.panel.hidePopup();
}
previewHidden() {
window.removeEventListener("TabSelect", this);
this.panel.removeEventListener("popuphidden", this);
/**
* @event TabPreview#previewhidden
* @type {CustomEvent}
*/
this.dispatchEvent(new CustomEvent("previewhidden"));
}
// 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);