tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ToastNotification.sys.mjs (4323B)


      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 
      5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  NimbusFeatures: "resource://nimbus/ExperimentAPI.sys.mjs",
     11  EnrollmentType: "resource://nimbus/ExperimentAPI.sys.mjs",
     12  RemoteL10n: "resource:///modules/asrouter/RemoteL10n.sys.mjs",
     13 });
     14 
     15 XPCOMUtils.defineLazyServiceGetters(lazy, {
     16  AlertsService: ["@mozilla.org/alerts-service;1", Ci.nsIAlertsService],
     17 });
     18 
     19 export const ToastNotification = {
     20  // Allow testing to stub the alerts service.
     21  get AlertsService() {
     22    return lazy.AlertsService;
     23  },
     24 
     25  sendUserEventTelemetry(event, message, dispatch) {
     26    const ping = {
     27      message_id: message.id,
     28      event,
     29    };
     30    dispatch({
     31      type: "TOAST_NOTIFICATION_TELEMETRY",
     32      data: { action: "toast_notification_user_event", ...ping },
     33    });
     34  },
     35 
     36  /**
     37   * Show a toast notification.
     38   *
     39   * @param message             Message containing content to show.
     40   * @param dispatch            A function to dispatch resulting actions.
     41   * @return                    boolean value capturing if toast notification was displayed.
     42   */
     43  async showToastNotification(message, dispatch) {
     44    let { content } = message;
     45    let title = await lazy.RemoteL10n.formatLocalizableText(content.title);
     46    let body = await lazy.RemoteL10n.formatLocalizableText(content.body);
     47 
     48    // The only link between background task message experiment and user
     49    // re-engagement via the notification is the associated "tag".  Said tag is
     50    // usually controlled by the message content, but for message experiments,
     51    // we want to avoid a missing tag and to ensure a deterministic tag for
     52    // easier analysis, including across branches.
     53    let { tag } = content;
     54 
     55    let experimentMetadata =
     56      lazy.NimbusFeatures.backgroundTaskMessage.getEnrollmentMetadata(
     57        lazy.EnrollmentType.EXPERIMENT
     58      );
     59 
     60    if (experimentMetadata) {
     61      // Like `my-experiment:my-branch`.
     62      tag = `${experimentMetadata.slug}:${experimentMetadata.branch}`;
     63    }
     64 
     65    // There are two events named `IMPRESSION` the first one refers to telemetry
     66    // while the other refers to ASRouter impressions used for the frequency cap
     67    this.sendUserEventTelemetry("IMPRESSION", message, dispatch);
     68    dispatch({ type: "IMPRESSION", data: message });
     69 
     70    let alert = Cc["@mozilla.org/alert-notification;1"].createInstance(
     71      Ci.nsIAlertNotification
     72    );
     73    let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     74    alert.init(
     75      tag,
     76      content.image_url
     77        ? Services.urlFormatter.formatURL(content.image_url)
     78        : content.image_url,
     79      title,
     80      body,
     81      true /* aTextClickable */,
     82      content.data,
     83      null /* aDir */,
     84      null /* aLang */,
     85      null /* aData */,
     86      systemPrincipal,
     87      null /* aInPrivateBrowsing */,
     88      content.requireInteraction
     89    );
     90 
     91    if (content.actions) {
     92      let actions = Cu.cloneInto(content.actions, {});
     93      for (let action of actions) {
     94        if (action.title) {
     95          action.title = await lazy.RemoteL10n.formatLocalizableText(
     96            action.title
     97          );
     98        }
     99        if (action.launch_action) {
    100          action.opaqueRelaunchData = JSON.stringify(action.launch_action);
    101          delete action.launch_action;
    102        }
    103      }
    104      alert.actions = actions;
    105    }
    106 
    107    // Populate `opaqueRelaunchData`, prefering `launch_action` if given,
    108    // falling back to `launch_url` if given.
    109    let relaunchAction = content.launch_action;
    110    if (!relaunchAction && content.launch_url) {
    111      relaunchAction = {
    112        type: "OPEN_URL",
    113        data: {
    114          args: content.launch_url,
    115          where: "tab",
    116        },
    117      };
    118    }
    119    if (relaunchAction) {
    120      alert.opaqueRelaunchData = JSON.stringify(relaunchAction);
    121    }
    122 
    123    let shownPromise = Promise.withResolvers();
    124    let obs = (subject, topic) => {
    125      if (topic === "alertshow") {
    126        shownPromise.resolve();
    127      }
    128    };
    129 
    130    this.AlertsService.showAlert(alert, obs);
    131 
    132    await shownPromise;
    133 
    134    return true;
    135  },
    136 };