diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /browser/components/tabpreview | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/components/tabpreview')
-rw-r--r-- | browser/components/tabpreview/jar.mn | 7 | ||||
-rw-r--r-- | browser/components/tabpreview/moz.build | 10 | ||||
-rw-r--r-- | browser/components/tabpreview/tabpreview.css | 63 | ||||
-rw-r--r-- | browser/components/tabpreview/tabpreview.mjs | 249 |
4 files changed, 329 insertions, 0 deletions
diff --git a/browser/components/tabpreview/jar.mn b/browser/components/tabpreview/jar.mn new file mode 100644 index 0000000000..8ff09ebb17 --- /dev/null +++ b/browser/components/tabpreview/jar.mn @@ -0,0 +1,7 @@ +# 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/. + +browser.jar: + content/browser/tabpreview/tabpreview.mjs (tabpreview.mjs) + content/browser/tabpreview/tabpreview.css (tabpreview.css) diff --git a/browser/components/tabpreview/moz.build b/browser/components/tabpreview/moz.build new file mode 100644 index 0000000000..f8c362efe1 --- /dev/null +++ b/browser/components/tabpreview/moz.build @@ -0,0 +1,10 @@ +# 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/. + +JAR_MANIFESTS += ["jar.mn"] + +with Files("**"): + BUG_COMPONENT = ("Firefox", "Tabbed Browser") + +SPHINX_TREES["docs"] = "docs" diff --git a/browser/components/tabpreview/tabpreview.css b/browser/components/tabpreview/tabpreview.css new file mode 100644 index 0000000000..8b288cb95d --- /dev/null +++ b/browser/components/tabpreview/tabpreview.css @@ -0,0 +1,63 @@ +/* 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/. */ + +.tab-preview-container { + background-color: #ffffff; + color: #15141a; + border-radius: 9px; + display: inline-block; + width: 280px; + overflow: hidden; + line-height: 1.5; +} + +.tab-preview-title { + max-height: 3em; + overflow: hidden; + font-weight: 600; +} + +.tab-preview-uri { + color: #4a4b49; + max-height: 1.5em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.tab-preview-text-container { + padding: 8px; +} + +.tab-preview-thumbnail-container img, +.tab-preview-thumbnail-container canvas { + display: block; + width: 100%; +} + +@media (prefers-color-scheme: dark) { + .tab-preview-container { + background-color: #42414d; + color: #fbfbfe; + } + .tab-preview-uri { + color: #cfcfd8; + } +} + +@media (prefers-contrast) { + .tab-preview-container { + background-color: Canvas; + color: CanvasText; + } + .tab-preview-uri { + color: CanvasText; + } +} + +@media (max-width: 640px) { + .tab-preview-thumbnail-container { + display: none; + } +} diff --git a/browser/components/tabpreview/tabpreview.mjs b/browser/components/tabpreview/tabpreview.mjs new file mode 100644 index 0000000000..5256ab22ff --- /dev/null +++ b/browser/components/tabpreview/tabpreview.mjs @@ -0,0 +1,249 @@ +/* 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); |