1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 ChromeUtils.defineESModuleGetters(lazy, {
9 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
10 RemoteL10n: "resource://activity-stream/lib/RemoteL10n.sys.mjs",
13 class InfoBarNotification {
14 constructor(message, dispatch) {
15 this._dispatch = dispatch;
16 this.dispatchUserAction = this.dispatchUserAction.bind(this);
17 this.buttonCallback = this.buttonCallback.bind(this);
18 this.infobarCallback = this.infobarCallback.bind(this);
19 this.message = message;
20 this.notification = null;
24 * Show the infobar notification and send an impression ping
26 * @param {object} browser Browser reference for the currently selected tab
28 showNotification(browser) {
29 let { content } = this.message;
30 let { gBrowser } = browser.ownerGlobal;
31 let doc = gBrowser.ownerDocument;
32 let notificationContainer;
33 if (content.type === "global") {
34 notificationContainer = browser.ownerGlobal.gNotificationBox;
36 notificationContainer = gBrowser.getNotificationBox(browser);
39 let priority = content.priority || notificationContainer.PRIORITY_SYSTEM;
41 this.notification = notificationContainer.appendNotification(
44 label: this.formatMessageConfig(doc, content.text),
45 image: content.icon || "chrome://branding/content/icon64.png",
47 eventCallback: this.infobarCallback,
49 content.buttons.map(b => this.formatButtonConfig(b))
55 formatMessageConfig(doc, content) {
56 let docFragment = doc.createDocumentFragment();
57 // notificationbox will only `appendChild` for documentFragments
58 docFragment.appendChild(
59 lazy.RemoteL10n.createElement(doc, "span", { content })
65 formatButtonConfig(button) {
66 let btnConfig = { callback: this.buttonCallback, ...button };
67 // notificationbox will set correct data-l10n-id attributes if passed in
68 // using the l10n-id key. Otherwise the `button.label` text is used.
69 if (button.label.string_id) {
70 btnConfig["l10n-id"] = button.label.string_id;
77 // Record an impression in ASRouter for frequency capping
78 this._dispatch({ type: "IMPRESSION", data: this.message });
79 // Send a user impression telemetry ping
80 this.sendUserEventTelemetry("IMPRESSION");
84 * Called when one of the infobar buttons is clicked
86 buttonCallback(notificationBox, btnDescription, target) {
87 this.dispatchUserAction(
88 btnDescription.action,
89 target.ownerGlobal.gBrowser.selectedBrowser
91 let isPrimary = target.classList.contains("primary");
92 let eventName = isPrimary
93 ? "CLICK_PRIMARY_BUTTON"
94 : "CLICK_SECONDARY_BUTTON";
95 this.sendUserEventTelemetry(eventName);
98 dispatchUserAction(action, selectedBrowser) {
99 this._dispatch({ type: "USER_ACTION", data: action }, selectedBrowser);
103 * Called when interacting with the toolbar (but not through the buttons)
105 infobarCallback(eventType) {
106 if (eventType === "removed") {
107 this.notification = null;
108 // eslint-disable-next-line no-use-before-define
109 InfoBar._activeInfobar = null;
110 } else if (this.notification) {
111 this.sendUserEventTelemetry("DISMISSED");
112 this.notification = null;
113 // eslint-disable-next-line no-use-before-define
114 InfoBar._activeInfobar = null;
118 sendUserEventTelemetry(event) {
120 message_id: this.message.id,
124 type: "INFOBAR_TELEMETRY",
125 data: { action: "infobar_user_event", ...ping },
131 _activeInfobar: null,
133 maybeLoadCustomElement(win) {
134 if (!win.customElements.get("remote-text")) {
135 Services.scriptloader.loadSubScript(
136 "resource://activity-stream/data/custom-elements/paragraph.js",
142 maybeInsertFTL(win) {
143 win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl");
144 win.MozXULElement.insertFTLIfNeeded(
145 "browser/defaultBrowserNotification.ftl"
149 showInfoBarMessage(browser, message, dispatch) {
150 // Prevent stacking multiple infobars
151 if (this._activeInfobar) {
155 const win = browser?.ownerGlobal;
157 if (!win || lazy.PrivateBrowsingUtils.isWindowPrivate(win)) {
161 this.maybeLoadCustomElement(win);
162 this.maybeInsertFTL(win);
164 let notification = new InfoBarNotification(message, dispatch);
165 notification.showNotification(browser);
166 this._activeInfobar = true;
172 const EXPORTED_SYMBOLS = ["InfoBar"];