tor-browser

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

TorSettingsNotification.sys.mjs (5548B)


      1 const lazy = {};
      2 
      3 ChromeUtils.defineESModuleGetters(lazy, {
      4  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
      5  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
      6  TorSettingsTopics: "resource://gre/modules/TorSettings.sys.mjs",
      7 });
      8 
      9 ChromeUtils.defineLazyGetter(lazy, "NotificationStrings", function () {
     10  return new Localization(["toolkit/global/tor-browser.ftl"]);
     11 });
     12 
     13 /**
     14 * Shows a notification whenever we get an ApplyError.
     15 */
     16 export const TorSettingsNotification = {
     17  /**
     18   * Whether we have already been initialised.
     19   *
     20   * @type {boolean}
     21   */
     22  _initialized: false,
     23 
     24  /**
     25   * Called when the UI is ready to show a notification.
     26   */
     27  ready() {
     28    if (this._initialized) {
     29      return;
     30    }
     31    this._initialized = true;
     32    Services.obs.addObserver(this, lazy.TorSettingsTopics.ApplyError);
     33 
     34    // Show the notification for each group of settings if they have an error
     35    // that was triggered prior to `ready` being called.
     36    this.showNotification("bridges");
     37    this.showNotification("proxy");
     38    this.showNotification("firewall");
     39  },
     40 
     41  observe(subject, topic) {
     42    if (topic === lazy.TorSettingsTopics.ApplyError) {
     43      this.showNotification(subject.wrappedJSObject.group);
     44    }
     45  },
     46 
     47  /**
     48   * A promise for the `showNotification` method to ensure we only show one
     49   * notification at a time.
     50   *
     51   * @type {?Promise}
     52   */
     53  _notificationPromise: null,
     54 
     55  /**
     56   * Show a notification for the given group of settings if `TorSettings` has an
     57   * error for them.
     58   *
     59   * @param {string} group - The settings group to show the notification for.
     60   */
     61  async showNotification(group) {
     62    const prevNotificationPromise = this._notificationPromise;
     63    let notificationComplete;
     64    ({ promise: this._notificationPromise, resolve: notificationComplete } =
     65      Promise.withResolvers());
     66    // Only want to show one notification at a time, so queue behind the
     67    // previous one.
     68    await prevNotificationPromise;
     69 
     70    // NOTE: We only show the notification for a single `group` at a time, even
     71    // when TorSettings has errors for multiple groups. This keeps the strings
     72    // simple and means we can show different buttons depending on `canUndo` for
     73    // each group individually.
     74    // If we do have multiple errors the notification for each group will simply
     75    // queue behind each other.
     76    try {
     77      // Grab the latest error value, which may have changed since
     78      // showNotification was first called.
     79      const error = lazy.TorSettings.getApplyError(group);
     80      if (!error) {
     81        // No current error for this group.
     82        return;
     83      }
     84 
     85      const { canUndo } = error;
     86 
     87      let titleId;
     88      let introId;
     89      switch (group) {
     90        case "bridges":
     91          titleId = "tor-settings-failed-notification-title-bridges";
     92          introId = "tor-settings-failed-notification-cause-bridges";
     93          break;
     94        case "proxy":
     95          titleId = "tor-settings-failed-notification-title-proxy";
     96          introId = "tor-settings-failed-notification-cause-proxy";
     97          break;
     98        case "firewall":
     99          titleId = "tor-settings-failed-notification-title-firewall";
    100          introId = "tor-settings-failed-notification-cause-firewall";
    101          break;
    102      }
    103 
    104      const [
    105        titleText,
    106        introText,
    107        bodyText,
    108        primaryButtonText,
    109        secondaryButtonText,
    110      ] = await lazy.NotificationStrings.formatValues([
    111        { id: titleId },
    112        { id: introId },
    113        {
    114          id: canUndo
    115            ? "tor-settings-failed-notification-body-undo"
    116            : "tor-settings-failed-notification-body-default",
    117        },
    118        {
    119          id: canUndo
    120            ? "tor-settings-failed-notification-button-undo"
    121            : "tor-settings-failed-notification-button-clear",
    122        },
    123        { id: "tor-settings-failed-notification-button-fix-myself" },
    124      ]);
    125 
    126      const propBag = await Services.prompt.asyncConfirmEx(
    127        lazy.BrowserWindowTracker.getTopWindow()?.browsingContext ?? null,
    128        Services.prompt.MODAL_TYPE_INTERNAL_WINDOW,
    129        titleText,
    130        // Concatenate the intro text and the body text. Really these should be
    131        // separate paragraph elements, but the prompt service does not support
    132        // this. We split them with a double newline, which will hopefully avoid
    133        // the usual problems with concatenating localised strings.
    134        `${introText}\n\n${bodyText}`,
    135        Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 +
    136          Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1,
    137        primaryButtonText,
    138        secondaryButtonText,
    139        null,
    140        null,
    141        null,
    142        {}
    143      );
    144 
    145      const buttonNum = propBag.get("buttonNumClicked");
    146 
    147      if (buttonNum === 0) {
    148        if (canUndo) {
    149          // Wait for these methods in case they resolve the error for a pending
    150          // showNotification call.
    151          await lazy.TorSettings.undoFailedSettings(group);
    152        } else {
    153          await lazy.TorSettings.clearFailedSettings(group);
    154        }
    155      } else if (buttonNum === 1) {
    156        let win = lazy.BrowserWindowTracker.getTopWindow();
    157        if (!win) {
    158          win = await lazy.BrowserWindowTracker.promiseOpenWindow();
    159        }
    160        // Open the preferences or switch to its tab and highlight the Tor log.
    161        win.openPreferences("connection-viewlogs");
    162      }
    163    } finally {
    164      notificationComplete();
    165    }
    166  },
    167 };