Backed out 7 changesets (bug 1845150) for causing dt failures on browser_screenshot_b...
[gecko.git] / browser / components / newtab / lib / InfoBar.jsm
blobc75f17f197b6db8ba53411ad017ccd9b250c9fa6
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/. */
4 "use strict";
6 const lazy = {};
8 ChromeUtils.defineESModuleGetters(lazy, {
9   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
10   RemoteL10n: "resource://activity-stream/lib/RemoteL10n.sys.mjs",
11 });
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;
21   }
23   /**
24    * Show the infobar notification and send an impression ping
25    *
26    * @param {object} browser Browser reference for the currently selected tab
27    */
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;
35     } else {
36       notificationContainer = gBrowser.getNotificationBox(browser);
37     }
39     let priority = content.priority || notificationContainer.PRIORITY_SYSTEM;
41     this.notification = notificationContainer.appendNotification(
42       this.message.id,
43       {
44         label: this.formatMessageConfig(doc, content.text),
45         image: content.icon || "chrome://branding/content/icon64.png",
46         priority,
47         eventCallback: this.infobarCallback,
48       },
49       content.buttons.map(b => this.formatButtonConfig(b))
50     );
52     this.addImpression();
53   }
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 })
60     );
62     return docFragment;
63   }
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;
71     }
73     return btnConfig;
74   }
76   addImpression() {
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");
81   }
83   /**
84    * Called when one of the infobar buttons is clicked
85    */
86   buttonCallback(notificationBox, btnDescription, target) {
87     this.dispatchUserAction(
88       btnDescription.action,
89       target.ownerGlobal.gBrowser.selectedBrowser
90     );
91     let isPrimary = target.classList.contains("primary");
92     let eventName = isPrimary
93       ? "CLICK_PRIMARY_BUTTON"
94       : "CLICK_SECONDARY_BUTTON";
95     this.sendUserEventTelemetry(eventName);
96   }
98   dispatchUserAction(action, selectedBrowser) {
99     this._dispatch({ type: "USER_ACTION", data: action }, selectedBrowser);
100   }
102   /**
103    * Called when interacting with the toolbar (but not through the buttons)
104    */
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;
115     }
116   }
118   sendUserEventTelemetry(event) {
119     const ping = {
120       message_id: this.message.id,
121       event,
122     };
123     this._dispatch({
124       type: "INFOBAR_TELEMETRY",
125       data: { action: "infobar_user_event", ...ping },
126     });
127   }
130 const InfoBar = {
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",
137         win
138       );
139     }
140   },
142   maybeInsertFTL(win) {
143     win.MozXULElement.insertFTLIfNeeded("browser/newtab/asrouter.ftl");
144     win.MozXULElement.insertFTLIfNeeded(
145       "browser/defaultBrowserNotification.ftl"
146     );
147   },
149   showInfoBarMessage(browser, message, dispatch) {
150     // Prevent stacking multiple infobars
151     if (this._activeInfobar) {
152       return null;
153     }
155     const win = browser?.ownerGlobal;
157     if (!win || lazy.PrivateBrowsingUtils.isWindowPrivate(win)) {
158       return null;
159     }
161     this.maybeLoadCustomElement(win);
162     this.maybeInsertFTL(win);
164     let notification = new InfoBarNotification(message, dispatch);
165     notification.showNotification(browser);
166     this._activeInfobar = true;
168     return notification;
169   },
172 const EXPORTED_SYMBOLS = ["InfoBar"];