feat: Animate glances comming from unknown sources, p=#10894, c=glance

* feat: Animate glances comming from unknown sources, b=no-bug, c=glance

* chore: Fixed linter, b=no-bug, c=glance
This commit is contained in:
mr. m
2025-10-21 12:16:32 +02:00
committed by GitHub
parent f52f913043
commit fbad844a08
3 changed files with 66 additions and 24 deletions

View File

@@ -24,6 +24,9 @@
#duringOpening = false;
#ignoreClose = false;
// Click handling
#lastLinkClickData = { clientX: 0, clientY: 0, height: 0, width: 0 };
// Arc animation configuration
#ARC_CONFIG = Object.freeze({
ARC_STEPS: 70, // Increased for smoother bounce
@@ -268,10 +271,31 @@
data.height
);
return await this.#imageBitmapToBase64(
await window.browsingContext.currentWindowGlobal.drawSnapshot(rect, 1, 'transparent', true)
await window.browsingContext.currentWindowGlobal.drawSnapshot(
rect,
1,
'transparent',
undefined
)
);
}
/**
* Set the last link click data
* @param {Object} data - The link click data
*/
set lastLinkClickData(data) {
this.#lastLinkClickData = data;
}
/**
* Get the last link click data
* @returns {Object} The last link click data
*/
get lastLinkClickData() {
return this.#lastLinkClickData;
}
/**
* Open a glance overlay with the specified data
* @param {Object} data - Glance data including URL, position, and dimensions
@@ -289,6 +313,13 @@
return;
}
if (!data.height || !data.width) {
data = {
...data,
...this.lastLinkClickData,
};
}
this.#setAnimationState(true);
const currentTab = ownerTab ?? gBrowser.selectedTab;
const browserElement = this.#createBrowserElement(data.url, currentTab, existingTab);
@@ -325,6 +356,9 @@
gZenViewSplitter.onLocationChange(browserElement);
this.#prepareGlanceAnimation(data, browserElement);
if (data.width && data.height) {
// It is guaranteed that we will animate this opacity later on
// when we start animating the glance.
this.contentWrapper.style.opacity = 0;
data.elementData = await this.#getElementPreviewData(data);
}
this.#glances.get(this.#currentGlanceID).elementData = data.elementData;
@@ -342,7 +376,6 @@
const newButtons = this.#createNewOverlayButtons();
this.browserWrapper.appendChild(newButtons);
this.#animateParentBackground();
this.#setupGlancePositioning(data);
this.#configureBrowserElement(browserElement);
}
@@ -486,7 +519,6 @@
// nice fade-in effect to the content. But if it doesn't exist,
// we just fall back to always showing the browser directly.
if (data.elementData) {
this.contentWrapper.style.opacity = 0;
gZenUIManager.motion
.animate(
this.contentWrapper,
@@ -501,6 +533,7 @@
});
}
this.#animateParentBackground();
gZenUIManager.motion
.animate(this.browserWrapper, arcSequence, {
duration: gZenUIManager.testingEnabled ? 0 : 0.4,
@@ -1355,22 +1388,9 @@
* @param {Tab} tab - The tab to open glance for
*/
#openGlanceForTab(tab) {
const browserRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabpanels);
const clickPosition = gZenUIManager._lastClickPosition || {
clientX: browserRect.width / 2,
clientY: browserRect.height / 2,
};
// Make it relative to the tabpanels
clickPosition.clientX -= browserRect.left;
clickPosition.clientY -= browserRect.top;
this.openGlance(
{
url: undefined,
...clickPosition,
width: 0,
height: 0,
},
tab,
tab.owner

View File

@@ -35,22 +35,30 @@ export class ZenGlanceChild extends JSWindowActorChild {
return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey);
}
openGlance(target, originalTarget) {
#openGlance(target) {
let url = target.href;
// Add domain to relative URLs
if (!url.match(/^(?:[a-z]+:)?\/\//i)) {
url = this.contentWindow.location.origin + url;
}
this.sendAsyncMessage('ZenGlance:OpenGlance', {
url,
});
}
#sendClickDataToParent(target, element) {
if (!element || !target) {
return;
}
// Get the largest element we can get. If the `A` element
// is a parent of the original target, use the anchor element,
// otherwise use the original target.
let rect = originalTarget.getBoundingClientRect();
let rect = element.getBoundingClientRect();
const anchorRect = target.getBoundingClientRect();
if (anchorRect.width * anchorRect.height > rect.width * rect.height) {
rect = anchorRect;
}
this.sendAsyncMessage('ZenGlance:OpenGlance', {
url,
this.sendAsyncMessage('ZenGlance:RecordLinkClickData', {
clientX: rect.left,
clientY: rect.top,
width: rect.width,
@@ -59,7 +67,19 @@ export class ZenGlanceChild extends JSWindowActorChild {
}
handleClick(event) {
if (this.ensureOnlyKeyModifiers(event) || event.button !== 0 || event.defaultPrevented) {
if (event.button !== 0 || event.defaultPrevented) {
return;
}
// get closest A element
const target = event.target.closest('A');
const elementToRecord = event.originalTarget || event.target;
// We record the link data anyway, even if the glance may be invoked
// or not. We have some cases where glance would open, for example,
// when clicking on a link with a different domain where glance would open.
// The problem is that at that stage we don't know the rect or even what
// element has been clicked, so we send the data here.
this.#sendClickDataToParent(target, elementToRecord);
if (this.ensureOnlyKeyModifiers(event)) {
return;
}
const activationMethod = this.#activationMethod;
@@ -72,13 +92,11 @@ export class ZenGlanceChild extends JSWindowActorChild {
} else if (activationMethod === 'meta' && !event.metaKey) {
return;
}
// get closest A element
const target = event.target.closest('A');
if (target) {
event.preventDefault();
event.stopPropagation();
this.openGlance(target, event.originalTarget || event.target);
this.#openGlance(target);
}
}

View File

@@ -23,6 +23,10 @@ export class ZenGlanceParent extends JSWindowActorParent {
this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(params);
break;
}
case 'ZenGlance:RecordLinkClickData': {
this.browsingContext.topChromeWindow.gZenGlanceManager.lastLinkClickData = message.data;
break;
}
default:
console.warn(`[glance]: Unknown message: ${message.name}`);
}