From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- .../components/asrouter/modules/InfoBar.sys.mjs | 169 +++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 browser/components/asrouter/modules/InfoBar.sys.mjs (limited to 'browser/components/asrouter/modules/InfoBar.sys.mjs') diff --git a/browser/components/asrouter/modules/InfoBar.sys.mjs b/browser/components/asrouter/modules/InfoBar.sys.mjs new file mode 100644 index 0000000000..a287b650a5 --- /dev/null +++ b/browser/components/asrouter/modules/InfoBar.sys.mjs @@ -0,0 +1,169 @@ +/* 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/. */ + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", + RemoteL10n: "resource:///modules/asrouter/RemoteL10n.sys.mjs", +}); + +class InfoBarNotification { + constructor(message, dispatch) { + this._dispatch = dispatch; + this.dispatchUserAction = this.dispatchUserAction.bind(this); + this.buttonCallback = this.buttonCallback.bind(this); + this.infobarCallback = this.infobarCallback.bind(this); + this.message = message; + this.notification = null; + } + + /** + * Show the infobar notification and send an impression ping + * + * @param {object} browser Browser reference for the currently selected tab + */ + async showNotification(browser) { + let { content } = this.message; + let { gBrowser } = browser.ownerGlobal; + let doc = gBrowser.ownerDocument; + let notificationContainer; + if (content.type === "global") { + notificationContainer = browser.ownerGlobal.gNotificationBox; + } else { + notificationContainer = gBrowser.getNotificationBox(browser); + } + + let priority = content.priority || notificationContainer.PRIORITY_SYSTEM; + + this.notification = await notificationContainer.appendNotification( + this.message.id, + { + label: this.formatMessageConfig(doc, content.text), + image: content.icon || "chrome://branding/content/icon64.png", + priority, + eventCallback: this.infobarCallback, + }, + content.buttons.map(b => this.formatButtonConfig(b)) + ); + + this.addImpression(); + } + + formatMessageConfig(doc, content) { + let docFragment = doc.createDocumentFragment(); + // notificationbox will only `appendChild` for documentFragments + docFragment.appendChild( + lazy.RemoteL10n.createElement(doc, "span", { content }) + ); + + return docFragment; + } + + formatButtonConfig(button) { + let btnConfig = { callback: this.buttonCallback, ...button }; + // notificationbox will set correct data-l10n-id attributes if passed in + // using the l10n-id key. Otherwise the `button.label` text is used. + if (button.label.string_id) { + btnConfig["l10n-id"] = button.label.string_id; + } + + return btnConfig; + } + + addImpression() { + // Record an impression in ASRouter for frequency capping + this._dispatch({ type: "IMPRESSION", data: this.message }); + // Send a user impression telemetry ping + this.sendUserEventTelemetry("IMPRESSION"); + } + + /** + * Called when one of the infobar buttons is clicked + */ + buttonCallback(notificationBox, btnDescription, target) { + this.dispatchUserAction( + btnDescription.action, + target.ownerGlobal.gBrowser.selectedBrowser + ); + let isPrimary = target.classList.contains("primary"); + let eventName = isPrimary + ? "CLICK_PRIMARY_BUTTON" + : "CLICK_SECONDARY_BUTTON"; + this.sendUserEventTelemetry(eventName); + } + + dispatchUserAction(action, selectedBrowser) { + this._dispatch({ type: "USER_ACTION", data: action }, selectedBrowser); + } + + /** + * Called when interacting with the toolbar (but not through the buttons) + */ + infobarCallback(eventType) { + if (eventType === "removed") { + this.notification = null; + // eslint-disable-next-line no-use-before-define + InfoBar._activeInfobar = null; + } else if (this.notification) { + this.sendUserEventTelemetry("DISMISSED"); + this.notification = null; + // eslint-disable-next-line no-use-before-define + InfoBar._activeInfobar = null; + } + } + + sendUserEventTelemetry(event) { + const ping = { + message_id: this.message.id, + event, + }; + this._dispatch({ + type: "INFOBAR_TELEMETRY", + data: { action: "infobar_user_event", ...ping }, + }); + } +} + +export const InfoBar = { + _activeInfobar: null, + + maybeLoadCustomElement(win) { + if (!win.customElements.get("remote-text")) { + Services.scriptloader.loadSubScript( + "resource://activity-stream/data/custom-elements/paragraph.js", + win + ); + } + }, + + maybeInsertFTL(win) { + win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl"); + win.MozXULElement.insertFTLIfNeeded( + "browser/defaultBrowserNotification.ftl" + ); + }, + + async showInfoBarMessage(browser, message, dispatch) { + // Prevent stacking multiple infobars + if (this._activeInfobar) { + return null; + } + + const win = browser?.ownerGlobal; + + if (!win || lazy.PrivateBrowsingUtils.isWindowPrivate(win)) { + return null; + } + + this.maybeLoadCustomElement(win); + this.maybeInsertFTL(win); + + let notification = new InfoBarNotification(message, dispatch); + await notification.showNotification(browser); + this._activeInfobar = true; + + return notification; + }, +}; -- cgit v1.2.3