diff options
Diffstat (limited to 'browser/components/newtab/lib/ToastNotification.jsm')
-rw-r--r-- | browser/components/newtab/lib/ToastNotification.jsm | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/browser/components/newtab/lib/ToastNotification.jsm b/browser/components/newtab/lib/ToastNotification.jsm new file mode 100644 index 0000000000..4d6193d76f --- /dev/null +++ b/browser/components/newtab/lib/ToastNotification.jsm @@ -0,0 +1,118 @@ +/* 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/. */ +"use strict"; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +const lazy = {}; + +XPCOMUtils.defineLazyModuleGetters(lazy, { + ExperimentAPI: "resource://nimbus/ExperimentAPI.jsm", + RemoteL10n: "resource://activity-stream/lib/RemoteL10n.jsm", +}); + +XPCOMUtils.defineLazyServiceGetters(lazy, { + AlertsService: ["@mozilla.org/alerts-service;1", "nsIAlertsService"], +}); + +const ToastNotification = { + // Allow testing to stub the alerts service. + get AlertsService() { + return lazy.AlertsService; + }, + + sendUserEventTelemetry(event, message, dispatch) { + const ping = { + message_id: message.id, + event, + }; + dispatch({ + type: "TOAST_NOTIFICATION_TELEMETRY", + data: { action: "toast_notification_user_event", ...ping }, + }); + }, + + /** + * Show a toast notification. + * @param message Message containing content to show. + * @param dispatch A function to dispatch resulting actions. + * @return boolean value capturing if toast notification was displayed. + */ + async showToastNotification(message, dispatch) { + let { content } = message; + let title = await lazy.RemoteL10n.formatLocalizableText(content.title); + let body = await lazy.RemoteL10n.formatLocalizableText(content.body); + + // The only link between background task message experiment and user + // re-engagement via the notification is the associated "tag". Said tag is + // usually controlled by the message content, but for message experiments, + // we want to avoid a missing tag and to ensure a deterministic tag for + // easier analysis, including across branches. + let { tag } = content; + + let experimentMetadata = + lazy.ExperimentAPI.getExperimentMetaData({ + featureId: "backgroundTaskMessage", + }) || {}; + + if ( + experimentMetadata?.active && + experimentMetadata?.slug && + experimentMetadata?.branch?.slug + ) { + // Like `my-experiment:my-branch`. + tag = `${experimentMetadata?.slug}:${experimentMetadata?.branch?.slug}`; + } + + // There are two events named `IMPRESSION` the first one refers to telemetry + // while the other refers to ASRouter impressions used for the frequency cap + this.sendUserEventTelemetry("IMPRESSION", message, dispatch); + dispatch({ type: "IMPRESSION", data: message }); + + let alert = Cc["@mozilla.org/alert-notification;1"].createInstance( + Ci.nsIAlertNotification + ); + let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); + alert.init( + tag, + content.image_url + ? Services.urlFormatter.formatURL(content.image_url) + : content.image_url, + title, + body, + true /* aTextClickable */, + content.data, + null /* aDir */, + null /* aLang */, + null /* aData */, + systemPrincipal, + null /* aInPrivateBrowsing */, + content.requireInteraction + ); + + if (content.actions) { + let actions = Cu.cloneInto(content.actions, {}); + for (let action of actions) { + if (action.title) { + action.title = await lazy.RemoteL10n.formatLocalizableText( + action.title + ); + } + } + alert.actions = actions; + } + + if (content.launch_url) { + alert.launchURL = Services.urlFormatter.formatURL(content.launch_url); + } + + this.AlertsService.showAlert(alert); + + return true; + }, +}; + +const EXPORTED_SYMBOLS = ["ToastNotification"]; |