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 };