/* 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/. */ /* eslint-disable no-use-before-define */ const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", RemoteL10n: "resource:///modules/asrouter/RemoteL10n.sys.mjs", SpecialMessageActions: "resource://messaging-system/lib/SpecialMessageActions.sys.mjs", }); const TYPES = { UNIVERSAL: "universal", GLOBAL: "global", }; const FTL_FILES = [ "browser/newtab/asrouter.ftl", "browser/defaultBrowserNotification.ftl", "preview/termsOfUse.ftl", ]; 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 ([TYPES.GLOBAL, TYPES.UNIVERSAL].includes(content.type)) { 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, browser, content.text), image: content.icon || "chrome://branding/content/icon64.png", priority, eventCallback: this.infobarCallback, }, content.buttons.map(b => this.formatButtonConfig(b)), false, content.dismissable ); // If InfoBar is universal, only record an impression for the first // instance. if ( content.type !== TYPES.UNIVERSAL || !InfoBar._universalInfobars.length ) { this.addImpression(); } if (content.type === TYPES.UNIVERSAL) { InfoBar._universalInfobars.push({ box: browser.ownerGlobal.gNotificationBox, notification: this.notification, }); } } formatMessageConfig(doc, browser, content) { const frag = doc.createDocumentFragment(); const parts = Array.isArray(content) ? content : [content]; for (const part of parts) { let node; if (typeof part === "string") { node = doc.createTextNode(part); // Handle embedded link } else if (part.href) { const a = doc.createElement("a"); a.href = part.href; a.addEventListener("click", e => { e.preventDefault(); lazy.SpecialMessageActions.handleAction( { type: "OPEN_URL", data: { args: a.href, where: part.where } }, browser ); }); if (part.string_id) { const l10n = lazy.RemoteL10n.createElement(doc, "span", { content: { string_id: part.string_id, ...(part.args && { args: part.args }), }, }); a.appendChild(l10n); } else { a.textContent = part.raw || ""; } node = a; } else if (part.string_id) { node = lazy.RemoteL10n.createElement(doc, "span", { content: { string_id: part.string_id, ...(part.args && { args: part.args }), }, }); } else { const text = part.raw !== null ? part.raw : String(part); node = doc.createTextNode(text); } frag.appendChild(node); } return frag; } 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"); } /** * Callback fired when a button in the infobar is clicked. * * @param {Element} notificationBox - The `` element representing the infobar. * @param {Object} btnDescription - An object describing the button, includes the label, the action with an optional dismiss property, and primary button styling. * @param {Element} target - The