169 lines
4.9 KiB
JavaScript
169 lines
4.9 KiB
JavaScript
/* 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(
|
|
"chrome://browser/content/asrouter/components/remote-text.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;
|
|
},
|
|
};
|