richard pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser

Commits:

24 changed files:

Changes:

  • browser/components/abouttor/TorCheckService.sys.mjs
    ... ... @@ -11,14 +11,9 @@ const lazy = {};
    11 11
     
    
    12 12
     ChromeUtils.defineESModuleGetters(lazy, {
    
    13 13
       ConsoleAPI: "resource://gre/modules/Console.sys.mjs",
    
    14
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    14 15
     });
    
    15 16
     
    
    16
    -ChromeUtils.defineModuleGetter(
    
    17
    -  lazy,
    
    18
    -  "TorProtocolService",
    
    19
    -  "resource://gre/modules/TorProtocolService.jsm"
    
    20
    -);
    
    21
    -
    
    22 17
     export const TorCheckService = {
    
    23 18
       kCheckNotInitiated: 0, // Possible values for status.
    
    24 19
       kCheckSuccessful: 1,
    
    ... ... @@ -109,7 +104,7 @@ export const TorCheckService = {
    109 104
     
    
    110 105
         let listeners;
    
    111 106
         try {
    
    112
    -      listeners = await lazy.TorProtocolService.getSocksListeners();
    
    107
    +      listeners = await lazy.TorProviderBuilder.build().getSocksListeners();
    
    113 108
         } catch (e) {
    
    114 109
           this._logger.error("Failed to get the SOCKS listerner addresses.", e);
    
    115 110
           return false;
    

  • browser/components/onionservices/content/authPrompt.js
    ... ... @@ -4,10 +4,13 @@
    4 4
     
    
    5 5
     /* globals gBrowser, PopupNotifications, Services, XPCOMUtils */
    
    6 6
     
    
    7
    +ChromeUtils.defineESModuleGetters(this, {
    
    8
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    9
    +});
    
    10
    +
    
    7 11
     XPCOMUtils.defineLazyModuleGetters(this, {
    
    8 12
       OnionAuthUtil: "chrome://browser/content/onionservices/authUtil.jsm",
    
    9 13
       CommonUtils: "resource://services-common/utils.js",
    
    10
    -  TorProtocolService: "resource://gre/modules/TorProtocolService.jsm",
    
    11 14
       TorStrings: "resource:///modules/TorStrings.jsm",
    
    12 15
     });
    
    13 16
     
    
    ... ... @@ -203,7 +206,8 @@ const OnionAuthPrompt = (function () {
    203 206
     
    
    204 207
             let checkboxElem = this._getCheckboxElement();
    
    205 208
             let isPermanent = checkboxElem && checkboxElem.checked;
    
    206
    -        TorProtocolService.onionAuthAdd(onionServiceId, base64key, isPermanent)
    
    209
    +        TorProviderBuilder.build()
    
    210
    +          .onionAuthAdd(onionServiceId, base64key, isPermanent)
    
    207 211
               .then(aResponse => {
    
    208 212
                 // Success! Reload the page.
    
    209 213
                 this._browser.sendMessageToActor(
    

  • browser/components/onionservices/content/savedKeysDialog.js
    ... ... @@ -8,11 +8,9 @@ ChromeUtils.defineModuleGetter(
    8 8
       "resource:///modules/TorStrings.jsm"
    
    9 9
     );
    
    10 10
     
    
    11
    -ChromeUtils.defineModuleGetter(
    
    12
    -  this,
    
    13
    -  "TorProtocolService",
    
    14
    -  "resource://gre/modules/TorProtocolService.jsm"
    
    15
    -);
    
    11
    +ChromeUtils.defineESModuleGetters(this, {
    
    12
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    13
    +});
    
    16 14
     
    
    17 15
     var gOnionServicesSavedKeysDialog = {
    
    18 16
       selector: {
    
    ... ... @@ -54,6 +52,7 @@ var gOnionServicesSavedKeysDialog = {
    54 52
               await this._deleteOneKey(indexesToDelete[i]);
    
    55 53
             }
    
    56 54
           } catch (e) {
    
    55
    +        console.error("Removing a saved key failed", e);
    
    57 56
             if (e.torMessage) {
    
    58 57
               this._showError(e.torMessage);
    
    59 58
             } else {
    
    ... ... @@ -125,22 +124,16 @@ var gOnionServicesSavedKeysDialog = {
    125 124
         try {
    
    126 125
           this._tree.view = this;
    
    127 126
     
    
    128
    -      const keyInfoList = await TorProtocolService.onionAuthViewKeys();
    
    127
    +      const keyInfoList = await TorProviderBuilder.build().onionAuthViewKeys();
    
    129 128
           if (keyInfoList) {
    
    130 129
             // Filter out temporary keys.
    
    131
    -        this._keyInfoList = keyInfoList.filter(aKeyInfo => {
    
    132
    -          if (!aKeyInfo.Flags) {
    
    133
    -            return false;
    
    134
    -          }
    
    135
    -
    
    136
    -          const flags = aKeyInfo.Flags.split(",");
    
    137
    -          return flags.includes("Permanent");
    
    138
    -        });
    
    139
    -
    
    130
    +        this._keyInfoList = keyInfoList.filter(aKeyInfo =>
    
    131
    +          aKeyInfo.flags?.includes("Permanent")
    
    132
    +        );
    
    140 133
             // Sort by the .onion address.
    
    141 134
             this._keyInfoList.sort((aObj1, aObj2) => {
    
    142
    -          const hsAddr1 = aObj1.hsAddress.toLowerCase();
    
    143
    -          const hsAddr2 = aObj2.hsAddress.toLowerCase();
    
    135
    +          const hsAddr1 = aObj1.address.toLowerCase();
    
    136
    +          const hsAddr2 = aObj2.address.toLowerCase();
    
    144 137
               if (hsAddr1 < hsAddr2) {
    
    145 138
                 return -1;
    
    146 139
               }
    
    ... ... @@ -164,7 +157,7 @@ var gOnionServicesSavedKeysDialog = {
    164 157
       // This method may throw; callers should catch errors.
    
    165 158
       async _deleteOneKey(aIndex) {
    
    166 159
         const keyInfoObj = this._keyInfoList[aIndex];
    
    167
    -    await TorProtocolService.onionAuthRemove(keyInfoObj.hsAddress);
    
    160
    +    await TorProviderBuilder.build().onionAuthRemove(keyInfoObj.address);
    
    168 161
         this._tree.view.selection.clearRange(aIndex, aIndex);
    
    169 162
         this._keyInfoList.splice(aIndex, 1);
    
    170 163
         this._tree.rowCountChanged(aIndex + 1, -1);
    
    ... ... @@ -193,26 +186,20 @@ var gOnionServicesSavedKeysDialog = {
    193 186
     
    
    194 187
       // XUL tree widget view implementation.
    
    195 188
       get rowCount() {
    
    196
    -    return this._keyInfoList ? this._keyInfoList.length : 0;
    
    189
    +    return this._keyInfoList?.length ?? 0;
    
    197 190
       },
    
    198 191
     
    
    199 192
       getCellText(aRow, aCol) {
    
    200
    -    let val = "";
    
    201 193
         if (this._keyInfoList && aRow < this._keyInfoList.length) {
    
    202 194
           const keyInfo = this._keyInfoList[aRow];
    
    203 195
           if (aCol.id.endsWith("-siteCol")) {
    
    204
    -        val = keyInfo.hsAddress;
    
    196
    +        return keyInfo.address;
    
    205 197
           } else if (aCol.id.endsWith("-keyCol")) {
    
    206
    -        val = keyInfo.typeAndKey;
    
    207
    -        // Omit keyType because it is always "x25519".
    
    208
    -        const idx = val.indexOf(":");
    
    209
    -        if (idx > 0) {
    
    210
    -          val = val.substring(idx + 1);
    
    211
    -        }
    
    198
    +        // keyType is always "x25519", so do not show it.
    
    199
    +        return keyInfo.keyBlob;
    
    212 200
           }
    
    213 201
         }
    
    214
    -
    
    215
    -    return val;
    
    202
    +    return "";
    
    216 203
       },
    
    217 204
     
    
    218 205
       isSeparator(index) {
    

  • browser/components/torpreferences/content/builtinBridgeDialog.jsm
    ... ... @@ -7,10 +7,10 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
    7 7
     const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
    
    8 8
     
    
    9 9
     const { TorSettings, TorBridgeSource, TorBuiltinBridgeTypes } =
    
    10
    -  ChromeUtils.import("resource:///modules/TorSettings.jsm");
    
    10
    +  ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
    
    11 11
     
    
    12
    -const { TorConnect, TorConnectTopics } = ChromeUtils.import(
    
    13
    -  "resource:///modules/TorConnect.jsm"
    
    12
    +const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
    
    13
    +  "resource:///modules/TorConnect.sys.mjs"
    
    14 14
     );
    
    15 15
     
    
    16 16
     class BuiltinBridgeDialog {
    

  • browser/components/torpreferences/content/connectionPane.js
    ... ... @@ -12,20 +12,17 @@ const { setTimeout, clearTimeout } = ChromeUtils.import(
    12 12
     );
    
    13 13
     
    
    14 14
     const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
    
    15
    -  ChromeUtils.import("resource:///modules/TorSettings.jsm");
    
    15
    +  ChromeUtils.importESModule("resource:///modules/TorSettings.sys.mjs");
    
    16 16
     
    
    17 17
     const { TorParsers } = ChromeUtils.importESModule(
    
    18 18
       "resource://gre/modules/TorParsers.sys.mjs"
    
    19 19
     );
    
    20
    -const { TorProtocolService } = ChromeUtils.importESModule(
    
    21
    -  "resource://gre/modules/TorProtocolService.sys.mjs"
    
    22
    -);
    
    23
    -const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
    
    24
    -  "resource://gre/modules/TorMonitorService.jsm"
    
    20
    +const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
    
    21
    +  "resource://gre/modules/TorProviderBuilder.sys.mjs"
    
    25 22
     );
    
    26 23
     
    
    27 24
     const { TorConnect, TorConnectTopics, TorConnectState, TorCensorshipLevel } =
    
    28
    -  ChromeUtils.import("resource:///modules/TorConnect.jsm");
    
    25
    +  ChromeUtils.importESModule("resource:///modules/TorConnect.sys.mjs");
    
    29 26
     
    
    30 27
     const { TorLogDialog } = ChromeUtils.import(
    
    31 28
       "chrome://browser/content/torpreferences/torLogDialog.jsm"
    
    ... ... @@ -51,7 +48,9 @@ const { ProvideBridgeDialog } = ChromeUtils.import(
    51 48
       "chrome://browser/content/torpreferences/provideBridgeDialog.jsm"
    
    52 49
     );
    
    53 50
     
    
    54
    -const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
    
    51
    +const { MoatRPC } = ChromeUtils.importESModule(
    
    52
    +  "resource:///modules/Moat.sys.mjs"
    
    53
    +);
    
    55 54
     
    
    56 55
     const { QRCode } = ChromeUtils.import("resource://gre/modules/QRCode.jsm");
    
    57 56
     
    
    ... ... @@ -156,7 +155,7 @@ const gConnectionPane = (function () {
    156 155
         _populateXUL() {
    
    157 156
           // saves tor settings to disk when navigate away from about:preferences
    
    158 157
           window.addEventListener("blur", val => {
    
    159
    -        TorProtocolService.flushSettings();
    
    158
    +        TorProviderBuilder.build().flushSettings();
    
    160 159
           });
    
    161 160
     
    
    162 161
           document
    
    ... ... @@ -751,7 +750,7 @@ const gConnectionPane = (function () {
    751 750
             // TODO: We could make sure TorSettings is in sync by monitoring also
    
    752 751
             // changes of settings. At that point, we could query it, instead of
    
    753 752
             // doing a query over the control port.
    
    754
    -        const bridge = TorMonitorService.currentBridge;
    
    753
    +        const bridge = TorProviderBuilder.build().currentBridge;
    
    755 754
             if (bridge?.fingerprint !== this._currentBridgeId) {
    
    756 755
               this._currentBridgeId = bridge?.fingerprint ?? null;
    
    757 756
               this._updateConnectedBridges();
    
    ... ... @@ -850,7 +849,7 @@ const gConnectionPane = (function () {
    850 849
           });
    
    851 850
     
    
    852 851
           Services.obs.addObserver(this, TorConnectTopics.StateChange);
    
    853
    -      Services.obs.addObserver(this, TorMonitorTopics.BridgeChanged);
    
    852
    +      Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
    
    854 853
           Services.obs.addObserver(this, "intl:app-locales-changed");
    
    855 854
         },
    
    856 855
     
    
    ... ... @@ -875,7 +874,7 @@ const gConnectionPane = (function () {
    875 874
           // unregister our observer topics
    
    876 875
           Services.obs.removeObserver(this, TorSettingsTopics.SettingChanged);
    
    877 876
           Services.obs.removeObserver(this, TorConnectTopics.StateChange);
    
    878
    -      Services.obs.removeObserver(this, TorMonitorTopics.BridgeChanged);
    
    877
    +      Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
    
    879 878
           Services.obs.removeObserver(this, "intl:app-locales-changed");
    
    880 879
         },
    
    881 880
     
    
    ... ... @@ -907,7 +906,7 @@ const gConnectionPane = (function () {
    907 906
               this.onStateChange();
    
    908 907
               break;
    
    909 908
             }
    
    910
    -        case TorMonitorTopics.BridgeChanged: {
    
    909
    +        case TorProviderTopics.BridgeChanged: {
    
    911 910
               if (data?.fingerprint !== this._currentBridgeId) {
    
    912 911
                 this._checkConnectedBridge();
    
    913 912
               }
    

  • browser/components/torpreferences/content/connectionSettingsDialog.jsm
    ... ... @@ -2,8 +2,8 @@
    2 2
     
    
    3 3
     var EXPORTED_SYMBOLS = ["ConnectionSettingsDialog"];
    
    4 4
     
    
    5
    -const { TorSettings, TorProxyType } = ChromeUtils.import(
    
    6
    -  "resource:///modules/TorSettings.jsm"
    
    5
    +const { TorSettings, TorProxyType } = ChromeUtils.importESModule(
    
    6
    +  "resource:///modules/TorSettings.sys.mjs"
    
    7 7
     );
    
    8 8
     
    
    9 9
     const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
    

  • browser/components/torpreferences/content/provideBridgeDialog.jsm
    ... ... @@ -6,12 +6,12 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
    6 6
     
    
    7 7
     const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
    
    8 8
     
    
    9
    -const { TorSettings, TorBridgeSource } = ChromeUtils.import(
    
    10
    -  "resource:///modules/TorSettings.jsm"
    
    9
    +const { TorSettings, TorBridgeSource } = ChromeUtils.importESModule(
    
    10
    +  "resource:///modules/TorSettings.sys.mjs"
    
    11 11
     );
    
    12 12
     
    
    13
    -const { TorConnect, TorConnectTopics } = ChromeUtils.import(
    
    14
    -  "resource:///modules/TorConnect.jsm"
    
    13
    +const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
    
    14
    +  "resource:///modules/TorConnect.sys.mjs"
    
    15 15
     );
    
    16 16
     
    
    17 17
     class ProvideBridgeDialog {
    

  • browser/components/torpreferences/content/requestBridgeDialog.jsm
    ... ... @@ -4,11 +4,13 @@ var EXPORTED_SYMBOLS = ["RequestBridgeDialog"];
    4 4
     
    
    5 5
     const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
    
    6 6
     
    
    7
    -const { BridgeDB } = ChromeUtils.import("resource:///modules/BridgeDB.jsm");
    
    7
    +const { BridgeDB } = ChromeUtils.importESModule(
    
    8
    +  "resource:///modules/BridgeDB.sys.mjs"
    
    9
    +);
    
    8 10
     const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
    
    9 11
     
    
    10
    -const { TorConnect, TorConnectTopics } = ChromeUtils.import(
    
    11
    -  "resource:///modules/TorConnect.jsm"
    
    12
    +const { TorConnect, TorConnectTopics } = ChromeUtils.importESModule(
    
    13
    +  "resource:///modules/TorConnect.sys.mjs"
    
    12 14
     );
    
    13 15
     
    
    14 16
     class RequestBridgeDialog {
    

  • browser/components/torpreferences/content/torLogDialog.jsm
    ... ... @@ -2,12 +2,12 @@
    2 2
     
    
    3 3
     var EXPORTED_SYMBOLS = ["TorLogDialog"];
    
    4 4
     
    
    5
    -const { setTimeout, clearTimeout } = ChromeUtils.import(
    
    6
    -  "resource://gre/modules/Timer.jsm"
    
    5
    +const { setTimeout, clearTimeout } = ChromeUtils.importESModule(
    
    6
    +  "resource://gre/modules/Timer.sys.mjs"
    
    7 7
     );
    
    8 8
     
    
    9
    -const { TorMonitorService } = ChromeUtils.import(
    
    10
    -  "resource://gre/modules/TorMonitorService.jsm"
    
    9
    +const { TorProviderBuilder } = ChromeUtils.importESModule(
    
    10
    +  "resource://gre/modules/TorProviderBuilder.sys.mjs"
    
    11 11
     );
    
    12 12
     const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
    
    13 13
     
    
    ... ... @@ -56,7 +56,7 @@ class TorLogDialog {
    56 56
           }, RESTORE_TIME);
    
    57 57
         });
    
    58 58
     
    
    59
    -    this._logTextarea.value = TorMonitorService.getLog();
    
    59
    +    this._logTextarea.value = TorProviderBuilder.build().getLog();
    
    60 60
       }
    
    61 61
     
    
    62 62
       init(window, aDialog) {
    

  • browser/modules/BridgeDB.jsmbrowser/modules/BridgeDB.sys.mjs
    1
    -"use strict";
    
    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/. */
    
    2 4
     
    
    3
    -var EXPORTED_SYMBOLS = ["BridgeDB"];
    
    5
    +const lazy = {};
    
    4 6
     
    
    5
    -const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
    
    7
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    8
    +  MoatRPC: "resource:///modules/Moat.sys.mjs",
    
    9
    +});
    
    6 10
     
    
    7
    -var BridgeDB = {
    
    11
    +export var BridgeDB = {
    
    8 12
       _moatRPC: null,
    
    9 13
       _challenge: null,
    
    10 14
       _image: null,
    
    ... ... @@ -20,7 +24,7 @@ var BridgeDB = {
    20 24
     
    
    21 25
       async submitCaptchaGuess(solution) {
    
    22 26
         if (!this._moatRPC) {
    
    23
    -      this._moatRPC = new MoatRPC();
    
    27
    +      this._moatRPC = new lazy.MoatRPC();
    
    24 28
           await this._moatRPC.init();
    
    25 29
         }
    
    26 30
     
    
    ... ... @@ -37,7 +41,7 @@ var BridgeDB = {
    37 41
       async requestNewCaptchaImage() {
    
    38 42
         try {
    
    39 43
           if (!this._moatRPC) {
    
    40
    -        this._moatRPC = new MoatRPC();
    
    44
    +        this._moatRPC = new lazy.MoatRPC();
    
    41 45
             await this._moatRPC.init();
    
    42 46
           }
    
    43 47
     
    

  • browser/modules/Moat.jsmbrowser/modules/Moat.sys.mjs
    1
    -"use strict";
    
    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/. */
    
    2 4
     
    
    3
    -var EXPORTED_SYMBOLS = ["MoatRPC"];
    
    5
    +import {
    
    6
    +  TorSettings,
    
    7
    +  TorBridgeSource,
    
    8
    +} from "resource:///modules/TorSettings.sys.mjs";
    
    4 9
     
    
    5
    -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
    
    10
    +const lazy = {};
    
    6 11
     
    
    7
    -const { Subprocess } = ChromeUtils.import(
    
    8
    -  "resource://gre/modules/Subprocess.jsm"
    
    9
    -);
    
    10
    -
    
    11
    -const { TorLauncherUtil } = ChromeUtils.import(
    
    12
    -  "resource://gre/modules/TorLauncherUtil.jsm"
    
    13
    -);
    
    14
    -
    
    15
    -const { TorProtocolService } = ChromeUtils.import(
    
    16
    -  "resource://gre/modules/TorProtocolService.jsm"
    
    17
    -);
    
    18
    -
    
    19
    -const { TorSettings, TorBridgeSource } = ChromeUtils.import(
    
    20
    -  "resource:///modules/TorSettings.jsm"
    
    21
    -);
    
    12
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    13
    +  Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
    
    14
    +  TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    15
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    16
    +});
    
    22 17
     
    
    23 18
     const TorLauncherPrefs = Object.freeze({
    
    24 19
       bridgedb_front: "extensions.torlauncher.bridgedb_front",
    
    ... ... @@ -26,73 +21,54 @@ const TorLauncherPrefs = Object.freeze({
    26 21
       moat_service: "extensions.torlauncher.moat_service",
    
    27 22
     });
    
    28 23
     
    
    29
    -// Config keys used to query tor daemon properties
    
    30
    -const TorConfigKeys = Object.freeze({
    
    31
    -  clientTransportPlugin: "ClientTransportPlugin",
    
    32
    -});
    
    33
    -
    
    34 24
     //
    
    35 25
     // Launches and controls the PT process lifetime
    
    36 26
     //
    
    37 27
     class MeekTransport {
    
    38
    -  constructor() {
    
    39
    -    this._inited = false;
    
    40
    -    this._meekClientProcess = null;
    
    41
    -    this._meekProxyType = null;
    
    42
    -    this._meekProxyAddress = null;
    
    43
    -    this._meekProxyPort = 0;
    
    44
    -    this._meekProxyUsername = null;
    
    45
    -    this._meekProxyPassword = null;
    
    46
    -  }
    
    28
    +  // These members are used by consumers to setup the proxy to do requests over
    
    29
    +  // meek. They are passed to newProxyInfoWithAuth.
    
    30
    +  proxyType = null;
    
    31
    +  proxyAddress = null;
    
    32
    +  proxyPort = 0;
    
    33
    +  proxyUsername = null;
    
    34
    +  proxyPassword = null;
    
    35
    +
    
    36
    +  #inited = false;
    
    37
    +  #meekClientProcess = null;
    
    47 38
     
    
    48 39
       // launches the meekprocess
    
    49 40
       async init() {
    
    50 41
         // ensure we haven't already init'd
    
    51
    -    if (this._inited) {
    
    42
    +    if (this.#inited) {
    
    52 43
           throw new Error("MeekTransport: Already initialized");
    
    53 44
         }
    
    54 45
     
    
    55
    -    // cleanup function for killing orphaned pt process
    
    56
    -    let onException = () => {};
    
    57 46
         try {
    
    58 47
           // figure out which pluggable transport to use
    
    59 48
           const supportedTransports = ["meek", "meek_lite"];
    
    60
    -      let transportPlugins = await TorProtocolService.readStringArraySetting(
    
    61
    -        TorConfigKeys.clientTransportPlugin
    
    49
    +      const proxy = (
    
    50
    +        await lazy.TorProviderBuilder.build().getPluggableTransports()
    
    51
    +      ).find(
    
    52
    +        pt =>
    
    53
    +          pt.type === "exec" &&
    
    54
    +          supportedTransports.some(t => pt.transports.includes(t))
    
    62 55
           );
    
    56
    +      if (!proxy) {
    
    57
    +        throw new Error("No supported transport found.");
    
    58
    +      }
    
    63 59
     
    
    64
    -      let { meekTransport, meekClientPath, meekClientArgs } = (() => {
    
    65
    -        for (const line of transportPlugins) {
    
    66
    -          let tokens = line.split(" ");
    
    67
    -          if (tokens.length > 2 && tokens[1] == "exec") {
    
    68
    -            let transportArray = tokens[0].split(",").map(aStr => aStr.trim());
    
    69
    -            let transport = transportArray.find(aTransport =>
    
    70
    -              supportedTransports.includes(aTransport)
    
    71
    -            );
    
    72
    -
    
    73
    -            if (transport != undefined) {
    
    74
    -              return {
    
    75
    -                meekTransport: transport,
    
    76
    -                meekClientPath: tokens[2],
    
    77
    -                meekClientArgs: tokens.slice(3),
    
    78
    -              };
    
    79
    -            }
    
    80
    -          }
    
    81
    -        }
    
    82
    -
    
    83
    -        return {
    
    84
    -          meekTransport: null,
    
    85
    -          meekClientPath: null,
    
    86
    -          meekClientArgs: null,
    
    87
    -        };
    
    88
    -      })();
    
    89
    -
    
    60
    +      const meekTransport = proxy.transports.find(t =>
    
    61
    +        supportedTransports.includes(t)
    
    62
    +      );
    
    90 63
           // Convert meek client path to absolute path if necessary
    
    91
    -      let meekWorkDir = TorLauncherUtil.getTorFile("pt-startup-dir", false);
    
    92
    -      if (TorLauncherUtil.isPathRelative(meekClientPath)) {
    
    93
    -        let meekPath = meekWorkDir.clone();
    
    94
    -        meekPath.appendRelativePath(meekClientPath);
    
    95
    -        meekClientPath = meekPath.path;
    
    64
    +      const meekWorkDir = lazy.TorLauncherUtil.getTorFile(
    
    65
    +        "pt-startup-dir",
    
    66
    +        false
    
    67
    +      );
    
    68
    +      if (lazy.TorLauncherUtil.isPathRelative(proxy.pathToBinary)) {
    
    69
    +        const meekPath = meekWorkDir.clone();
    
    70
    +        meekPath.appendRelativePath(proxy.pathToBinary);
    
    71
    +        proxy.pathToBinary = meekPath.path;
    
    96 72
           }
    
    97 73
     
    
    98 74
           // Construct the per-connection arguments.
    
    ... ... @@ -105,16 +81,13 @@ class MeekTransport {
    105 81
           //   First the "<Key>=<Value>" formatted arguments MUST be escaped,
    
    106 82
           //   such that all backslash, equal sign, and semicolon characters
    
    107 83
           //   are escaped with a backslash.
    
    108
    -      let escapeArgValue = aValue => {
    
    109
    -        if (!aValue) {
    
    110
    -          return "";
    
    111
    -        }
    
    112
    -
    
    113
    -        let rv = aValue.replace(/\\/g, "\\\\");
    
    114
    -        rv = rv.replace(/=/g, "\\=");
    
    115
    -        rv = rv.replace(/;/g, "\\;");
    
    116
    -        return rv;
    
    117
    -      };
    
    84
    +      const escapeArgValue = aValue =>
    
    85
    +        aValue
    
    86
    +          ? aValue
    
    87
    +              .replaceAll("\\", "\\\\")
    
    88
    +              .replaceAll("=", "\\=")
    
    89
    +              .replaceAll(";", "\\;")
    
    90
    +          : "";
    
    118 91
     
    
    119 92
           if (meekReflector) {
    
    120 93
             meekClientEscapedArgs += "url=";
    
    ... ... @@ -132,10 +105,10 @@ class MeekTransport {
    132 105
           }
    
    133 106
     
    
    134 107
           // Setup env and start meek process
    
    135
    -      let ptStateDir = TorLauncherUtil.getTorFile("tordatadir", false);
    
    108
    +      const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
    
    136 109
           ptStateDir.append("pt_state"); // Match what tor uses.
    
    137 110
     
    
    138
    -      let envAdditions = {
    
    111
    +      const envAdditions = {
    
    139 112
             TOR_PT_MANAGED_TRANSPORT_VER: "1",
    
    140 113
             TOR_PT_STATE_LOCATION: ptStateDir.path,
    
    141 114
             TOR_PT_EXIT_ON_STDIN_CLOSE: "1",
    
    ... ... @@ -145,9 +118,9 @@ class MeekTransport {
    145 118
             envAdditions.TOR_PT_PROXY = TorSettings.proxy.uri;
    
    146 119
           }
    
    147 120
     
    
    148
    -      let opts = {
    
    149
    -        command: meekClientPath,
    
    150
    -        arguments: meekClientArgs,
    
    121
    +      const opts = {
    
    122
    +        command: proxy.pathToBinary,
    
    123
    +        arguments: proxy.options.split(/s+/),
    
    151 124
             workdir: meekWorkDir.path,
    
    152 125
             environmentAppend: true,
    
    153 126
             environment: envAdditions,
    
    ... ... @@ -155,27 +128,23 @@ class MeekTransport {
    155 128
           };
    
    156 129
     
    
    157 130
           // Launch meek client
    
    158
    -      let meekClientProcess = await Subprocess.call(opts);
    
    159
    -      // kill our process if exception is thrown
    
    160
    -      onException = () => {
    
    161
    -        meekClientProcess.kill();
    
    162
    -      };
    
    131
    +      this.#meekClientProcess = await lazy.Subprocess.call(opts);
    
    163 132
     
    
    164 133
           // Callback chain for reading stderr
    
    165
    -      let stderrLogger = async () => {
    
    166
    -        if (this._meekClientProcess) {
    
    167
    -          let errString = await this._meekClientProcess.stderr.readString();
    
    168
    -          console.log(`MeekTransport: stderr => ${errString}`);
    
    169
    -          await stderrLogger();
    
    134
    +      const stderrLogger = async () => {
    
    135
    +        while (this.#meekClientProcess) {
    
    136
    +          const errString = await this.#meekClientProcess.stderr.readString();
    
    137
    +          if (errString) {
    
    138
    +            console.log(`MeekTransport: stderr => ${errString}`);
    
    139
    +          }
    
    170 140
             }
    
    171 141
           };
    
    172 142
           stderrLogger();
    
    173 143
     
    
    174 144
           // Read pt's stdout until terminal (CMETHODS DONE) is reached
    
    175 145
           // returns array of lines for parsing
    
    176
    -      let getInitLines = async (stdout = "") => {
    
    177
    -        let string = await meekClientProcess.stdout.readString();
    
    178
    -        stdout += string;
    
    146
    +      const getInitLines = async (stdout = "") => {
    
    147
    +        stdout += await this.#meekClientProcess.stdout.readString();
    
    179 148
     
    
    180 149
             // look for the final message
    
    181 150
             const CMETHODS_DONE = "CMETHODS DONE";
    
    ... ... @@ -188,20 +157,16 @@ class MeekTransport {
    188 157
           };
    
    189 158
     
    
    190 159
           // read our lines from pt's stdout
    
    191
    -      let meekInitLines = await getInitLines();
    
    160
    +      const meekInitLines = await getInitLines();
    
    192 161
           // tokenize our pt lines
    
    193
    -      let meekInitTokens = meekInitLines.map(line => {
    
    194
    -        let tokens = line.split(" ");
    
    162
    +      const meekInitTokens = meekInitLines.map(line => {
    
    163
    +        const tokens = line.split(" ");
    
    195 164
             return {
    
    196 165
               keyword: tokens[0],
    
    197 166
               args: tokens.slice(1),
    
    198 167
             };
    
    199 168
           });
    
    200 169
     
    
    201
    -      let meekProxyType = null;
    
    202
    -      let meekProxyAddr = null;
    
    203
    -      let meekProxyPort = 0;
    
    204
    -
    
    205 170
           // parse our pt tokens
    
    206 171
           for (const { keyword, args } of meekInitTokens) {
    
    207 172
             const argsJoined = args.join(" ");
    
    ... ... @@ -251,9 +216,9 @@ class MeekTransport {
    251 216
                 }
    
    252 217
     
    
    253 218
                 // convert proxy type to strings used by protocol-proxy-servce
    
    254
    -            meekProxyType = proxyType === "socks5" ? "socks" : "socks4";
    
    255
    -            meekProxyAddr = addr;
    
    256
    -            meekProxyPort = port;
    
    219
    +            this.proxyType = proxyType === "socks5" ? "socks" : "socks4";
    
    220
    +            this.proxyAddress = addr;
    
    221
    +            this.proxyPort = port;
    
    257 222
     
    
    258 223
                 break;
    
    259 224
               }
    
    ... ... @@ -278,49 +243,47 @@ class MeekTransport {
    278 243
             }
    
    279 244
           }
    
    280 245
     
    
    281
    -      this._meekClientProcess = meekClientProcess;
    
    282 246
           // register callback to cleanup on process exit
    
    283
    -      this._meekClientProcess.wait().then(exitObj => {
    
    284
    -        this._meekClientProcess = null;
    
    247
    +      this.#meekClientProcess.wait().then(exitObj => {
    
    248
    +        this.#meekClientProcess = null;
    
    285 249
             this.uninit();
    
    286 250
           });
    
    287 251
     
    
    288
    -      this._meekProxyType = meekProxyType;
    
    289
    -      this._meekProxyAddress = meekProxyAddr;
    
    290
    -      this._meekProxyPort = meekProxyPort;
    
    291
    -
    
    292 252
           // socks5
    
    293
    -      if (meekProxyType === "socks") {
    
    253
    +      if (this.proxyType === "socks") {
    
    294 254
             if (meekClientEscapedArgs.length <= 255) {
    
    295
    -          this._meekProxyUsername = meekClientEscapedArgs;
    
    296
    -          this._meekProxyPassword = "\x00";
    
    255
    +          this.proxyUsername = meekClientEscapedArgs;
    
    256
    +          this.proxyPassword = "\x00";
    
    297 257
             } else {
    
    298
    -          this._meekProxyUsername = meekClientEscapedArgs.substring(0, 255);
    
    299
    -          this._meekProxyPassword = meekClientEscapedArgs.substring(255);
    
    258
    +          this.proxyUsername = meekClientEscapedArgs.substring(0, 255);
    
    259
    +          this.proxyPassword = meekClientEscapedArgs.substring(255);
    
    300 260
             }
    
    301 261
             // socks4
    
    302 262
           } else {
    
    303
    -        this._meekProxyUsername = meekClientEscapedArgs;
    
    304
    -        this._meekProxyPassword = undefined;
    
    263
    +        this.proxyUsername = meekClientEscapedArgs;
    
    264
    +        this.proxyPassword = undefined;
    
    305 265
           }
    
    306 266
     
    
    307
    -      this._inited = true;
    
    267
    +      this.#inited = true;
    
    308 268
         } catch (ex) {
    
    309
    -      onException();
    
    269
    +      if (this.#meekClientProcess) {
    
    270
    +        this.#meekClientProcess.kill();
    
    271
    +        this.#meekClientProcess = null;
    
    272
    +      }
    
    310 273
           throw ex;
    
    311 274
         }
    
    312 275
       }
    
    313 276
     
    
    314 277
       async uninit() {
    
    315
    -    this._inited = false;
    
    316
    -
    
    317
    -    await this._meekClientProcess?.kill();
    
    318
    -    this._meekClientProcess = null;
    
    319
    -    this._meekProxyType = null;
    
    320
    -    this._meekProxyAddress = null;
    
    321
    -    this._meekProxyPort = 0;
    
    322
    -    this._meekProxyUsername = null;
    
    323
    -    this._meekProxyPassword = null;
    
    278
    +    this.#inited = false;
    
    279
    +
    
    280
    +    await this.#meekClientProcess?.kill();
    
    281
    +    this.#meekClientProcess = null;
    
    282
    +    this.proxyType = null;
    
    283
    +    this.proxyAddress = null;
    
    284
    +    this.proxyPort = 0;
    
    285
    +    this.proxyUsername = null;
    
    286
    +    this.proxyPassword = null;
    
    324 287
       }
    
    325 288
     }
    
    326 289
     
    
    ... ... @@ -328,21 +291,25 @@ class MeekTransport {
    328 291
     // Callback object with a cached promise for the returned Moat data
    
    329 292
     //
    
    330 293
     class MoatResponseListener {
    
    294
    +  #response = "";
    
    295
    +  #responsePromise;
    
    296
    +  #resolve;
    
    297
    +  #reject;
    
    331 298
       constructor() {
    
    332
    -    this._response = "";
    
    299
    +    this.#response = "";
    
    333 300
         // we need this promise here because await nsIHttpChannel::asyncOpen does
    
    334 301
         // not return only once the request is complete, it seems to return
    
    335 302
         // after it begins, so we have to get the result from this listener object.
    
    336 303
         // This promise is only resolved once onStopRequest is called
    
    337
    -    this._responsePromise = new Promise((resolve, reject) => {
    
    338
    -      this._resolve = resolve;
    
    339
    -      this._reject = reject;
    
    304
    +    this.#responsePromise = new Promise((resolve, reject) => {
    
    305
    +      this.#resolve = resolve;
    
    306
    +      this.#reject = reject;
    
    340 307
         });
    
    341 308
       }
    
    342 309
     
    
    343 310
       // callers wait on this for final response
    
    344 311
       response() {
    
    345
    -    return this._responsePromise;
    
    312
    +    return this.#responsePromise;
    
    346 313
       }
    
    347 314
     
    
    348 315
       // noop
    
    ... ... @@ -352,16 +319,17 @@ class MoatResponseListener {
    352 319
       onStopRequest(request, status) {
    
    353 320
         try {
    
    354 321
           if (!Components.isSuccessCode(status)) {
    
    355
    -        const errorMessage = TorLauncherUtil.getLocalizedStringForError(status);
    
    356
    -        this._reject(new Error(errorMessage));
    
    322
    +        const errorMessage =
    
    323
    +          lazy.TorLauncherUtil.getLocalizedStringForError(status);
    
    324
    +        this.#reject(new Error(errorMessage));
    
    357 325
           }
    
    358 326
           if (request.responseStatus != 200) {
    
    359
    -        this._reject(new Error(request.responseStatusText));
    
    327
    +        this.#reject(new Error(request.responseStatusText));
    
    360 328
           }
    
    361 329
         } catch (err) {
    
    362
    -      this._reject(err);
    
    330
    +      this.#reject(err);
    
    363 331
         }
    
    364
    -    this._resolve(this._response);
    
    332
    +    this.#resolve(this.#response);
    
    365 333
       }
    
    366 334
     
    
    367 335
       // read response data
    
    ... ... @@ -370,30 +338,32 @@ class MoatResponseListener {
    370 338
           "@mozilla.org/scriptableinputstream;1"
    
    371 339
         ].createInstance(Ci.nsIScriptableInputStream);
    
    372 340
         scriptableStream.init(stream);
    
    373
    -    this._response += scriptableStream.read(length);
    
    341
    +    this.#response += scriptableStream.read(length);
    
    374 342
       }
    
    375 343
     }
    
    376 344
     
    
    377 345
     class InternetTestResponseListener {
    
    346
    +  #promise;
    
    347
    +  #resolve;
    
    348
    +  #reject;
    
    378 349
       constructor() {
    
    379
    -    this._promise = new Promise((resolve, reject) => {
    
    380
    -      this._resolve = resolve;
    
    381
    -      this._reject = reject;
    
    350
    +    this.#promise = new Promise((resolve, reject) => {
    
    351
    +      this.#resolve = resolve;
    
    352
    +      this.#reject = reject;
    
    382 353
         });
    
    383 354
       }
    
    384 355
     
    
    385 356
       // callers wait on this for final response
    
    386 357
       get status() {
    
    387
    -    return this._promise;
    
    358
    +    return this.#promise;
    
    388 359
       }
    
    389 360
     
    
    390 361
       onStartRequest(request) {}
    
    391 362
     
    
    392 363
       // resolve or reject our Promise
    
    393 364
       onStopRequest(request, status) {
    
    394
    -    let statuses = {};
    
    395 365
         try {
    
    396
    -      statuses = {
    
    366
    +      const statuses = {
    
    397 367
             components: status,
    
    398 368
             successful: Components.isSuccessCode(status),
    
    399 369
           };
    
    ... ... @@ -408,56 +378,51 @@ class InternetTestResponseListener {
    408 378
               err
    
    409 379
             );
    
    410 380
           }
    
    381
    +      this.#resolve(statuses);
    
    411 382
         } catch (err) {
    
    412
    -      this._reject(err);
    
    383
    +      this.#reject(err);
    
    413 384
         }
    
    414
    -    this._resolve(statuses);
    
    415 385
       }
    
    416 386
     
    
    417 387
       onDataAvailable(request, stream, offset, length) {
    
    418
    -    //  We do not care of the actual data, as long as we have a successful
    
    388
    +    // We do not care of the actual data, as long as we have a successful
    
    419 389
         // connection
    
    420 390
       }
    
    421 391
     }
    
    422 392
     
    
    423 393
     // constructs the json objects and sends the request over moat
    
    424
    -class MoatRPC {
    
    425
    -  constructor() {
    
    426
    -    this._meekTransport = null;
    
    427
    -    this._inited = false;
    
    428
    -  }
    
    394
    +export class MoatRPC {
    
    395
    +  #inited = false;
    
    396
    +  #meekTransport = null;
    
    429 397
     
    
    430 398
       get inited() {
    
    431
    -    return this._inited;
    
    399
    +    return this.#inited;
    
    432 400
       }
    
    433 401
     
    
    434 402
       async init() {
    
    435
    -    if (this._inited) {
    
    403
    +    if (this.#inited) {
    
    436 404
           throw new Error("MoatRPC: Already initialized");
    
    437 405
         }
    
    438 406
     
    
    439 407
         let meekTransport = new MeekTransport();
    
    440 408
         await meekTransport.init();
    
    441
    -    this._meekTransport = meekTransport;
    
    442
    -    this._inited = true;
    
    409
    +    this.#meekTransport = meekTransport;
    
    410
    +    this.#inited = true;
    
    443 411
       }
    
    444 412
     
    
    445 413
       async uninit() {
    
    446
    -    await this._meekTransport?.uninit();
    
    447
    -    this._meekTransport = null;
    
    448
    -    this._inited = false;
    
    414
    +    await this.#meekTransport?.uninit();
    
    415
    +    this.#meekTransport = null;
    
    416
    +    this.#inited = false;
    
    449 417
       }
    
    450 418
     
    
    451
    -  _makeHttpHandler(uriString) {
    
    452
    -    if (!this._inited) {
    
    419
    +  #makeHttpHandler(uriString) {
    
    420
    +    if (!this.#inited) {
    
    453 421
           throw new Error("MoatRPC: Not initialized");
    
    454 422
         }
    
    455 423
     
    
    456
    -    const proxyType = this._meekTransport._meekProxyType;
    
    457
    -    const proxyAddress = this._meekTransport._meekProxyAddress;
    
    458
    -    const proxyPort = this._meekTransport._meekProxyPort;
    
    459
    -    const proxyUsername = this._meekTransport._meekProxyUsername;
    
    460
    -    const proxyPassword = this._meekTransport._meekProxyPassword;
    
    424
    +    const { proxyType, proxyAddress, proxyPort, proxyUsername, proxyPassword } =
    
    425
    +      this.#meekTransport;
    
    461 426
     
    
    462 427
         const proxyPS = Cc[
    
    463 428
           "@mozilla.org/network/protocol-proxy-service;1"
    
    ... ... @@ -511,11 +476,11 @@ class MoatRPC {
    511 476
         return ch;
    
    512 477
       }
    
    513 478
     
    
    514
    -  async _makeRequest(procedure, args) {
    
    479
    +  async #makeRequest(procedure, args) {
    
    515 480
         const procedureURIString = `${Services.prefs.getStringPref(
    
    516 481
           TorLauncherPrefs.moat_service
    
    517 482
         )}/${procedure}`;
    
    518
    -    const ch = this._makeHttpHandler(procedureURIString);
    
    483
    +    const ch = this.#makeHttpHandler(procedureURIString);
    
    519 484
     
    
    520 485
         // Arrange for the POST data to be sent.
    
    521 486
         const argsJson = JSON.stringify(args);
    
    ... ... @@ -544,7 +509,7 @@ class MoatRPC {
    544 509
         const uri = `${Services.prefs.getStringPref(
    
    545 510
           TorLauncherPrefs.moat_service
    
    546 511
         )}/circumvention/countries`;
    
    547
    -    const ch = this._makeHttpHandler(uri);
    
    512
    +    const ch = this.#makeHttpHandler(uri);
    
    548 513
         ch.requestMethod = "HEAD";
    
    549 514
     
    
    550 515
         const listener = new InternetTestResponseListener();
    
    ... ... @@ -582,7 +547,7 @@ class MoatRPC {
    582 547
               },
    
    583 548
             ],
    
    584 549
           };
    
    585
    -      const response = await this._makeRequest("fetch", args);
    
    550
    +      const response = await this.#makeRequest("fetch", args);
    
    586 551
           if ("errors" in response) {
    
    587 552
             const code = response.errors[0].code;
    
    588 553
             const detail = response.errors[0].detail;
    
    ... ... @@ -623,7 +588,7 @@ class MoatRPC {
    623 588
             },
    
    624 589
           ],
    
    625 590
         };
    
    626
    -    const response = await this._makeRequest("check", args);
    
    591
    +    const response = await this.#makeRequest("check", args);
    
    627 592
         if ("errors" in response) {
    
    628 593
           const code = response.errors[0].code;
    
    629 594
           const detail = response.errors[0].detail;
    
    ... ... @@ -642,7 +607,7 @@ class MoatRPC {
    642 607
     
    
    643 608
       // Convert received settings object to format used by TorSettings module
    
    644 609
       // In the event of error, just return null
    
    645
    -  _fixupSettings(settings) {
    
    610
    +  #fixupSettings(settings) {
    
    646 611
         try {
    
    647 612
           let retval = TorSettings.defaultSettings();
    
    648 613
           if ("bridges" in settings) {
    
    ... ... @@ -691,11 +656,11 @@ class MoatRPC {
    691 656
       // Converts a list of settings objects received from BridgeDB to a list of settings objects
    
    692 657
       // understood by the TorSettings module
    
    693 658
       // In the event of error, returns and empty list
    
    694
    -  _fixupSettingsList(settingsList) {
    
    659
    +  #fixupSettingsList(settingsList) {
    
    695 660
         try {
    
    696 661
           let retval = [];
    
    697 662
           for (let settings of settingsList) {
    
    698
    -        settings = this._fixupSettings(settings);
    
    663
    +        settings = this.#fixupSettings(settings);
    
    699 664
             if (settings != null) {
    
    700 665
               retval.push(settings);
    
    701 666
             }
    
    ... ... @@ -724,7 +689,7 @@ class MoatRPC {
    724 689
           transports: transports ? transports : [],
    
    725 690
           country,
    
    726 691
         };
    
    727
    -    const response = await this._makeRequest("circumvention/settings", args);
    
    692
    +    const response = await this.#makeRequest("circumvention/settings", args);
    
    728 693
         let settings = {};
    
    729 694
         if ("errors" in response) {
    
    730 695
           const code = response.errors[0].code;
    
    ... ... @@ -739,7 +704,7 @@ class MoatRPC {
    739 704
     
    
    740 705
           throw new Error(`MoatRPC: ${detail} (${code})`);
    
    741 706
         } else if ("settings" in response) {
    
    742
    -      settings.settings = this._fixupSettingsList(response.settings);
    
    707
    +      settings.settings = this.#fixupSettingsList(response.settings);
    
    743 708
         }
    
    744 709
         if ("country" in response) {
    
    745 710
           settings.country = response.country;
    
    ... ... @@ -753,7 +718,7 @@ class MoatRPC {
    753 718
       // for
    
    754 719
       async circumvention_countries() {
    
    755 720
         const args = {};
    
    756
    -    return this._makeRequest("circumvention/countries", args);
    
    721
    +    return this.#makeRequest("circumvention/countries", args);
    
    757 722
       }
    
    758 723
     
    
    759 724
       // Request a copy of the builtin bridges, takes the following parameters:
    
    ... ... @@ -766,7 +731,7 @@ class MoatRPC {
    766 731
         const args = {
    
    767 732
           transports: transports ? transports : [],
    
    768 733
         };
    
    769
    -    const response = await this._makeRequest("circumvention/builtin", args);
    
    734
    +    const response = await this.#makeRequest("circumvention/builtin", args);
    
    770 735
         if ("errors" in response) {
    
    771 736
           const code = response.errors[0].code;
    
    772 737
           const detail = response.errors[0].detail;
    
    ... ... @@ -791,13 +756,13 @@ class MoatRPC {
    791 756
         const args = {
    
    792 757
           transports: transports ? transports : [],
    
    793 758
         };
    
    794
    -    const response = await this._makeRequest("circumvention/defaults", args);
    
    759
    +    const response = await this.#makeRequest("circumvention/defaults", args);
    
    795 760
         if ("errors" in response) {
    
    796 761
           const code = response.errors[0].code;
    
    797 762
           const detail = response.errors[0].detail;
    
    798 763
           throw new Error(`MoatRPC: ${detail} (${code})`);
    
    799 764
         } else if ("settings" in response) {
    
    800
    -      return this._fixupSettingsList(response.settings);
    
    765
    +      return this.#fixupSettingsList(response.settings);
    
    801 766
         }
    
    802 767
         return [];
    
    803 768
       }
    

  • browser/modules/TorConnect.jsmbrowser/modules/TorConnect.sys.mjs
    1
    -"use strict";
    
    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/. */
    
    2 4
     
    
    3
    -var EXPORTED_SYMBOLS = [
    
    4
    -  "InternetStatus",
    
    5
    -  "TorConnect",
    
    6
    -  "TorConnectTopics",
    
    7
    -  "TorConnectState",
    
    8
    -];
    
    5
    +import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
    
    9 6
     
    
    10
    -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
    
    7
    +const lazy = {};
    
    11 8
     
    
    12
    -const { setTimeout, clearTimeout } = ChromeUtils.import(
    
    13
    -  "resource://gre/modules/Timer.jsm"
    
    14
    -);
    
    9
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    10
    +  MoatRPC: "resource:///modules/Moat.sys.mjs",
    
    11
    +  TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
    
    12
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    13
    +});
    
    15 14
     
    
    16
    -const { BrowserWindowTracker } = ChromeUtils.import(
    
    15
    +// TODO: Should we move this to the about:torconnect actor?
    
    16
    +ChromeUtils.defineModuleGetter(
    
    17
    +  lazy,
    
    18
    +  "BrowserWindowTracker",
    
    17 19
       "resource:///modules/BrowserWindowTracker.jsm"
    
    18 20
     );
    
    19 21
     
    
    20
    -const { TorMonitorService } = ChromeUtils.import(
    
    21
    -  "resource://gre/modules/TorMonitorService.jsm"
    
    22
    -);
    
    23
    -const { TorBootstrapRequest } = ChromeUtils.import(
    
    24
    -  "resource://gre/modules/TorBootstrapRequest.jsm"
    
    25
    -);
    
    26
    -
    
    27
    -const { TorSettings, TorSettingsTopics, TorBuiltinBridgeTypes } =
    
    28
    -  ChromeUtils.import("resource:///modules/TorSettings.jsm");
    
    22
    +import {
    
    23
    +  TorSettings,
    
    24
    +  TorSettingsTopics,
    
    25
    +  TorBuiltinBridgeTypes,
    
    26
    +} from "resource:///modules/TorSettings.sys.mjs";
    
    29 27
     
    
    30 28
     const { TorStrings } = ChromeUtils.import("resource:///modules/TorStrings.jsm");
    
    31 29
     
    
    32
    -const { MoatRPC } = ChromeUtils.import("resource:///modules/Moat.jsm");
    
    33
    -
    
    34 30
     const TorTopics = Object.freeze({
    
    35 31
       LogHasWarnOrErr: "TorLogHasWarnOrErr",
    
    36 32
       ProcessExited: "TorProcessExited",
    
    ... ... @@ -46,7 +42,7 @@ const TorConnectPrefs = Object.freeze({
    46 42
       allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
    
    47 43
     });
    
    48 44
     
    
    49
    -const TorConnectState = Object.freeze({
    
    45
    +export const TorConnectState = Object.freeze({
    
    50 46
       /* Our initial state */
    
    51 47
       Initial: "Initial",
    
    52 48
       /* In-between initial boot and bootstrapping, users can change tor network settings during this state */
    
    ... ... @@ -156,7 +152,7 @@ const TorConnectStateTransitions = Object.freeze(
    156 152
     );
    
    157 153
     
    
    158 154
     /* Topics Notified by the TorConnect module */
    
    159
    -const TorConnectTopics = Object.freeze({
    
    155
    +export const TorConnectTopics = Object.freeze({
    
    160 156
       StateChange: "torconnect:state-change",
    
    161 157
       BootstrapProgress: "torconnect:bootstrap-progress",
    
    162 158
       BootstrapComplete: "torconnect:bootstrap-complete",
    
    ... ... @@ -238,7 +234,7 @@ const debug_sleep = async ms => {
    238 234
       });
    
    239 235
     };
    
    240 236
     
    
    241
    -const InternetStatus = Object.freeze({
    
    237
    +export const InternetStatus = Object.freeze({
    
    242 238
       Unknown: -1,
    
    243 239
       Offline: 0,
    
    244 240
       Online: 1,
    
    ... ... @@ -302,7 +298,7 @@ class InternetTest {
    302 298
         // waiting both for the bootstrap, and for the Internet test.
    
    303 299
         // However, managing Moat with async/await is much easier as it avoids a
    
    304 300
         // callback hell, and it makes extra explicit that we are uniniting it.
    
    305
    -    const mrpc = new MoatRPC();
    
    301
    +    const mrpc = new lazy.MoatRPC();
    
    306 302
         let status = null;
    
    307 303
         let error = null;
    
    308 304
         try {
    
    ... ... @@ -340,7 +336,7 @@ class InternetTest {
    340 336
       }
    
    341 337
     }
    
    342 338
     
    
    343
    -const TorConnect = (() => {
    
    339
    +export const TorConnect = (() => {
    
    344 340
       let retval = {
    
    345 341
         _state: TorConnectState.Initial,
    
    346 342
         _bootstrapProgress: 0,
    
    ... ... @@ -459,7 +455,7 @@ const TorConnect = (() => {
    459 455
                     return;
    
    460 456
                   }
    
    461 457
     
    
    462
    -              const tbr = new TorBootstrapRequest();
    
    458
    +              const tbr = new lazy.TorBootstrapRequest();
    
    463 459
                   const internetTest = new InternetTest();
    
    464 460
                   let cancelled = false;
    
    465 461
     
    
    ... ... @@ -604,7 +600,7 @@ const TorConnect = (() => {
    604 600
     
    
    605 601
                   // lookup user's potential censorship circumvention settings from Moat service
    
    606 602
                   try {
    
    607
    -                this.mrpc = new MoatRPC();
    
    603
    +                this.mrpc = new lazy.MoatRPC();
    
    608 604
                     await this.mrpc.init();
    
    609 605
     
    
    610 606
                     if (this.transitioning) {
    
    ... ... @@ -678,7 +674,7 @@ const TorConnect = (() => {
    678 674
                         await TorSettings.applySettings();
    
    679 675
     
    
    680 676
                         // build out our bootstrap request
    
    681
    -                    const tbr = new TorBootstrapRequest();
    
    677
    +                    const tbr = new lazy.TorBootstrapRequest();
    
    682 678
                         tbr.onbootstrapstatus = (progress, status) => {
    
    683 679
                           TorConnect._updateBootstrapStatus(progress, status);
    
    684 680
                         };
    
    ... ... @@ -915,7 +911,7 @@ const TorConnect = (() => {
    915 911
          * @type {boolean}
    
    916 912
          */
    
    917 913
         get enabled() {
    
    918
    -      return TorMonitorService.ownsTorDaemon;
    
    914
    +      return lazy.TorProviderBuilder.build().ownsTorDaemon;
    
    919 915
         },
    
    920 916
     
    
    921 917
         get shouldShowTorConnect() {
    
    ... ... @@ -1053,7 +1049,7 @@ const TorConnect = (() => {
    1053 1049
             Further external commands and helper methods
    
    1054 1050
             */
    
    1055 1051
         openTorPreferences() {
    
    1056
    -      const win = BrowserWindowTracker.getTopWindow();
    
    1052
    +      const win = lazy.BrowserWindowTracker.getTopWindow();
    
    1057 1053
           win.switchToTabHavingURI("about:preferences#connection", true);
    
    1058 1054
         },
    
    1059 1055
     
    
    ... ... @@ -1073,7 +1069,7 @@ const TorConnect = (() => {
    1073 1069
          *   begin AutoBootstrapping, if possible.
    
    1074 1070
          */
    
    1075 1071
         openTorConnect(options) {
    
    1076
    -      const win = BrowserWindowTracker.getTopWindow();
    
    1072
    +      const win = lazy.BrowserWindowTracker.getTopWindow();
    
    1077 1073
           win.switchToTabHavingURI("about:torconnect", true, {
    
    1078 1074
             ignoreQueryString: true,
    
    1079 1075
           });
    
    ... ... @@ -1094,7 +1090,7 @@ const TorConnect = (() => {
    1094 1090
         },
    
    1095 1091
     
    
    1096 1092
         viewTorLogs() {
    
    1097
    -      const win = BrowserWindowTracker.getTopWindow();
    
    1093
    +      const win = lazy.BrowserWindowTracker.getTopWindow();
    
    1098 1094
           win.switchToTabHavingURI("about:preferences#connection-viewlogs", true);
    
    1099 1095
         },
    
    1100 1096
     
    
    ... ... @@ -1104,7 +1100,7 @@ const TorConnect = (() => {
    1104 1100
           if (this._countryCodes.length) {
    
    1105 1101
             return this._countryCodes;
    
    1106 1102
           }
    
    1107
    -      const mrpc = new MoatRPC();
    
    1103
    +      const mrpc = new lazy.MoatRPC();
    
    1108 1104
           try {
    
    1109 1105
             await mrpc.init();
    
    1110 1106
             this._countryCodes = await mrpc.circumvention_countries();
    

  • browser/modules/TorSettings.jsmbrowser/modules/TorSettings.sys.mjs
    1
    -"use strict";
    
    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/. */
    
    2 4
     
    
    3
    -var EXPORTED_SYMBOLS = [
    
    4
    -  "TorSettings",
    
    5
    -  "TorSettingsTopics",
    
    6
    -  "TorSettingsData",
    
    7
    -  "TorBridgeSource",
    
    8
    -  "TorBuiltinBridgeTypes",
    
    9
    -  "TorProxyType",
    
    10
    -];
    
    5
    +const lazy = {};
    
    11 6
     
    
    12
    -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
    
    13
    -
    
    14
    -const { TorMonitorService } = ChromeUtils.import(
    
    15
    -  "resource://gre/modules/TorMonitorService.jsm"
    
    16
    -);
    
    17
    -const { TorProtocolService } = ChromeUtils.import(
    
    18
    -  "resource://gre/modules/TorProtocolService.jsm"
    
    19
    -);
    
    20
    -
    
    21
    -/* tor-launcher observer topics */
    
    22
    -const TorTopics = Object.freeze({
    
    23
    -  ProcessIsReady: "TorProcessIsReady",
    
    7
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    8
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    9
    +  TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    24 10
     });
    
    25 11
     
    
    26 12
     /* TorSettings observer topics */
    
    27
    -const TorSettingsTopics = Object.freeze({
    
    13
    +export const TorSettingsTopics = Object.freeze({
    
    28 14
       Ready: "torsettings:ready",
    
    29 15
       SettingChanged: "torsettings:setting-changed",
    
    30 16
     });
    
    31 17
     
    
    32 18
     /* TorSettings observer data (for SettingChanged topic) */
    
    33
    -const TorSettingsData = Object.freeze({
    
    19
    +export const TorSettingsData = Object.freeze({
    
    34 20
       QuickStartEnabled: "torsettings:quickstart_enabled",
    
    35 21
     });
    
    36 22
     
    
    ... ... @@ -98,21 +84,21 @@ const TorConfigKeys = Object.freeze({
    98 84
       clientTransportPlugin: "ClientTransportPlugin",
    
    99 85
     });
    
    100 86
     
    
    101
    -const TorBridgeSource = Object.freeze({
    
    87
    +export const TorBridgeSource = Object.freeze({
    
    102 88
       Invalid: -1,
    
    103 89
       BuiltIn: 0,
    
    104 90
       BridgeDB: 1,
    
    105 91
       UserProvided: 2,
    
    106 92
     });
    
    107 93
     
    
    108
    -const TorProxyType = Object.freeze({
    
    94
    +export const TorProxyType = Object.freeze({
    
    109 95
       Invalid: -1,
    
    110 96
       Socks4: 0,
    
    111 97
       Socks5: 1,
    
    112 98
       HTTPS: 2,
    
    113 99
     });
    
    114 100
     
    
    115
    -const TorBuiltinBridgeTypes = Object.freeze(
    
    101
    +export const TorBuiltinBridgeTypes = Object.freeze(
    
    116 102
       (() => {
    
    117 103
         const bridgeListBranch = Services.prefs.getBranch(
    
    118 104
           TorLauncherPrefs.default_bridge
    
    ... ... @@ -254,7 +240,7 @@ const arrayCopy = function (array) {
    254 240
     
    
    255 241
     /* TorSettings module */
    
    256 242
     
    
    257
    -const TorSettings = (() => {
    
    243
    +export const TorSettings = (() => {
    
    258 244
       const self = {
    
    259 245
         _settings: null,
    
    260 246
     
    
    ... ... @@ -288,7 +274,8 @@ const TorSettings = (() => {
    288 274
     
    
    289 275
         /* load or init our settings, and register observers */
    
    290 276
         init() {
    
    291
    -      if (TorMonitorService.ownsTorDaemon) {
    
    277
    +      const provider = lazy.TorProviderBuilder.build();
    
    278
    +      if (provider.ownsTorDaemon) {
    
    292 279
             // if the settings branch exists, load settings from prefs
    
    293 280
             if (Services.prefs.getBoolPref(TorSettingsPrefs.enabled, false)) {
    
    294 281
               this.loadFromPrefs();
    
    ... ... @@ -296,9 +283,9 @@ const TorSettings = (() => {
    296 283
               // otherwise load defaults
    
    297 284
               this._settings = this.defaultSettings();
    
    298 285
             }
    
    299
    -        Services.obs.addObserver(this, TorTopics.ProcessIsReady);
    
    286
    +        Services.obs.addObserver(this, lazy.TorProviderTopics.ProcessIsReady);
    
    300 287
     
    
    301
    -        if (TorMonitorService.isRunning) {
    
    288
    +        if (provider.isRunning) {
    
    302 289
               this.handleProcessReady();
    
    303 290
             }
    
    304 291
           }
    
    ... ... @@ -309,8 +296,11 @@ const TorSettings = (() => {
    309 296
           console.log(`TorSettings: Observed ${topic}`);
    
    310 297
     
    
    311 298
           switch (topic) {
    
    312
    -        case TorTopics.ProcessIsReady:
    
    313
    -          Services.obs.removeObserver(this, TorTopics.ProcessIsReady);
    
    299
    +        case lazy.TorProviderTopics.ProcessIsReady:
    
    300
    +          Services.obs.removeObserver(
    
    301
    +            this,
    
    302
    +            lazy.TorProviderTopics.ProcessIsReady
    
    303
    +          );
    
    314 304
               await this.handleProcessReady();
    
    315 305
               break;
    
    316 306
           }
    
    ... ... @@ -569,7 +559,7 @@ const TorSettings = (() => {
    569 559
           }
    
    570 560
     
    
    571 561
           /* Push to Tor */
    
    572
    -      await TorProtocolService.writeSettings(settingsMap);
    
    562
    +      await lazy.TorProviderBuilder.build().writeSettings(settingsMap);
    
    573 563
     
    
    574 564
           return this;
    
    575 565
         },
    

  • browser/modules/moz.build
    ... ... @@ -123,7 +123,7 @@ XPCSHELL_TESTS_MANIFESTS += ["test/unit/xpcshell.ini"]
    123 123
     EXTRA_JS_MODULES += [
    
    124 124
         "AboutNewTab.jsm",
    
    125 125
         "AsyncTabSwitcher.jsm",
    
    126
    -    "BridgeDB.jsm",
    
    126
    +    "BridgeDB.sys.mjs",
    
    127 127
         "BrowserUIUtils.jsm",
    
    128 128
         "BrowserUsageTelemetry.jsm",
    
    129 129
         "BrowserWindowTracker.jsm",
    
    ... ... @@ -135,7 +135,7 @@ EXTRA_JS_MODULES += [
    135 135
         "FeatureCallout.sys.mjs",
    
    136 136
         "HomePage.jsm",
    
    137 137
         "LaterRun.jsm",
    
    138
    -    'Moat.jsm',
    
    138
    +    "Moat.sys.mjs",
    
    139 139
         "NewTabPagePreloading.jsm",
    
    140 140
         "OpenInTabsUtils.jsm",
    
    141 141
         "PageActions.jsm",
    
    ... ... @@ -149,8 +149,8 @@ EXTRA_JS_MODULES += [
    149 149
         "SitePermissions.sys.mjs",
    
    150 150
         "TabsList.jsm",
    
    151 151
         "TabUnloader.jsm",
    
    152
    -    "TorConnect.jsm",
    
    153
    -    "TorSettings.jsm",
    
    152
    +    "TorConnect.sys.mjs",
    
    153
    +    "TorSettings.sys.mjs",
    
    154 154
         "TorStrings.jsm",
    
    155 155
         "TransientPrefs.jsm",
    
    156 156
         "URILoadingHelper.sys.mjs",
    

  • toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
    1 1
     import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
    
    2 2
     
    
    3
    -import { TorProtocolService } from "resource://gre/modules/TorProtocolService.sys.mjs";
    
    3
    +import { TorProviderBuilder } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
    
    4 4
     import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs";
    
    5 5
     
    
    6 6
     /* tor-launcher observer topics */
    
    ... ... @@ -13,19 +13,23 @@ export const TorTopics = Object.freeze({
    13 13
     // modeled after XMLHttpRequest
    
    14 14
     // nicely encapsulates the observer register/unregister logic
    
    15 15
     export class TorBootstrapRequest {
    
    16
    +  // number of ms to wait before we abandon the bootstrap attempt
    
    17
    +  // a value of 0 implies we never wait
    
    18
    +  timeout = 0;
    
    19
    +
    
    20
    +  // callbacks for bootstrap process status updates
    
    21
    +  onbootstrapstatus = (progress, status) => {};
    
    22
    +  onbootstrapcomplete = () => {};
    
    23
    +  onbootstraperror = (message, details) => {};
    
    24
    +
    
    25
    +  // internal resolve() method for bootstrap
    
    26
    +  #bootstrapPromiseResolve = null;
    
    27
    +  #bootstrapPromise = null;
    
    28
    +  #timeoutID = null;
    
    29
    +  #provider = null;
    
    30
    +
    
    16 31
       constructor() {
    
    17
    -    // number of ms to wait before we abandon the bootstrap attempt
    
    18
    -    // a value of 0 implies we never wait
    
    19
    -    this.timeout = 0;
    
    20
    -    // callbacks for bootstrap process status updates
    
    21
    -    this.onbootstrapstatus = (progress, status) => {};
    
    22
    -    this.onbootstrapcomplete = () => {};
    
    23
    -    this.onbootstraperror = (message, details) => {};
    
    24
    -
    
    25
    -    // internal resolve() method for bootstrap
    
    26
    -    this._bootstrapPromiseResolve = null;
    
    27
    -    this._bootstrapPromise = null;
    
    28
    -    this._timeoutID = null;
    
    32
    +    this.#provider = TorProviderBuilder.build();
    
    29 33
       }
    
    30 34
     
    
    31 35
       observe(subject, topic, data) {
    
    ... ... @@ -41,15 +45,16 @@ export class TorBootstrapRequest {
    41 45
               if (this.onbootstrapcomplete) {
    
    42 46
                 this.onbootstrapcomplete();
    
    43 47
               }
    
    44
    -          this._bootstrapPromiseResolve(true);
    
    45
    -          clearTimeout(this._timeoutID);
    
    48
    +          this.#bootstrapPromiseResolve(true);
    
    49
    +          clearTimeout(this.#timeoutID);
    
    50
    +          this.#timeoutID = null;
    
    46 51
             }
    
    47 52
     
    
    48 53
             break;
    
    49 54
           }
    
    50 55
           case TorTopics.BootstrapError: {
    
    51 56
             console.info("TorBootstrapRequest: observerd TorBootstrapError", obj);
    
    52
    -        this._stop(obj?.message, obj?.details);
    
    57
    +        this.#stop(obj?.message, obj?.details);
    
    53 58
             break;
    
    54 59
           }
    
    55 60
         }
    
    ... ... @@ -57,12 +62,12 @@ export class TorBootstrapRequest {
    57 62
     
    
    58 63
       // resolves 'true' if bootstrap succeeds, false otherwise
    
    59 64
       bootstrap() {
    
    60
    -    if (this._bootstrapPromise) {
    
    61
    -      return this._bootstrapPromise;
    
    65
    +    if (this.#bootstrapPromise) {
    
    66
    +      return this.#bootstrapPromise;
    
    62 67
         }
    
    63 68
     
    
    64
    -    this._bootstrapPromise = new Promise((resolve, reject) => {
    
    65
    -      this._bootstrapPromiseResolve = resolve;
    
    69
    +    this.#bootstrapPromise = new Promise((resolve, reject) => {
    
    70
    +      this.#bootstrapPromiseResolve = resolve;
    
    66 71
     
    
    67 72
           // register ourselves to listen for bootstrap events
    
    68 73
           Services.obs.addObserver(this, TorTopics.BootstrapStatus);
    
    ... ... @@ -70,10 +75,10 @@ export class TorBootstrapRequest {
    70 75
     
    
    71 76
           // optionally cancel bootstrap after a given timeout
    
    72 77
           if (this.timeout > 0) {
    
    73
    -        this._timeoutID = setTimeout(async () => {
    
    74
    -          this._timeoutID = null;
    
    78
    +        this.#timeoutID = setTimeout(async () => {
    
    79
    +          this.#timeoutID = null;
    
    75 80
               // TODO: Translate, if really used
    
    76
    -          await this._stop(
    
    81
    +          await this.#stop(
    
    77 82
                 "Tor Bootstrap process timed out",
    
    78 83
                 `Bootstrap attempt abandoned after waiting ${this.timeout} ms`
    
    79 84
               );
    
    ... ... @@ -81,38 +86,45 @@ export class TorBootstrapRequest {
    81 86
           }
    
    82 87
     
    
    83 88
           // wait for bootstrapping to begin and maybe handle error
    
    84
    -      TorProtocolService.connect().catch(err => {
    
    85
    -        this._stop(err.message, "");
    
    89
    +      this.#provider.connect().catch(err => {
    
    90
    +        this.#stop(err.message, "");
    
    86 91
           });
    
    87 92
         }).finally(() => {
    
    88 93
           // and remove ourselves once bootstrap is resolved
    
    89 94
           Services.obs.removeObserver(this, TorTopics.BootstrapStatus);
    
    90 95
           Services.obs.removeObserver(this, TorTopics.BootstrapError);
    
    91
    -      this._bootstrapPromise = null;
    
    96
    +      this.#bootstrapPromise = null;
    
    92 97
         });
    
    93 98
     
    
    94
    -    return this._bootstrapPromise;
    
    99
    +    return this.#bootstrapPromise;
    
    95 100
       }
    
    96 101
     
    
    97 102
       async cancel() {
    
    98
    -    await this._stop();
    
    103
    +    await this.#stop();
    
    99 104
       }
    
    100 105
     
    
    101 106
       // Internal implementation. Do not use directly, but call cancel, instead.
    
    102
    -  async _stop(message, details) {
    
    107
    +  async #stop(message, details) {
    
    103 108
         // first stop our bootstrap timeout before handling the error
    
    104
    -    if (this._timeoutID !== null) {
    
    105
    -      clearTimeout(this._timeoutID);
    
    106
    -      this._timeoutID = null;
    
    109
    +    if (this.#timeoutID !== null) {
    
    110
    +      clearTimeout(this.#timeoutID);
    
    111
    +      this.#timeoutID = null;
    
    107 112
         }
    
    108 113
     
    
    109
    -    // stopBootstrap never throws
    
    110
    -    await TorProtocolService.stopBootstrap();
    
    114
    +    try {
    
    115
    +      await this.#provider.stopBootstrap();
    
    116
    +    } catch (e) {
    
    117
    +      console.error("Failed to stop the bootstrap.", e);
    
    118
    +      if (!message) {
    
    119
    +        message = e.message;
    
    120
    +        details = "";
    
    121
    +      }
    
    122
    +    }
    
    111 123
     
    
    112 124
         if (this.onbootstraperror && message) {
    
    113 125
           this.onbootstraperror(message, details);
    
    114 126
         }
    
    115 127
     
    
    116
    -    this._bootstrapPromiseResolve(false);
    
    128
    +    this.#bootstrapPromiseResolve(false);
    
    117 129
       }
    
    118 130
     }

  • toolkit/components/tor-launcher/TorControlPort.sys.mjs
    ... ... @@ -274,6 +274,44 @@ class AsyncSocket {
    274 274
      * the command
    
    275 275
      */
    
    276 276
     
    
    277
    +/**
    
    278
    + * @typedef {object} Bridge
    
    279
    + * @property {string} transport The transport of the bridge, or vanilla if not
    
    280
    + * specified.
    
    281
    + * @property {string} addr The IP address and port of the bridge
    
    282
    + * @property {string} id The fingerprint of the bridge
    
    283
    + * @property {string} args Optional arguments passed to the bridge
    
    284
    + */
    
    285
    +/**
    
    286
    + * @typedef {object} PTInfo The information about a pluggable transport
    
    287
    + * @property {string[]} transports An array with all the transports supported by
    
    288
    + * this configuration.
    
    289
    + * @property {string} type Either socks4, socks5 or exec
    
    290
    + * @property {string} [ip] The IP address of the proxy (only for socks4 and
    
    291
    + * socks5)
    
    292
    + * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
    
    293
    + * @property {string} [pathToBinary] Path to the binary that is run (only for
    
    294
    + * exec)
    
    295
    + * @property {string} [options] Optional options passed to the binary (only for
    
    296
    + * exec)
    
    297
    + */
    
    298
    +/**
    
    299
    + * @typedef {object} OnionAuthKeyInfo
    
    300
    + * @property {string} address The address of the onion service
    
    301
    + * @property {string} typeAndKey Onion service key and type of key, as
    
    302
    + * `type:base64-private-key`
    
    303
    + * @property {string} Flags Additional flags, such as Permanent
    
    304
    + */
    
    305
    +/**
    
    306
    + * @callback EventFilterCallback
    
    307
    + * @param {any} data Either a raw string, or already parsed data
    
    308
    + * @returns {boolean}
    
    309
    + */
    
    310
    +/**
    
    311
    + * @callback EventCallback
    
    312
    + * @param {any} data Either a raw string, or already parsed data
    
    313
    + */
    
    314
    +
    
    277 315
     class TorError extends Error {
    
    278 316
       constructor(command, reply) {
    
    279 317
         super(`${command} -> ${reply}`);
    
    ... ... @@ -584,319 +622,6 @@ class ControlSocket {
    584 622
       }
    
    585 623
     }
    
    586 624
     
    
    587
    -// ## utils
    
    588
    -// A namespace for utility functions
    
    589
    -let utils = {};
    
    590
    -
    
    591
    -// __utils.identity(x)__.
    
    592
    -// Returns its argument unchanged.
    
    593
    -utils.identity = function (x) {
    
    594
    -  return x;
    
    595
    -};
    
    596
    -
    
    597
    -// __utils.capture(string, regex)__.
    
    598
    -// Takes a string and returns an array of capture items, where regex must have a single
    
    599
    -// capturing group and use the suffix /.../g to specify a global search.
    
    600
    -utils.capture = function (string, regex) {
    
    601
    -  let matches = [];
    
    602
    -  // Special trick to use string.replace for capturing multiple matches.
    
    603
    -  string.replace(regex, function (a, captured) {
    
    604
    -    matches.push(captured);
    
    605
    -  });
    
    606
    -  return matches;
    
    607
    -};
    
    608
    -
    
    609
    -// __utils.extractor(regex)__.
    
    610
    -// Returns a function that takes a string and returns an array of regex matches. The
    
    611
    -// regex must use the suffix /.../g to specify a global search.
    
    612
    -utils.extractor = function (regex) {
    
    613
    -  return function (text) {
    
    614
    -    return utils.capture(text, regex);
    
    615
    -  };
    
    616
    -};
    
    617
    -
    
    618
    -// __utils.splitLines(string)__.
    
    619
    -// Splits a string into an array of strings, each corresponding to a line.
    
    620
    -utils.splitLines = function (string) {
    
    621
    -  return string.split(/\r?\n/);
    
    622
    -};
    
    623
    -
    
    624
    -// __utils.splitAtSpaces(string)__.
    
    625
    -// Splits a string into chunks between spaces. Does not split at spaces
    
    626
    -// inside pairs of quotation marks.
    
    627
    -utils.splitAtSpaces = utils.extractor(/((\S*?"(.*?)")+\S*|\S+)/g);
    
    628
    -
    
    629
    -// __utils.splitAtFirst(string, regex)__.
    
    630
    -// Splits a string at the first instance of regex match. If no match is
    
    631
    -// found, returns the whole string.
    
    632
    -utils.splitAtFirst = function (string, regex) {
    
    633
    -  let match = string.match(regex);
    
    634
    -  return match
    
    635
    -    ? [
    
    636
    -        string.substring(0, match.index),
    
    637
    -        string.substring(match.index + match[0].length),
    
    638
    -      ]
    
    639
    -    : string;
    
    640
    -};
    
    641
    -
    
    642
    -// __utils.splitAtEquals(string)__.
    
    643
    -// Splits a string into chunks between equals. Does not split at equals
    
    644
    -// inside pairs of quotation marks.
    
    645
    -utils.splitAtEquals = utils.extractor(/(([^=]*?"(.*?)")+[^=]*|[^=]+)/g);
    
    646
    -
    
    647
    -// __utils.mergeObjects(arrayOfObjects)__.
    
    648
    -// Takes an array of objects like [{"a":"b"},{"c":"d"}] and merges to a single object.
    
    649
    -// Pure function.
    
    650
    -utils.mergeObjects = function (arrayOfObjects) {
    
    651
    -  let result = {};
    
    652
    -  for (let obj of arrayOfObjects) {
    
    653
    -    for (let key in obj) {
    
    654
    -      result[key] = obj[key];
    
    655
    -    }
    
    656
    -  }
    
    657
    -  return result;
    
    658
    -};
    
    659
    -
    
    660
    -// __utils.listMapData(parameterString, listNames)__.
    
    661
    -// Takes a list of parameters separated by spaces, of which the first several are
    
    662
    -// unnamed, and the remainder are named, in the form `NAME=VALUE`. Apply listNames
    
    663
    -// to the unnamed parameters, and combine them in a map with the named parameters.
    
    664
    -// Example: `40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH`
    
    665
    -//
    
    666
    -//     utils.listMapData("40 FAILED 0 95.78.59.36:80 REASON=CANT_ATTACH",
    
    667
    -//                       ["streamID", "event", "circuitID", "IP"])
    
    668
    -//     // --> {"streamID" : "40", "event" : "FAILED", "circuitID" : "0",
    
    669
    -//     //      "address" : "95.78.59.36:80", "REASON" : "CANT_ATTACH"}"
    
    670
    -utils.listMapData = function (parameterString, listNames) {
    
    671
    -  // Split out the space-delimited parameters.
    
    672
    -  let parameters = utils.splitAtSpaces(parameterString),
    
    673
    -    dataMap = {};
    
    674
    -  // Assign listNames to the first n = listNames.length parameters.
    
    675
    -  for (let i = 0; i < listNames.length; ++i) {
    
    676
    -    dataMap[listNames[i]] = parameters[i];
    
    677
    -  }
    
    678
    -  // Read key-value pairs and copy these to the dataMap.
    
    679
    -  for (let i = listNames.length; i < parameters.length; ++i) {
    
    680
    -    let [key, value] = utils.splitAtEquals(parameters[i]);
    
    681
    -    if (key && value) {
    
    682
    -      dataMap[key] = value;
    
    683
    -    }
    
    684
    -  }
    
    685
    -  return dataMap;
    
    686
    -};
    
    687
    -
    
    688
    -// ## info
    
    689
    -// A namespace for functions related to tor's GETINFO and GETCONF command.
    
    690
    -let info = {};
    
    691
    -
    
    692
    -// __info.keyValueStringsFromMessage(messageText)__.
    
    693
    -// Takes a message (text) response to GETINFO or GETCONF and provides
    
    694
    -// a series of key-value strings, which are either multiline (with a `250+` prefix):
    
    695
    -//
    
    696
    -//     250+config/defaults=
    
    697
    -//     AccountingMax "0 bytes"
    
    698
    -//     AllowDotExit "0"
    
    699
    -//     .
    
    700
    -//
    
    701
    -// or single-line (with a `250-` or `250 ` prefix):
    
    702
    -//
    
    703
    -//     250-version=0.2.6.0-alpha-dev (git-b408125288ad6943)
    
    704
    -info.keyValueStringsFromMessage = utils.extractor(
    
    705
    -  /^(250\+[\s\S]+?^\.|250[- ].+?)$/gim
    
    706
    -);
    
    707
    -
    
    708
    -// __info.applyPerLine(transformFunction)__.
    
    709
    -// Returns a function that splits text into lines,
    
    710
    -// and applies transformFunction to each line.
    
    711
    -info.applyPerLine = function (transformFunction) {
    
    712
    -  return function (text) {
    
    713
    -    return utils.splitLines(text.trim()).map(transformFunction);
    
    714
    -  };
    
    715
    -};
    
    716
    -
    
    717
    -// __info.routerStatusParser(valueString)__.
    
    718
    -// Parses a router status entry as, described in
    
    719
    -// https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt
    
    720
    -// (search for "router status entry")
    
    721
    -info.routerStatusParser = function (valueString) {
    
    722
    -  let lines = utils.splitLines(valueString),
    
    723
    -    objects = [];
    
    724
    -  for (let line of lines) {
    
    725
    -    // Drop first character and grab data following it.
    
    726
    -    let myData = line.substring(2),
    
    727
    -      // Accumulate more maps with data, depending on the first character in the line.
    
    728
    -      dataFun = {
    
    729
    -        r: data =>
    
    730
    -          utils.listMapData(data, [
    
    731
    -            "nickname",
    
    732
    -            "identity",
    
    733
    -            "digest",
    
    734
    -            "publicationDate",
    
    735
    -            "publicationTime",
    
    736
    -            "IP",
    
    737
    -            "ORPort",
    
    738
    -            "DirPort",
    
    739
    -          ]),
    
    740
    -        a: data => ({ IPv6: data }),
    
    741
    -        s: data => ({ statusFlags: utils.splitAtSpaces(data) }),
    
    742
    -        v: data => ({ version: data }),
    
    743
    -        w: data => utils.listMapData(data, []),
    
    744
    -        p: data => ({ portList: data.split(",") }),
    
    745
    -      }[line.charAt(0)];
    
    746
    -    if (dataFun !== undefined) {
    
    747
    -      objects.push(dataFun(myData));
    
    748
    -    }
    
    749
    -  }
    
    750
    -  return utils.mergeObjects(objects);
    
    751
    -};
    
    752
    -
    
    753
    -// __info.circuitStatusParser(line)__.
    
    754
    -// Parse the output of a circuit status line.
    
    755
    -info.circuitStatusParser = function (line) {
    
    756
    -  let data = utils.listMapData(line, ["id", "status", "circuit"]),
    
    757
    -    circuit = data.circuit;
    
    758
    -  // Parse out the individual circuit IDs and names.
    
    759
    -  if (circuit) {
    
    760
    -    data.circuit = circuit.split(",").map(function (x) {
    
    761
    -      return x.split(/~|=/);
    
    762
    -    });
    
    763
    -  }
    
    764
    -  return data;
    
    765
    -};
    
    766
    -
    
    767
    -// __info.streamStatusParser(line)__.
    
    768
    -// Parse the output of a stream status line.
    
    769
    -info.streamStatusParser = function (text) {
    
    770
    -  return utils.listMapData(text, [
    
    771
    -    "StreamID",
    
    772
    -    "StreamStatus",
    
    773
    -    "CircuitID",
    
    774
    -    "Target",
    
    775
    -  ]);
    
    776
    -};
    
    777
    -
    
    778
    -// TODO: fix this parsing logic to handle bridgeLine correctly
    
    779
    -// fingerprint/id is an optional parameter
    
    780
    -// __info.bridgeParser(bridgeLine)__.
    
    781
    -// Takes a single line from a `getconf bridge` result and returns
    
    782
    -// a map containing the bridge's type, address, and ID.
    
    783
    -info.bridgeParser = function (bridgeLine) {
    
    784
    -  let result = {},
    
    785
    -    tokens = bridgeLine.split(/\s+/);
    
    786
    -  // First check if we have a "vanilla" bridge:
    
    787
    -  if (tokens[0].match(/^\d+\.\d+\.\d+\.\d+/)) {
    
    788
    -    result.type = "vanilla";
    
    789
    -    [result.address, result.ID] = tokens;
    
    790
    -    // Several bridge types have a similar format:
    
    791
    -  } else {
    
    792
    -    result.type = tokens[0];
    
    793
    -    if (
    
    794
    -      [
    
    795
    -        "flashproxy",
    
    796
    -        "fte",
    
    797
    -        "meek",
    
    798
    -        "meek_lite",
    
    799
    -        "obfs3",
    
    800
    -        "obfs4",
    
    801
    -        "scramblesuit",
    
    802
    -        "snowflake",
    
    803
    -      ].includes(result.type)
    
    804
    -    ) {
    
    805
    -      [result.address, result.ID] = tokens.slice(1);
    
    806
    -    }
    
    807
    -  }
    
    808
    -  return result.type ? result : null;
    
    809
    -};
    
    810
    -
    
    811
    -// __info.parsers__.
    
    812
    -// A map of GETINFO and GETCONF keys to parsing function, which convert
    
    813
    -// result strings to JavaScript data.
    
    814
    -info.parsers = {
    
    815
    -  "ns/id/": info.routerStatusParser,
    
    816
    -  "ip-to-country/": utils.identity,
    
    817
    -  "circuit-status": info.applyPerLine(info.circuitStatusParser),
    
    818
    -  bridge: info.bridgeParser,
    
    819
    -  // Currently unused parsers:
    
    820
    -  //  "ns/name/" : info.routerStatusParser,
    
    821
    -  //  "stream-status" : info.applyPerLine(info.streamStatusParser),
    
    822
    -  //  "version" : utils.identity,
    
    823
    -  //  "config-file" : utils.identity,
    
    824
    -};
    
    825
    -
    
    826
    -// __info.getParser(key)__.
    
    827
    -// Takes a key and determines the parser function that should be used to
    
    828
    -// convert its corresponding valueString to JavaScript data.
    
    829
    -info.getParser = function (key) {
    
    830
    -  return (
    
    831
    -    info.parsers[key] ||
    
    832
    -    info.parsers[key.substring(0, key.lastIndexOf("/") + 1)]
    
    833
    -  );
    
    834
    -};
    
    835
    -
    
    836
    -// __info.stringToValue(string)__.
    
    837
    -// Converts a key-value string as from GETINFO or GETCONF to a value.
    
    838
    -info.stringToValue = function (string) {
    
    839
    -  // key should look something like `250+circuit-status=` or `250-circuit-status=...`
    
    840
    -  // or `250 circuit-status=...`
    
    841
    -  let matchForKey = string.match(/^250[ +-](.+?)=/),
    
    842
    -    key = matchForKey ? matchForKey[1] : null;
    
    843
    -  if (key === null) {
    
    844
    -    return null;
    
    845
    -  }
    
    846
    -  // matchResult finds a single-line result for `250-` or `250 `,
    
    847
    -  // or a multi-line one for `250+`.
    
    848
    -  let matchResult =
    
    849
    -      string.match(/^250[ -].+?=(.*)$/) ||
    
    850
    -      string.match(/^250\+.+?=([\s\S]*?)^\.$/m),
    
    851
    -    // Retrieve the captured group (the text of the value in the key-value pair)
    
    852
    -    valueString = matchResult ? matchResult[1] : null,
    
    853
    -    // Get the parser function for the key found.
    
    854
    -    parse = info.getParser(key.toLowerCase());
    
    855
    -  if (parse === undefined) {
    
    856
    -    throw new Error("No parser found for '" + key + "'");
    
    857
    -  }
    
    858
    -  // Return value produced by the parser.
    
    859
    -  return parse(valueString);
    
    860
    -};
    
    861
    -
    
    862
    -/**
    
    863
    - * @typedef {object} Bridge
    
    864
    - * @property {string} transport The transport of the bridge, or vanilla if not
    
    865
    - * specified.
    
    866
    - * @property {string} addr The IP address and port of the bridge
    
    867
    - * @property {string} id The fingerprint of the bridge
    
    868
    - * @property {string} args Optional arguments passed to the bridge
    
    869
    - */
    
    870
    -/**
    
    871
    - * @typedef {object} PTInfo The information about a pluggable transport
    
    872
    - * @property {string[]} transports An array with all the transports supported by
    
    873
    - * this configuration.
    
    874
    - * @property {string} type Either socks4, socks5 or exec
    
    875
    - * @property {string} [ip] The IP address of the proxy (only for socks4 and
    
    876
    - * socks5)
    
    877
    - * @property {integer} [port] The port of the proxy (only for socks4 and socks5)
    
    878
    - * @property {string} [pathToBinary] Path to the binary that is run (only for
    
    879
    - * exec)
    
    880
    - * @property {string} [options] Optional options passed to the binary (only for
    
    881
    - * exec)
    
    882
    - */
    
    883
    -/**
    
    884
    - * @typedef {object} OnionAuthKeyInfo
    
    885
    - * @property {string} address The address of the onion service
    
    886
    - * @property {string} typeAndKey Onion service key and type of key, as
    
    887
    - * `type:base64-private-key`
    
    888
    - * @property {string} Flags Additional flags, such as Permanent
    
    889
    - */
    
    890
    -/**
    
    891
    - * @callback EventFilterCallback
    
    892
    - * @param {any} data Either a raw string, or already parsed data
    
    893
    - * @returns {boolean}
    
    894
    - */
    
    895
    -/**
    
    896
    - * @callback EventCallback
    
    897
    - * @param {any} data Either a raw string, or already parsed data
    
    898
    - */
    
    899
    -
    
    900 625
     class TorController {
    
    901 626
       /**
    
    902 627
        * The control socket
    
    ... ... @@ -905,16 +630,6 @@ class TorController {
    905 630
        */
    
    906 631
       #socket;
    
    907 632
     
    
    908
    -  /**
    
    909
    -   * A map of EVENT keys to parsing functions, which convert result strings to
    
    910
    -   * JavaScript data.
    
    911
    -   */
    
    912
    -  #eventParsers = {
    
    913
    -    stream: info.streamStatusParser,
    
    914
    -    // Currently unused:
    
    915
    -    // "circ" : info.circuitStatusParser,
    
    916
    -  };
    
    917
    -
    
    918 633
       /**
    
    919 634
        * Builds a new TorController.
    
    920 635
        *
    
    ... ... @@ -981,18 +696,6 @@ class TorController {
    981 696
         await this.#sendCommandSimple(`authenticate ${password || ""}`);
    
    982 697
       }
    
    983 698
     
    
    984
    -  /**
    
    985
    -   * Sends a GETINFO for a single key.
    
    986
    -   *
    
    987
    -   * @param {string} key The key to get value for
    
    988
    -   * @returns {any} The return value depends on the requested key
    
    989
    -   */
    
    990
    -  async getInfo(key) {
    
    991
    -    this.#expectString(key, "key");
    
    992
    -    const response = await this.sendCommand(`getinfo ${key}`);
    
    993
    -    return this.#getMultipleResponseValues(response)[0];
    
    994
    -  }
    
    995
    -
    
    996 699
       /**
    
    997 700
        * Sends a GETINFO for a single key.
    
    998 701
        * control-spec.txt says "one ReplyLine is sent for each requested value", so,
    
    ... ... @@ -1054,9 +757,7 @@ class TorController {
    1054 757
         const addresses = [v4[5]];
    
    1055 758
         // a address:port
    
    1056 759
         // dir-spec.txt also states only the first one should be taken
    
    1057
    -    // TODO: The consumers do not care about the port or the square brackets
    
    1058
    -    // either. Remove them when integrating this function with the rest
    
    1059
    -    const v6 = reply.match(/^a\s+(\[[0-9a-fA-F:]+\]:[0-9]{1,5})$/m);
    
    760
    +    const v6 = reply.match(/^a\s+\[([0-9a-fA-F:]+)\]:\d{1,5}$/m);
    
    1060 761
         if (v6) {
    
    1061 762
           addresses.push(v6[1]);
    
    1062 763
         }
    
    ... ... @@ -1091,23 +792,6 @@ class TorController {
    1091 792
     
    
    1092 793
       // Configuration
    
    1093 794
     
    
    1094
    -  /**
    
    1095
    -   * Sends a GETCONF for a single key.
    
    1096
    -   * GETCONF with a single argument returns results with one or more lines that
    
    1097
    -   * look like `250[- ]key=value`.
    
    1098
    -   * Any GETCONF lines that contain a single keyword only are currently dropped.
    
    1099
    -   * So we can use similar parsing to that for getInfo.
    
    1100
    -   *
    
    1101
    -   * @param {string} key The key to get value for
    
    1102
    -   * @returns {any} A parsed config value (it depends if a parser is known)
    
    1103
    -   */
    
    1104
    -  async getConf(key) {
    
    1105
    -    this.#expectString(key, "key");
    
    1106
    -    return this.#getMultipleResponseValues(
    
    1107
    -      await this.sendCommand(`getconf ${key}`)
    
    1108
    -    );
    
    1109
    -  }
    
    1110
    -
    
    1111 795
       /**
    
    1112 796
        * Sends a GETCONF for a single key.
    
    1113 797
        * The function could be easily generalized to get multiple keys at once, but
    
    ... ... @@ -1264,12 +948,14 @@ class TorController {
    1264 948
           // TODO: Change the consumer and make the fields more consistent with what
    
    1265 949
           // we get (e.g., separate key and type, and use a boolen for permanent).
    
    1266 950
           const info = {
    
    1267
    -        hsAddress: match.groups.HSAddress,
    
    1268
    -        typeAndKey: `${match.groups.KeyType}:${match.groups.PrivateKeyBlob}`,
    
    951
    +        address: match.groups.HSAddress,
    
    952
    +        keyType: match.groups.KeyType,
    
    953
    +        keyBlob: match.groups.PrivateKeyBlob,
    
    954
    +        flags: [],
    
    1269 955
           };
    
    1270 956
           const maybeFlags = match.groups.other?.match(/Flags=(\S+)/);
    
    1271 957
           if (maybeFlags) {
    
    1272
    -        info.Flags = maybeFlags[1];
    
    958
    +        info.flags = maybeFlags[1].split(",");
    
    1273 959
           }
    
    1274 960
           return info;
    
    1275 961
         });
    
    ... ... @@ -1369,28 +1055,12 @@ class TorController {
    1369 1055
        * first.
    
    1370 1056
        *
    
    1371 1057
        * @param {string} type The event type to catch
    
    1372
    -   * @param {EventFilterCallback?} filter An optional callback to filter
    
    1373
    -   * events for which the callback will be called. If null, all events will be
    
    1374
    -   * passed.
    
    1375 1058
        * @param {EventCallback} callback The callback that will handle the event
    
    1376
    -   * @param {boolean} raw Tell whether to ignore the data parser, even if
    
    1377
    -   * supported
    
    1378 1059
        */
    
    1379
    -  watchEvent(type, filter, callback, raw = false) {
    
    1060
    +  watchEvent(type, callback) {
    
    1380 1061
         this.#expectString(type, "type");
    
    1381 1062
         const start = `650 ${type}`;
    
    1382
    -    this.#socket.addNotificationCallback(new RegExp(`^${start}`), message => {
    
    1383
    -      // Remove also the initial text
    
    1384
    -      const dataText = message.substring(start.length + 1);
    
    1385
    -      const parser = this.#eventParsers[type.toLowerCase()];
    
    1386
    -      const data = dataText && parser ? parser(dataText) : null;
    
    1387
    -      // FIXME: This is the original code, but we risk of not filtering on the
    
    1388
    -      // data, if we ask for raw data (which we always do at the moment, but we
    
    1389
    -      // do not use a filter either...)
    
    1390
    -      if (filter === null || filter(data)) {
    
    1391
    -        callback(data && !raw ? data : message);
    
    1392
    -      }
    
    1393
    -    });
    
    1063
    +    this.#socket.addNotificationCallback(new RegExp(`^${start}`), callback);
    
    1394 1064
       }
    
    1395 1065
     
    
    1396 1066
       // Other helpers
    
    ... ... @@ -1453,19 +1123,6 @@ class TorController {
    1453 1123
           )
    
    1454 1124
         );
    
    1455 1125
       }
    
    1456
    -
    
    1457
    -  /**
    
    1458
    -   * Process multiple responses to a GETINFO or GETCONF request.
    
    1459
    -   *
    
    1460
    -   * @param {string} message The message to process
    
    1461
    -   * @returns {object[]} The keys depend on the message
    
    1462
    -   */
    
    1463
    -  #getMultipleResponseValues(message) {
    
    1464
    -    return info
    
    1465
    -      .keyValueStringsFromMessage(message)
    
    1466
    -      .map(info.stringToValue)
    
    1467
    -      .filter(x => x);
    
    1468
    -  }
    
    1469 1126
     }
    
    1470 1127
     
    
    1471 1128
     const controlPortInfo = {};
    

  • toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
    ... ... @@ -12,6 +12,11 @@ import {
    12 12
     
    
    13 13
     const lazy = {};
    
    14 14
     
    
    15
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    16
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    17
    +  TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    18
    +});
    
    19
    +
    
    15 20
     XPCOMUtils.defineLazyServiceGetters(lazy, {
    
    16 21
       ProtocolProxyService: [
    
    17 22
         "@mozilla.org/network/protocol-proxy-service;1",
    
    ... ... @@ -19,11 +24,6 @@ XPCOMUtils.defineLazyServiceGetters(lazy, {
    19 24
       ],
    
    20 25
     });
    
    21 26
     
    
    22
    -ChromeUtils.defineESModuleGetters(lazy, {
    
    23
    -  TorMonitorTopics: "resource://gre/modules/TorMonitorService.sys.mjs",
    
    24
    -  TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
    
    25
    -});
    
    26
    -
    
    27 27
     const logger = new ConsoleAPI({
    
    28 28
       prefix: "TorDomainIsolator",
    
    29 29
       maxLogLevel: "warn",
    
    ... ... @@ -143,7 +143,7 @@ class TorDomainIsolatorImpl {
    143 143
     
    
    144 144
         Services.prefs.addObserver(NON_TOR_PROXY_PREF, this);
    
    145 145
         Services.obs.addObserver(this, NEW_IDENTITY_TOPIC);
    
    146
    -    Services.obs.addObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
    
    146
    +    Services.obs.addObserver(this, lazy.TorProviderTopics.StreamSucceeded);
    
    147 147
     
    
    148 148
         this.#cleanupIntervalId = setInterval(
    
    149 149
           this.#clearKnownCircuits.bind(this),
    
    ... ... @@ -158,7 +158,7 @@ class TorDomainIsolatorImpl {
    158 158
       uninit() {
    
    159 159
         Services.prefs.removeObserver(NON_TOR_PROXY_PREF, this);
    
    160 160
         Services.obs.removeObserver(this, NEW_IDENTITY_TOPIC);
    
    161
    -    Services.obs.removeObserver(this, lazy.TorMonitorTopics.StreamSucceeded);
    
    161
    +    Services.obs.removeObserver(this, lazy.TorProviderTopics.StreamSucceeded);
    
    162 162
         clearInterval(this.#cleanupIntervalId);
    
    163 163
         this.#cleanupIntervalId = null;
    
    164 164
         this.clearIsolation();
    
    ... ... @@ -257,12 +257,12 @@ class TorDomainIsolatorImpl {
    257 257
           );
    
    258 258
           this.clearIsolation();
    
    259 259
           try {
    
    260
    -        await lazy.TorProtocolService.newnym();
    
    260
    +        await lazy.TorProviderBuilder.build().newnym();
    
    261 261
           } catch (e) {
    
    262 262
             logger.error("Could not send the newnym command", e);
    
    263 263
             // TODO: What UX to use here? See tor-browser#41708
    
    264 264
           }
    
    265
    -    } else if (topic === lazy.TorMonitorTopics.StreamSucceeded) {
    
    265
    +    } else if (topic === lazy.TorProviderTopics.StreamSucceeded) {
    
    266 266
           const { username, password, circuit } = subject.wrappedJSObject;
    
    267 267
           this.#updateCircuit(username, password, circuit);
    
    268 268
         }
    
    ... ... @@ -553,7 +553,7 @@ class TorDomainIsolatorImpl {
    553 553
     
    
    554 554
         data = await Promise.all(
    
    555 555
           circuit.map(fingerprint =>
    
    556
    -        lazy.TorProtocolService.getNodeInfo(fingerprint)
    
    556
    +        lazy.TorProviderBuilder.build().getNodeInfo(fingerprint)
    
    557 557
           )
    
    558 558
         );
    
    559 559
         this.#knownCircuits.set(id, data);
    

  • toolkit/components/tor-launcher/TorMonitorService.sys.mjs deleted
    1
    -// Copyright (c) 2022, The Tor Project, Inc.
    
    2
    -
    
    3
    -import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
    
    4
    -
    
    5
    -const lazy = {};
    
    6
    -ChromeUtils.defineESModuleGetters(lazy, {
    
    7
    -  TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
    
    8
    -});
    
    9
    -
    
    10
    -export const TorMonitorTopics = Object.freeze({
    
    11
    -  BridgeChanged: TorProviderTopics.BridgeChanged,
    
    12
    -  StreamSucceeded: TorProviderTopics.StreamSucceeded,
    
    13
    -});
    
    14
    -
    
    15
    -/**
    
    16
    - * This service monitors an existing Tor instance, or starts one, if needed, and
    
    17
    - * then starts monitoring it.
    
    18
    - *
    
    19
    - * This is the service which should be queried to know information about the
    
    20
    - * status of the bootstrap, the logs, etc...
    
    21
    - */
    
    22
    -export const TorMonitorService = {
    
    23
    -  get currentBridge() {
    
    24
    -    return lazy.TorProtocolService.currentBridge;
    
    25
    -  },
    
    26
    -
    
    27
    -  get ownsTorDaemon() {
    
    28
    -    return lazy.TorProtocolService.ownsTorDaemon;
    
    29
    -  },
    
    30
    -
    
    31
    -  get isRunning() {
    
    32
    -    return lazy.TorProtocolService.isRunning;
    
    33
    -  },
    
    34
    -
    
    35
    -  get isBootstrapDone() {
    
    36
    -    return lazy.TorProtocolService.isBootstrapDone;
    
    37
    -  },
    
    38
    -
    
    39
    -  getLog() {
    
    40
    -    return lazy.TorProtocolService.getLog();
    
    41
    -  },
    
    42
    -};

  • toolkit/components/tor-launcher/TorParsers.sys.mjs
    ... ... @@ -269,11 +269,14 @@ export const TorParsers = Object.freeze({
    269 269
       },
    
    270 270
     
    
    271 271
       parseBridgeLine(line) {
    
    272
    +    if (!line) {
    
    273
    +      return null;
    
    274
    +    }
    
    272 275
         const re =
    
    273 276
           /\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
    
    274 277
         const match = re.exec(line);
    
    275 278
         if (!match) {
    
    276
    -      throw new Error("Invalid bridge line.");
    
    279
    +      throw new Error(`Invalid bridge line: ${line}.`);
    
    277 280
         }
    
    278 281
         const bridge = match.groups;
    
    279 282
         if (!bridge.transport) {
    

  • toolkit/components/tor-launcher/TorProtocolService.sys.mjstoolkit/components/tor-launcher/TorProvider.sys.mjs
    1
    -// Copyright (c) 2021, The Tor Project, Inc.
    
    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/. */
    
    2 4
     
    
    3 5
     import { setTimeout, clearTimeout } from "resource://gre/modules/Timer.sys.mjs";
    
    4 6
     import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
    
    ... ... @@ -11,7 +13,6 @@ import {
    11 13
     import { TorProviderTopics } from "resource://gre/modules/TorProviderBuilder.sys.mjs";
    
    12 14
     
    
    13 15
     const lazy = {};
    
    14
    -
    
    15 16
     ChromeUtils.defineESModuleGetters(lazy, {
    
    16 17
       controller: "resource://gre/modules/TorControlPort.sys.mjs",
    
    17 18
       configureControlPortModule: "resource://gre/modules/TorControlPort.sys.mjs",
    
    ... ... @@ -21,7 +22,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
    21 22
     
    
    22 23
     const logger = new ConsoleAPI({
    
    23 24
       maxLogLevel: "warn",
    
    24
    -  prefix: "TorProtocolService",
    
    25
    +  maxLogLevelPref: "browser.tor_provider.log_level",
    
    26
    +  prefix: "TorProvider",
    
    25 27
     });
    
    26 28
     
    
    27 29
     /**
    
    ... ... @@ -70,7 +72,7 @@ const ControlConnTimings = Object.freeze({
    70 72
      * It can start a new tor instance, or connect to an existing one.
    
    71 73
      * In the former case, it also takes its ownership by default.
    
    72 74
      */
    
    73
    -class TorProvider {
    
    75
    +export class TorProvider {
    
    74 76
       #inited = false;
    
    75 77
     
    
    76 78
       // Maintain a map of tor settings set by Tor Browser so that we don't
    
    ... ... @@ -85,7 +87,6 @@ class TorProvider {
    85 87
       #SOCKSPortInfo = null; // An object that contains ipcFile, host, port.
    
    86 88
     
    
    87 89
       #controlConnection = null; // This is cached and reused.
    
    88
    -  #connectionQueue = [];
    
    89 90
     
    
    90 91
       // Public methods
    
    91 92
     
    
    ... ... @@ -123,39 +124,34 @@ class TorProvider {
    123 124
       // takes a Map containing tor settings
    
    124 125
       // throws on error
    
    125 126
       async writeSettings(aSettingsObj) {
    
    127
    +    const entries =
    
    128
    +      aSettingsObj instanceof Map
    
    129
    +        ? Array.from(aSettingsObj.entries())
    
    130
    +        : Object.entries(aSettingsObj);
    
    126 131
         // only write settings that have changed
    
    127
    -    const newSettings = Array.from(aSettingsObj).filter(([setting, value]) => {
    
    128
    -      // make sure we have valid data here
    
    129
    -      this.#assertValidSetting(setting, value);
    
    130
    -
    
    132
    +    const newSettings = entries.filter(([setting, value]) => {
    
    131 133
           if (!this.#settingsCache.has(setting)) {
    
    132 134
             // no cached setting, so write
    
    133 135
             return true;
    
    134 136
           }
    
    135 137
     
    
    136 138
           const cachedValue = this.#settingsCache.get(setting);
    
    137
    -      if (value === cachedValue) {
    
    138
    -        return false;
    
    139
    -      } else if (Array.isArray(value) && Array.isArray(cachedValue)) {
    
    140
    -        // compare arrays member-wise
    
    141
    -        if (value.length !== cachedValue.length) {
    
    142
    -          return true;
    
    143
    -        }
    
    144
    -        for (let i = 0; i < value.length; i++) {
    
    145
    -          if (value[i] !== cachedValue[i]) {
    
    146
    -            return true;
    
    147
    -          }
    
    148
    -        }
    
    149
    -        return false;
    
    139
    +      // Arrays are the only special case for which === could fail.
    
    140
    +      // The other values we accept (strings, booleans, numbers, null and
    
    141
    +      // undefined) work correctly with ===.
    
    142
    +      if (Array.isArray(value) && Array.isArray(cachedValue)) {
    
    143
    +        return (
    
    144
    +          value.length !== cachedValue.length ||
    
    145
    +          value.some((val, idx) => val !== cachedValue[idx])
    
    146
    +        );
    
    150 147
           }
    
    151
    -      // some other different values
    
    152
    -      return true;
    
    148
    +      return value !== cachedValue;
    
    153 149
         });
    
    154 150
     
    
    155 151
         // only write if new setting to save
    
    156 152
         if (newSettings.length) {
    
    157
    -      const settingsObject = Object.fromEntries(newSettings);
    
    158
    -      await this.setConfWithReply(settingsObject);
    
    153
    +      const conn = await this.#getConnection();
    
    154
    +      await conn.setConf(Object.fromEntries(newSettings));
    
    159 155
     
    
    160 156
           // save settings to cache after successfully writing to Tor
    
    161 157
           for (const [setting, value] of newSettings) {
    
    ... ... @@ -164,23 +160,15 @@ class TorProvider {
    164 160
         }
    
    165 161
       }
    
    166 162
     
    
    167
    -  async readStringArraySetting(aSetting) {
    
    168
    -    const value = await this.#readSetting(aSetting);
    
    169
    -    this.#settingsCache.set(aSetting, value);
    
    170
    -    return value;
    
    171
    -  }
    
    172
    -
    
    173 163
       // writes current tor settings to disk
    
    174 164
       async flushSettings() {
    
    175
    -    await this.sendCommand("SAVECONF");
    
    165
    +    const conn = await this.#getConnection();
    
    166
    +    await conn.flushSettings();
    
    176 167
       }
    
    177 168
     
    
    178 169
       async connect() {
    
    179
    -    const kTorConfKeyDisableNetwork = "DisableNetwork";
    
    180
    -    const settings = {};
    
    181
    -    settings[kTorConfKeyDisableNetwork] = false;
    
    182
    -    await this.setConfWithReply(settings);
    
    183
    -    await this.sendCommand("SAVECONF");
    
    170
    +    const conn = await this.#getConnection();
    
    171
    +    await conn.setNetworkEnabled(true);
    
    184 172
         this.clearBootstrapError();
    
    185 173
         this.retrieveBootstrapStatus();
    
    186 174
       }
    
    ... ... @@ -188,12 +176,8 @@ class TorProvider {
    188 176
       async stopBootstrap() {
    
    189 177
         // Tell tor to disable use of the network; this should stop the bootstrap
    
    190 178
         // process.
    
    191
    -    try {
    
    192
    -      const settings = { DisableNetwork: true };
    
    193
    -      await this.setConfWithReply(settings);
    
    194
    -    } catch (e) {
    
    195
    -      logger.error("Error stopping bootstrap", e);
    
    196
    -    }
    
    179
    +    const conn = await this.#getConnection();
    
    180
    +    await conn.setNetworkEnabled(false);
    
    197 181
         // We are not interested in waiting for this, nor in **catching its error**,
    
    198 182
         // so we do not await this. We just want to be notified when the bootstrap
    
    199 183
         // status is actually updated through observers.
    
    ... ... @@ -201,28 +185,31 @@ class TorProvider {
    201 185
       }
    
    202 186
     
    
    203 187
       async newnym() {
    
    204
    -    return this.sendCommand("SIGNAL NEWNYM");
    
    188
    +    const conn = await this.#getConnection();
    
    189
    +    await conn.newnym();
    
    205 190
       }
    
    206 191
     
    
    207 192
       // Ask tor which ports it is listening to for SOCKS connections.
    
    208 193
       // At the moment this is used only in TorCheckService.
    
    209 194
       async getSocksListeners() {
    
    210
    -    const cmd = "GETINFO";
    
    211
    -    const keyword = "net/listeners/socks";
    
    212
    -    const response = await this.sendCommand(cmd, keyword);
    
    213
    -    return TorParsers.parseReply(cmd, keyword, response);
    
    195
    +    const conn = await this.#getConnection();
    
    196
    +    return conn.getSocksListeners();
    
    214 197
       }
    
    215 198
     
    
    216 199
       async getBridges() {
    
    200
    +    const conn = await this.#getConnection();
    
    217 201
         // Ideally, we would not need this function, because we should be the one
    
    218 202
         // setting them with TorSettings. However, TorSettings is not notified of
    
    219 203
         // change of settings. So, asking tor directly with the control connection
    
    220 204
         // is the most reliable way of getting the configured bridges, at the
    
    221 205
         // moment. Also, we are using this for the circuit display, which should
    
    222 206
         // work also when we are not configuring the tor daemon, but just using it.
    
    223
    -    return this.#withConnection(conn => {
    
    224
    -      return conn.getConf("bridge");
    
    225
    -    });
    
    207
    +    return conn.getBridges();
    
    208
    +  }
    
    209
    +
    
    210
    +  async getPluggableTransports() {
    
    211
    +    const conn = await this.#getConnection();
    
    212
    +    return conn.getPluggableTransports();
    
    226 213
       }
    
    227 214
     
    
    228 215
       /**
    
    ... ... @@ -232,68 +219,55 @@ class TorProvider {
    232 219
        * @returns {Promise<NodeData>}
    
    233 220
        */
    
    234 221
       async getNodeInfo(id) {
    
    235
    -    return this.#withConnection(async conn => {
    
    236
    -      const node = {
    
    237
    -        fingerprint: id,
    
    238
    -        ipAddrs: [],
    
    239
    -        bridgeType: null,
    
    240
    -        regionCode: null,
    
    241
    -      };
    
    242
    -      const bridge = (await conn.getConf("bridge"))?.find(
    
    243
    -        foundBridge => foundBridge.ID?.toUpperCase() === id.toUpperCase()
    
    244
    -      );
    
    245
    -      const addrRe = /^\[?([^\]]+)\]?:\d+$/;
    
    246
    -      if (bridge) {
    
    247
    -        node.bridgeType = bridge.type ?? "";
    
    248
    -        // Attempt to get an IP address from bridge address string.
    
    249
    -        const ip = bridge.address.match(addrRe)?.[1];
    
    250
    -        if (ip && !ip.startsWith("0.")) {
    
    251
    -          node.ipAddrs.push(ip);
    
    252
    -        }
    
    253
    -      } else {
    
    254
    -        // Either dealing with a relay, or a bridge whose fingerprint is not
    
    255
    -        // saved in torrc.
    
    256
    -        const info = await conn.getInfo(`ns/id/${id}`);
    
    257
    -        if (info.IP && !info.IP.startsWith("0.")) {
    
    258
    -          node.ipAddrs.push(info.IP);
    
    259
    -        }
    
    260
    -        const ip6 = info.IPv6?.match(addrRe)?.[1];
    
    261
    -        if (ip6) {
    
    262
    -          node.ipAddrs.push(ip6);
    
    263
    -        }
    
    222
    +    const conn = await this.#getConnection();
    
    223
    +    const node = {
    
    224
    +      fingerprint: id,
    
    225
    +      ipAddrs: [],
    
    226
    +      bridgeType: null,
    
    227
    +      regionCode: null,
    
    228
    +    };
    
    229
    +    const bridge = (await conn.getBridges())?.find(
    
    230
    +      foundBridge => foundBridge.id?.toUpperCase() === id.toUpperCase()
    
    231
    +    );
    
    232
    +    if (bridge) {
    
    233
    +      node.bridgeType = bridge.transport ?? "";
    
    234
    +      // Attempt to get an IP address from bridge address string.
    
    235
    +      const ip = bridge.addr.match(/^\[?([^\]]+)\]?:\d+$/)?.[1];
    
    236
    +      if (ip && !ip.startsWith("0.")) {
    
    237
    +        node.ipAddrs.push(ip);
    
    264 238
           }
    
    265
    -      if (node.ipAddrs.length) {
    
    266
    -        // Get the country code for the node's IP address.
    
    267
    -        let regionCode;
    
    268
    -        try {
    
    269
    -          // Expect a 2-letter ISO3166-1 code, which should also be a valid
    
    270
    -          // BCP47 Region subtag.
    
    271
    -          regionCode = await conn.getInfo("ip-to-country/" + node.ipAddrs[0]);
    
    272
    -        } catch {}
    
    239
    +    } else {
    
    240
    +      node.ipAddrs = await conn.getNodeAddresses(id);
    
    241
    +    }
    
    242
    +    if (node.ipAddrs.length) {
    
    243
    +      // Get the country code for the node's IP address.
    
    244
    +      try {
    
    245
    +        // Expect a 2-letter ISO3166-1 code, which should also be a valid
    
    246
    +        // BCP47 Region subtag.
    
    247
    +        const regionCode = await conn.getIPCountry(node.ipAddrs[0]);
    
    273 248
             if (regionCode && regionCode !== "??") {
    
    274 249
               node.regionCode = regionCode.toUpperCase();
    
    275 250
             }
    
    251
    +      } catch (e) {
    
    252
    +        logger.warn(`Cannot get a country for IP ${node.ipAddrs[0]}`, e);
    
    276 253
           }
    
    277
    -      return node;
    
    278
    -    });
    
    254
    +    }
    
    255
    +    return node;
    
    279 256
       }
    
    280 257
     
    
    281
    -  async onionAuthAdd(hsAddress, b64PrivateKey, isPermanent) {
    
    282
    -    return this.#withConnection(conn => {
    
    283
    -      return conn.onionAuthAdd(hsAddress, b64PrivateKey, isPermanent);
    
    284
    -    });
    
    258
    +  async onionAuthAdd(address, b64PrivateKey, isPermanent) {
    
    259
    +    const conn = await this.#getConnection();
    
    260
    +    return conn.onionAuthAdd(address, b64PrivateKey, isPermanent);
    
    285 261
       }
    
    286 262
     
    
    287
    -  async onionAuthRemove(hsAddress) {
    
    288
    -    return this.#withConnection(conn => {
    
    289
    -      return conn.onionAuthRemove(hsAddress);
    
    290
    -    });
    
    263
    +  async onionAuthRemove(address) {
    
    264
    +    const conn = await this.#getConnection();
    
    265
    +    return conn.onionAuthRemove(address);
    
    291 266
       }
    
    292 267
     
    
    293 268
       async onionAuthViewKeys() {
    
    294
    -    return this.#withConnection(conn => {
    
    295
    -      return conn.onionAuthViewKeys();
    
    296
    -    });
    
    269
    +    const conn = await this.#getConnection();
    
    270
    +    return conn.onionAuthViewKeys();
    
    297 271
       }
    
    298 272
     
    
    299 273
       // TODO: transform the following 4 functions in getters.
    
    ... ... @@ -333,106 +307,6 @@ class TorProvider {
    333 307
         return this.#SOCKSPortInfo;
    
    334 308
       }
    
    335 309
     
    
    336
    -  // Public, but called only internally
    
    337
    -
    
    338
    -  // Executes a command on the control port.
    
    339
    -  // Return a reply object or null if a fatal error occurs.
    
    340
    -  async sendCommand(cmd, args) {
    
    341
    -    const maxTimeout = 1000;
    
    342
    -    let leftConnAttempts = 5;
    
    343
    -    let timeout = 250;
    
    344
    -    let reply;
    
    345
    -    while (leftConnAttempts-- > 0) {
    
    346
    -      const response = await this.#trySend(cmd, args, leftConnAttempts === 0);
    
    347
    -      if (response.connected) {
    
    348
    -        reply = response.reply;
    
    349
    -        break;
    
    350
    -      }
    
    351
    -      // We failed to acquire the controller after multiple attempts.
    
    352
    -      // Try again after some time.
    
    353
    -      logger.warn(
    
    354
    -        "sendCommand: Acquiring control connection failed, trying again later.",
    
    355
    -        cmd,
    
    356
    -        args
    
    357
    -      );
    
    358
    -      await new Promise(resolve => setTimeout(() => resolve(), timeout));
    
    359
    -      timeout = Math.min(2 * timeout, maxTimeout);
    
    360
    -    }
    
    361
    -
    
    362
    -    // We sent the command, but we still got an empty response.
    
    363
    -    // Something must be busted elsewhere.
    
    364
    -    if (!reply) {
    
    365
    -      throw new Error(`${cmd} sent an empty response`);
    
    366
    -    }
    
    367
    -
    
    368
    -    // TODO: Move the parsing of the reply to the controller, because anyone
    
    369
    -    // calling sendCommand on it actually wants a parsed reply.
    
    370
    -
    
    371
    -    reply = TorParsers.parseCommandResponse(reply);
    
    372
    -    if (!TorParsers.commandSucceeded(reply)) {
    
    373
    -      if (reply?.lineArray) {
    
    374
    -        throw new Error(reply.lineArray.join("\n"));
    
    375
    -      }
    
    376
    -      throw new Error(`${cmd} failed with code ${reply.statusCode}`);
    
    377
    -    }
    
    378
    -
    
    379
    -    return reply;
    
    380
    -  }
    
    381
    -
    
    382
    -  // Perform a SETCONF command.
    
    383
    -  // aSettingsObj should be a JavaScript object with keys (property values)
    
    384
    -  // that correspond to tor config. keys. The value associated with each
    
    385
    -  // key should be a simple string, a string array, or a Boolean value.
    
    386
    -  // If an associated value is undefined or null, a key with no value is
    
    387
    -  // passed in the SETCONF command.
    
    388
    -  // Throws in case of error, or returns a reply object.
    
    389
    -  async setConfWithReply(settings) {
    
    390
    -    if (!settings) {
    
    391
    -      throw new Error("Empty settings object");
    
    392
    -    }
    
    393
    -    const args = Object.entries(settings)
    
    394
    -      .map(([key, val]) => {
    
    395
    -        if (val === undefined || val === null) {
    
    396
    -          return key;
    
    397
    -        }
    
    398
    -        const valType = typeof val;
    
    399
    -        let rv = `${key}=`;
    
    400
    -        if (valType === "boolean") {
    
    401
    -          rv += val ? "1" : "0";
    
    402
    -        } else if (Array.isArray(val)) {
    
    403
    -          rv += val.map(TorParsers.escapeString).join(` ${key}=`);
    
    404
    -        } else if (valType === "string") {
    
    405
    -          rv += TorParsers.escapeString(val);
    
    406
    -        } else {
    
    407
    -          logger.error(`Got unsupported type for ${key}`, val);
    
    408
    -          throw new Error(`Unsupported type ${valType} (key ${key})`);
    
    409
    -        }
    
    410
    -        return rv;
    
    411
    -      })
    
    412
    -      .filter(arg => arg);
    
    413
    -    if (!args.length) {
    
    414
    -      throw new Error("No settings to set");
    
    415
    -    }
    
    416
    -
    
    417
    -    await this.sendCommand("SETCONF", args.join(" "));
    
    418
    -  }
    
    419
    -
    
    420
    -  // Public, never called?
    
    421
    -
    
    422
    -  async readBoolSetting(aSetting) {
    
    423
    -    let value = await this.#readBoolSetting(aSetting);
    
    424
    -    this.#settingsCache.set(aSetting, value);
    
    425
    -    return value;
    
    426
    -  }
    
    427
    -
    
    428
    -  async readStringSetting(aSetting) {
    
    429
    -    let value = await this.#readStringSetting(aSetting);
    
    430
    -    this.#settingsCache.set(aSetting, value);
    
    431
    -    return value;
    
    432
    -  }
    
    433
    -
    
    434
    -  // Private
    
    435
    -
    
    436 310
       async #setSockets() {
    
    437 311
         try {
    
    438 312
           const isWindows = TorLauncherUtil.isWindows;
    
    ... ... @@ -511,167 +385,24 @@ class TorProvider {
    511 385
         }
    
    512 386
       }
    
    513 387
     
    
    514
    -  #assertValidSettingKey(aSetting) {
    
    515
    -    // ensure the 'key' is a string
    
    516
    -    if (typeof aSetting !== "string") {
    
    517
    -      throw new Error(
    
    518
    -        `Expected setting of type string but received ${typeof aSetting}`
    
    519
    -      );
    
    520
    -    }
    
    521
    -  }
    
    522
    -
    
    523
    -  #assertValidSetting(aSetting, aValue) {
    
    524
    -    this.#assertValidSettingKey(aSetting);
    
    525
    -    switch (typeof aValue) {
    
    526
    -      case "boolean":
    
    527
    -      case "string":
    
    528
    -        return;
    
    529
    -      case "object":
    
    530
    -        if (aValue === null) {
    
    531
    -          return;
    
    532
    -        } else if (Array.isArray(aValue)) {
    
    533
    -          for (const element of aValue) {
    
    534
    -            if (typeof element !== "string") {
    
    535
    -              throw new Error(
    
    536
    -                `Setting '${aSetting}' array contains value of invalid type '${typeof element}'`
    
    537
    -              );
    
    538
    -            }
    
    539
    -          }
    
    540
    -          return;
    
    541
    -        }
    
    542
    -      // fall through
    
    543
    -      default:
    
    544
    -        throw new Error(
    
    545
    -          `Invalid object type received for setting '${aSetting}'`
    
    546
    -        );
    
    547
    -    }
    
    548
    -  }
    
    549
    -
    
    550
    -  // Perform a GETCONF command.
    
    551
    -  async #readSetting(aSetting) {
    
    552
    -    this.#assertValidSettingKey(aSetting);
    
    553
    -
    
    554
    -    const cmd = "GETCONF";
    
    555
    -    let reply = await this.sendCommand(cmd, aSetting);
    
    556
    -    return TorParsers.parseReply(cmd, aSetting, reply);
    
    557
    -  }
    
    558
    -
    
    559
    -  async #readStringSetting(aSetting) {
    
    560
    -    let lineArray = await this.#readSetting(aSetting);
    
    561
    -    if (lineArray.length !== 1) {
    
    562
    -      throw new Error(
    
    563
    -        `Expected an array with length 1 but received array of length ${lineArray.length}`
    
    564
    -      );
    
    565
    -    }
    
    566
    -    return lineArray[0];
    
    567
    -  }
    
    568
    -
    
    569
    -  async #readBoolSetting(aSetting) {
    
    570
    -    const value = this.#readStringSetting(aSetting);
    
    571
    -    switch (value) {
    
    572
    -      case "0":
    
    573
    -        return false;
    
    574
    -      case "1":
    
    575
    -        return true;
    
    576
    -      default:
    
    577
    -        throw new Error(`Expected boolean (1 or 0) but received '${value}'`);
    
    578
    -    }
    
    579
    -  }
    
    580
    -
    
    581
    -  async #trySend(cmd, args, rethrow) {
    
    582
    -    let connected = false;
    
    583
    -    let reply;
    
    584
    -    let leftAttempts = 2;
    
    585
    -    while (leftAttempts-- > 0) {
    
    586
    -      let conn;
    
    587
    -      try {
    
    588
    -        conn = await this.#getConnection();
    
    589
    -      } catch (e) {
    
    590
    -        logger.error("Cannot get a connection to the control port", e);
    
    591
    -        if (leftAttempts == 0 && rethrow) {
    
    592
    -          throw e;
    
    593
    -        }
    
    594
    -      }
    
    595
    -      if (!conn) {
    
    596
    -        continue;
    
    597
    -      }
    
    598
    -      // If we _ever_ got a connection, the caller should not try again
    
    599
    -      connected = true;
    
    600
    -      try {
    
    601
    -        reply = await conn.sendCommand(cmd + (args ? " " + args : ""));
    
    602
    -        if (reply) {
    
    603
    -          // Return for reuse.
    
    604
    -          this.#returnConnection();
    
    605
    -        } else {
    
    606
    -          // Connection is bad.
    
    607
    -          logger.warn(
    
    608
    -            "sendCommand returned an empty response, taking the connection as broken and closing it."
    
    609
    -          );
    
    610
    -          this.#closeConnection();
    
    611
    -        }
    
    612
    -      } catch (e) {
    
    613
    -        logger.error(`Cannot send the command ${cmd}`, e);
    
    614
    -        this.#closeConnection();
    
    615
    -        if (leftAttempts == 0 && rethrow) {
    
    616
    -          throw e;
    
    617
    -        }
    
    618
    -      }
    
    619
    -    }
    
    620
    -    return { connected, reply };
    
    621
    -  }
    
    622
    -
    
    623
    -  // Opens an authenticated connection, sets it to this.#controlConnection, and
    
    624
    -  // return it.
    
    625 388
       async #getConnection() {
    
    626
    -    if (!this.#controlConnection) {
    
    389
    +    if (!this.#controlConnection?.isOpen) {
    
    627 390
           this.#controlConnection = await lazy.controller();
    
    628 391
         }
    
    629
    -    if (this.#controlConnection.inUse) {
    
    630
    -      await new Promise((resolve, reject) =>
    
    631
    -        this.#connectionQueue.push({ resolve, reject })
    
    632
    -      );
    
    633
    -    } else {
    
    634
    -      this.#controlConnection.inUse = true;
    
    635
    -    }
    
    636 392
         return this.#controlConnection;
    
    637 393
       }
    
    638 394
     
    
    639
    -  #returnConnection() {
    
    640
    -    if (this.#connectionQueue.length) {
    
    641
    -      this.#connectionQueue.shift().resolve();
    
    642
    -    } else {
    
    643
    -      this.#controlConnection.inUse = false;
    
    644
    -    }
    
    645
    -  }
    
    646
    -
    
    647
    -  async #withConnection(func) {
    
    648
    -    // TODO: Make more robust?
    
    649
    -    const conn = await this.#getConnection();
    
    650
    -    try {
    
    651
    -      return await func(conn);
    
    652
    -    } finally {
    
    653
    -      this.#returnConnection();
    
    654
    -    }
    
    655
    -  }
    
    656
    -
    
    657
    -  // If aConn is omitted, the cached connection is closed.
    
    658 395
       #closeConnection() {
    
    659 396
         if (this.#controlConnection) {
    
    660 397
           logger.info("Closing the control connection");
    
    661 398
           this.#controlConnection.close();
    
    662 399
           this.#controlConnection = null;
    
    663 400
         }
    
    664
    -    for (const promise of this.#connectionQueue) {
    
    665
    -      promise.reject("Connection closed");
    
    666
    -    }
    
    667
    -    this.#connectionQueue = [];
    
    668 401
       }
    
    669 402
     
    
    670 403
       async #reconnect() {
    
    671 404
         this.#closeConnection();
    
    672
    -    const conn = await this.#getConnection();
    
    673
    -    logger.debug("Reconnected to the control port.");
    
    674
    -    this.#returnConnection(conn);
    
    405
    +    await this.#getConnection();
    
    675 406
       }
    
    676 407
     
    
    677 408
       async #readAuthenticationCookie(aPath) {
    
    ... ... @@ -777,8 +508,9 @@ class TorProvider {
    777 508
         if (this.ownsTorDaemon) {
    
    778 509
           // When we own the tor daemon, we listen to more events, that are used
    
    779 510
           // for about:torconnect or for showing the logs in the settings page.
    
    780
    -      this._eventHandlers.set("STATUS_CLIENT", (_eventType, lines) =>
    
    781
    -        this._processBootstrapStatus(lines[0], false)
    
    511
    +      this._eventHandlers.set(
    
    512
    +        "STATUS_CLIENT",
    
    513
    +        this._processStatusClient.bind(this)
    
    782 514
           );
    
    783 515
           this._eventHandlers.set("NOTICE", this._processLog.bind(this));
    
    784 516
           this._eventHandlers.set("WARN", this._processLog.bind(this));
    
    ... ... @@ -809,23 +541,10 @@ class TorProvider {
    809 541
           throw new Error("Event monitor connection not available");
    
    810 542
         }
    
    811 543
     
    
    812
    -    // TODO: Unify with TorProtocolService.sendCommand and put everything in the
    
    813
    -    // reviewed torbutton replacement.
    
    814
    -    const cmd = "GETINFO";
    
    815
    -    const key = "status/bootstrap-phase";
    
    816
    -    let reply = await this._connection.sendCommand(`${cmd} ${key}`);
    
    817
    -
    
    818
    -    // A typical reply looks like:
    
    819
    -    //  250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"
    
    820
    -    //  250 OK
    
    821
    -    reply = TorParsers.parseCommandResponse(reply);
    
    822
    -    if (!TorParsers.commandSucceeded(reply)) {
    
    823
    -      throw new Error(`${cmd} failed`);
    
    824
    -    }
    
    825
    -    reply = TorParsers.parseReply(cmd, key, reply);
    
    826
    -    if (reply.length) {
    
    827
    -      this._processBootstrapStatus(reply[0], true);
    
    828
    -    }
    
    544
    +    this._processBootstrapStatus(
    
    545
    +      await this._connection.getBootstrapPhase(),
    
    546
    +      true
    
    547
    +    );
    
    829 548
       }
    
    830 549
     
    
    831 550
       // Returns captured log message as a text string (one message per line).
    
    ... ... @@ -1058,37 +777,32 @@ class TorProvider {
    1058 777
       _monitorEvent(type, callback) {
    
    1059 778
         logger.info(`Watching events of type ${type}.`);
    
    1060 779
         let replyObj = {};
    
    1061
    -    this._connection.watchEvent(
    
    1062
    -      type,
    
    1063
    -      null,
    
    1064
    -      line => {
    
    1065
    -        if (!line) {
    
    1066
    -          return;
    
    1067
    -        }
    
    1068
    -        logger.debug("Event response: ", line);
    
    1069
    -        const isComplete = TorParsers.parseReplyLine(line, replyObj);
    
    1070
    -        if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
    
    1071
    -          return;
    
    1072
    -        }
    
    1073
    -        const reply = replyObj;
    
    1074
    -        replyObj = {};
    
    1075
    -        if (reply.statusCode !== TorStatuses.EventNotification) {
    
    1076
    -          logger.error("Unexpected event status code:", reply.statusCode);
    
    1077
    -          return;
    
    1078
    -        }
    
    1079
    -        if (!reply.lineArray[0].startsWith(`${type} `)) {
    
    1080
    -          logger.error("Wrong format for the first line:", reply.lineArray[0]);
    
    1081
    -          return;
    
    1082
    -        }
    
    1083
    -        reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
    
    1084
    -        try {
    
    1085
    -          callback(type, reply.lineArray);
    
    1086
    -        } catch (e) {
    
    1087
    -          logger.error("Exception while handling an event", reply, e);
    
    1088
    -        }
    
    1089
    -      },
    
    1090
    -      true
    
    1091
    -    );
    
    780
    +    this._connection.watchEvent(type, line => {
    
    781
    +      if (!line) {
    
    782
    +        return;
    
    783
    +      }
    
    784
    +      logger.debug("Event response: ", line);
    
    785
    +      const isComplete = TorParsers.parseReplyLine(line, replyObj);
    
    786
    +      if (!isComplete || replyObj._parseError || !replyObj.lineArray.length) {
    
    787
    +        return;
    
    788
    +      }
    
    789
    +      const reply = replyObj;
    
    790
    +      replyObj = {};
    
    791
    +      if (reply.statusCode !== TorStatuses.EventNotification) {
    
    792
    +        logger.error("Unexpected event status code:", reply.statusCode);
    
    793
    +        return;
    
    794
    +      }
    
    795
    +      if (!reply.lineArray[0].startsWith(`${type} `)) {
    
    796
    +        logger.error("Wrong format for the first line:", reply.lineArray[0]);
    
    797
    +        return;
    
    798
    +      }
    
    799
    +      reply.lineArray[0] = reply.lineArray[0].substring(type.length + 1);
    
    800
    +      try {
    
    801
    +        callback(type, reply.lineArray);
    
    802
    +      } catch (e) {
    
    803
    +        logger.error("Exception while handling an event", reply, e);
    
    804
    +      }
    
    805
    +    });
    
    1092 806
       }
    
    1093 807
     
    
    1094 808
       _processLog(type, lines) {
    
    ... ... @@ -1116,15 +830,12 @@ class TorProvider {
    1116 830
       // to TorBootstrapStatus observers.
    
    1117 831
       // If aSuppressErrors is true, errors are ignored. This is used when we
    
    1118 832
       // are handling the response to a "GETINFO status/bootstrap-phase" command.
    
    1119
    -  _processBootstrapStatus(aStatusMsg, aSuppressErrors) {
    
    1120
    -    const statusObj = TorParsers.parseBootstrapStatus(aStatusMsg);
    
    1121
    -    if (!statusObj) {
    
    1122
    -      return;
    
    1123
    -    }
    
    1124
    -
    
    833
    +  _processBootstrapStatus(statusObj, suppressErrors) {
    
    1125 834
         // Notify observers
    
    1126
    -    statusObj.wrappedJSObject = statusObj;
    
    1127
    -    Services.obs.notifyObservers(statusObj, "TorBootstrapStatus");
    
    835
    +    Services.obs.notifyObservers(
    
    836
    +      { wrappedJSObject: statusObj },
    
    837
    +      "TorBootstrapStatus"
    
    838
    +    );
    
    1128 839
     
    
    1129 840
         if (statusObj.PROGRESS === 100) {
    
    1130 841
           this._isBootstrapDone = true;
    
    ... ... @@ -1141,7 +852,7 @@ class TorProvider {
    1141 852
         if (
    
    1142 853
           statusObj.TYPE === "WARN" &&
    
    1143 854
           statusObj.RECOMMENDATION !== "ignore" &&
    
    1144
    -      !aSuppressErrors
    
    855
    +      !suppressErrors
    
    1145 856
         ) {
    
    1146 857
           this._notifyBootstrapError(statusObj);
    
    1147 858
         }
    
    ... ... @@ -1184,6 +895,15 @@ class TorProvider {
    1184 895
         }
    
    1185 896
       }
    
    1186 897
     
    
    898
    +  _processStatusClient(_type, lines) {
    
    899
    +    const statusObj = TorParsers.parseBootstrapStatus(lines[0]);
    
    900
    +    if (!statusObj) {
    
    901
    +      // No `BOOTSTRAP` in the line
    
    902
    +      return;
    
    903
    +    }
    
    904
    +    this._processBootstrapStatus(statusObj, false);
    
    905
    +  }
    
    906
    +
    
    1187 907
       async _processCircEvent(_type, lines) {
    
    1188 908
         const builtEvent =
    
    1189 909
           /^(?<CircuitID>[a-zA-Z0-9]{1,16})\sBUILT\s(?<Path>(?:,?\$[0-9a-fA-F]{40}(?:~[a-zA-Z0-9]{1,19})?)+)/.exec(
    
    ... ... @@ -1295,7 +1015,3 @@ class TorProvider {
    1295 1015
         this.clearBootstrapError();
    
    1296 1016
       }
    
    1297 1017
     }
    1298
    -
    
    1299
    -// TODO: Stop defining TorProtocolService, make the builder instance the
    
    1300
    -// TorProvider.
    
    1301
    -export const TorProtocolService = new TorProvider();

  • toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
    ... ... @@ -4,7 +4,7 @@
    4 4
     
    
    5 5
     const lazy = {};
    
    6 6
     ChromeUtils.defineESModuleGetters(lazy, {
    
    7
    -  TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
    
    7
    +  TorProvider: "resource://gre/modules/TorProvider.sys.mjs",
    
    8 8
     });
    
    9 9
     
    
    10 10
     export const TorProviderTopics = Object.freeze({
    
    ... ... @@ -19,16 +19,25 @@ export const TorProviderTopics = Object.freeze({
    19 19
     });
    
    20 20
     
    
    21 21
     export class TorProviderBuilder {
    
    22
    +  static #provider = null;
    
    23
    +
    
    22 24
       static async init() {
    
    23
    -    await lazy.TorProtocolService.init();
    
    25
    +    const provider = new lazy.TorProvider();
    
    26
    +    await provider.init();
    
    27
    +    // Assign it only when initialization succeeds.
    
    28
    +    TorProviderBuilder.#provider = provider;
    
    24 29
       }
    
    25 30
     
    
    26 31
       static uninit() {
    
    27
    -    lazy.TorProtocolService.uninit();
    
    32
    +    TorProviderBuilder.#provider.uninit();
    
    33
    +    TorProviderBuilder.#provider = null;
    
    28 34
       }
    
    29 35
     
    
    30 36
       // TODO: Switch to an async build?
    
    31 37
       static build() {
    
    32
    -    return lazy.TorProtocolService;
    
    38
    +    if (!TorProviderBuilder.#provider) {
    
    39
    +      throw new Error("TorProviderBuilder has not been initialized yet.");
    
    40
    +    }
    
    41
    +    return TorProviderBuilder.#provider;
    
    33 42
       }
    
    34 43
     }

  • toolkit/components/tor-launcher/TorStartupService.sys.mjs
    ... ... @@ -3,22 +3,13 @@ const lazy = {};
    3 3
     // We will use the modules only when the profile is loaded, so prefer lazy
    
    4 4
     // loading
    
    5 5
     ChromeUtils.defineESModuleGetters(lazy, {
    
    6
    +  TorConnect: "resource:///modules/TorConnect.sys.mjs",
    
    6 7
       TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
    
    7 8
       TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    8 9
       TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    10
    +  TorSettings: "resource:///modules/TorSettings.sys.mjs",
    
    9 11
     });
    
    10 12
     
    
    11
    -ChromeUtils.defineModuleGetter(
    
    12
    -  lazy,
    
    13
    -  "TorConnect",
    
    14
    -  "resource:///modules/TorConnect.jsm"
    
    15
    -);
    
    16
    -ChromeUtils.defineModuleGetter(
    
    17
    -  lazy,
    
    18
    -  "TorSettings",
    
    19
    -  "resource:///modules/TorSettings.jsm"
    
    20
    -);
    
    21
    -
    
    22 13
     /* Browser observer topis */
    
    23 14
     const BrowserTopics = Object.freeze({
    
    24 15
       ProfileAfterChange: "profile-after-change",
    

  • toolkit/components/tor-launcher/moz.build
    ... ... @@ -3,10 +3,9 @@ EXTRA_JS_MODULES += [
    3 3
         "TorControlPort.sys.mjs",
    
    4 4
         "TorDomainIsolator.sys.mjs",
    
    5 5
         "TorLauncherUtil.sys.mjs",
    
    6
    -    "TorMonitorService.sys.mjs",
    
    7 6
         "TorParsers.sys.mjs",
    
    8 7
         "TorProcess.sys.mjs",
    
    9
    -    "TorProtocolService.sys.mjs",
    
    8
    +    "TorProvider.sys.mjs",
    
    10 9
         "TorProviderBuilder.sys.mjs",
    
    11 10
         "TorStartupService.sys.mjs",
    
    12 11
     ]
    

  • toolkit/mozapps/update/UpdateService.sys.mjs
    ... ... @@ -23,18 +23,13 @@ ChromeUtils.defineESModuleGetters(lazy, {
    23 23
       AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs",
    
    24 24
       CertUtils: "resource://gre/modules/CertUtils.sys.mjs",
    
    25 25
       DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
    
    26
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    26 27
       UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
    
    27 28
       WindowsRegistry: "resource://gre/modules/WindowsRegistry.sys.mjs",
    
    28 29
       ctypes: "resource://gre/modules/ctypes.sys.mjs",
    
    29 30
       setTimeout: "resource://gre/modules/Timer.sys.mjs",
    
    30 31
     });
    
    31 32
     
    
    32
    -ChromeUtils.defineModuleGetter(
    
    33
    -  lazy,
    
    34
    -  "TorMonitorService",
    
    35
    -  "resource://gre/modules/TorMonitorService.jsm"
    
    36
    -);
    
    37
    -
    
    38 33
     XPCOMUtils.defineLazyServiceGetter(
    
    39 34
       lazy,
    
    40 35
       "AUS",
    
    ... ... @@ -394,10 +389,11 @@ XPCOMUtils.defineLazyGetter(
    394 389
     );
    
    395 390
     
    
    396 391
     function _shouldRegisterBootstrapObserver(errorCode) {
    
    392
    +  const provider = lazy.TorProviderBuilder.build();
    
    397 393
       return (
    
    398 394
         errorCode == PROXY_SERVER_CONNECTION_REFUSED &&
    
    399
    -    !lazy.TorMonitorService.isBootstrapDone &&
    
    400
    -    lazy.TorMonitorService.ownsTorDaemon
    
    395
    +    !provider.isBootstrapDone &&
    
    396
    +    provider.ownsTorDaemon
    
    401 397
       );
    
    402 398
     }
    
    403 399
     
    
    ... ... @@ -5833,10 +5829,7 @@ Downloader.prototype = {
    5833 5829
           // we choose to compute these hashes.
    
    5834 5830
           hash = hash.finish(false);
    
    5835 5831
           digest = Array.from(hash, (c, i) =>
    
    5836
    -        hash
    
    5837
    -          .charCodeAt(i)
    
    5838
    -          .toString(16)
    
    5839
    -          .padStart(2, "0")
    
    5832
    +        hash.charCodeAt(i).toString(16).padStart(2, "0")
    
    5840 5833
           ).join("");
    
    5841 5834
         } catch (e) {
    
    5842 5835
           LOG(