Bug 1815769 - Add optional anchor lookup to PopupNotifications - r=Gijs,adw

Differential Revision: https://phabricator.services.mozilla.com/D169814
This commit is contained in:
James Teow 2023-03-14 14:29:58 +00:00
parent 9234f5fbb8
commit 4feaf446ef
3 changed files with 47 additions and 35 deletions

View File

@ -439,11 +439,28 @@ XPCOMUtils.defineLazyGetter(this, "PopupNotifications", () => {
);
};
// Before a Popup is shown, check that its anchor is visible.
// If the anchor is not visible, use one of the fallbacks.
// If no fallbacks are visible, return null.
const getVisibleAnchorElement = anchorElement => {
// If the anchor element is present in the Urlbar,
// ensure that both the anchor and page URL are visible.
gURLBar.maybeHandleRevertFromPopup(anchorElement);
if (anchorElement?.checkVisibility()) {
return anchorElement;
}
let fallback = [
document.getElementById("identity-icon"),
document.getElementById("urlbar-search-button"),
];
return fallback.find(element => element?.checkVisibility()) ?? null;
};
return new PopupNotifications(
gBrowser,
document.getElementById("notification-popup"),
document.getElementById("notification-popup-box"),
{ shouldSuppress }
{ shouldSuppress, getVisibleAnchorElement }
);
} catch (ex) {
console.error(ex);

View File

@ -764,6 +764,16 @@ export class UrlbarInput {
}
}
maybeHandleRevertFromPopup(anchorElement) {
if (
anchorElement?.closest("#urlbar") &&
this.window.gBrowser.selectedBrowser.searchTerms
) {
this.handleRevert(true);
// TODO: Bug 1815971, add telemetry.
}
}
/**
* Called by inputs that resemble search boxes, but actually hand input off
* to the Urlbar. We use these fake inputs on the new tab page and

View File

@ -212,6 +212,10 @@ Notification.prototype = {
* If this function returns true, then all notifications are
* suppressed for this window. This state is checked on construction
* and when the "anchorVisibilityChange" method is called.
* getVisibleAnchorElement(anchorElement):
* A function which takes an anchor element as input and should return
* either the anchor if it's visible, a fallback anchor element, or if
* no fallback exists, a null element.
* }
*/
export function PopupNotifications(tabbrowser, panel, iconBox, options = {}) {
@ -228,6 +232,8 @@ export function PopupNotifications(tabbrowser, panel, iconBox, options = {}) {
this._shouldSuppress = options.shouldSuppress || (() => false);
this._suppress = this._shouldSuppress();
this._getVisibleAnchorElement = options.getVisibleAnchorElement;
this.window = tabbrowser.ownerGlobal;
this.panel = panel;
this.tabbrowser = tabbrowser;
@ -1214,48 +1220,27 @@ PopupNotifications.prototype = {
if (!notificationsToShow.length) {
return;
}
// Bug 1812232: Interim fix to avoid the Urlbar showing persisted
// terms as a Popup is trying to show. This isn't the best solution
// since it couples PopupNotifications with browser components, so it
// should be refactored (Bug 1815769) via dependency injection.
if (this.window.gURLBar && this.tabbrowser.selectedBrowser.searchTerms) {
this.window.gURLBar.handleRevert(true);
}
let notificationIds = notificationsToShow.map(n => n.id);
this._refreshPanel(notificationsToShow);
function isNullOrHidden(elem) {
if (!elem) {
return true;
}
let anchorRect = elem.getBoundingClientRect();
return anchorRect.width == 0 && anchorRect.height == 0;
// The element the PopupNotification should anchor to might not be visible.
// Check its visibility using a callback that returns the same anchor
// element if its visible, or a fallback option that is visible.
// If no fallbacks are visible, it should return null.
if (this._getVisibleAnchorElement) {
anchorElement = this._getVisibleAnchorElement(anchorElement);
}
// If the anchor element is hidden or null, fall back to the identity icon.
if (isNullOrHidden(anchorElement)) {
anchorElement = this.window.document.getElementById("identity-icon");
if (isNullOrHidden(anchorElement)) {
anchorElement = this.window.document.getElementById(
"urlbar-search-button"
);
}
// If the identity and search icons are not available in this window, use
// the tab as the anchor. We only ever show notifications for the current
// browser, so we can just use the current tab.
if (isNullOrHidden(anchorElement)) {
anchorElement = this.tabbrowser.selectedTab;
// In case _getVisibleAnchorElement provided a non-visible element.
if (!anchorElement?.checkVisibility()) {
// We only ever show notifications for the current browser,
// so we can just use the current tab.
anchorElement = this.tabbrowser.selectedTab;
if (!anchorElement?.checkVisibility()) {
// If we're in an entirely chromeless environment, set the anchorElement
// to null and let openPopup show the notification at (0,0) later.
if (isNullOrHidden(anchorElement)) {
anchorElement = null;
}
anchorElement = null;
}
}