Pier Angelo Vendrame pushed to branch tor-browser-128.7.0esr-14.5-1 at The Tor Project / Applications / Tor Browser

Commits:

7 changed files:

Changes:

  • browser/components/BrowserGlue.sys.mjs
    ... ... @@ -4824,6 +4824,7 @@ BrowserGlue.prototype = {
    4824 4824
         //            since we hid the UI (tor-browser#43118).
    
    4825 4825
         // Version 6: Tor Browser 14.5a3: Clear preference for TorSettings that is
    
    4826 4826
         //            no longer used (tor-browser#41921).
    
    4827
    +    //            Drop unused TorConnect setting (tor-browser#43462).
    
    4827 4828
         const TBB_MIGRATION_VERSION = 6;
    
    4828 4829
         const MIGRATION_PREF = "torbrowser.migration.version";
    
    4829 4830
     
    
    ... ... @@ -4908,6 +4909,7 @@ BrowserGlue.prototype = {
    4908 4909
     
    
    4909 4910
         if (currentVersion < 6) {
    
    4910 4911
           Services.prefs.clearUserPref("torbrowser.settings.enabled");
    
    4912
    +      Services.prefs.clearUserPref("torbrowser.bootstrap.allow_internet_test");
    
    4911 4913
         }
    
    4912 4914
     
    
    4913 4915
         Services.prefs.setIntPref(MIGRATION_PREF, TBB_MIGRATION_VERSION);
    

  • browser/components/torpreferences/content/connectionPane.js
    ... ... @@ -22,13 +22,9 @@ const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
    22 22
       "resource://gre/modules/TorProviderBuilder.sys.mjs"
    
    23 23
     );
    
    24 24
     
    
    25
    -const { TorConnect, TorConnectTopics, TorConnectStage, TorCensorshipLevel } =
    
    25
    +const { InternetStatus, TorConnect, TorConnectTopics, TorConnectStage } =
    
    26 26
       ChromeUtils.importESModule("resource://gre/modules/TorConnect.sys.mjs");
    
    27 27
     
    
    28
    -const { MoatRPC } = ChromeUtils.importESModule(
    
    29
    -  "resource://gre/modules/Moat.sys.mjs"
    
    30
    -);
    
    31
    -
    
    32 28
     const { QRCode } = ChromeUtils.importESModule(
    
    33 29
       "resource://gre/modules/QRCode.sys.mjs"
    
    34 30
     );
    
    ... ... @@ -2371,12 +2367,6 @@ const gNetworkStatus = {
    2371 2367
         this._internetResultEl = this._internetAreaEl.querySelector(
    
    2372 2368
           ".network-status-result"
    
    2373 2369
         );
    
    2374
    -    this._internetTestButton = document.getElementById(
    
    2375
    -      "network-status-internet-test-button"
    
    2376
    -    );
    
    2377
    -    this._internetTestButton.addEventListener("click", () => {
    
    2378
    -      this._startInternetTest();
    
    2379
    -    });
    
    2380 2370
     
    
    2381 2371
         this._torAreaEl = document.getElementById("network-status-tor-area");
    
    2382 2372
         this._torResultEl = this._torAreaEl.querySelector(".network-status-result");
    
    ... ... @@ -2387,10 +2377,11 @@ const gNetworkStatus = {
    2387 2377
           TorConnect.openTorConnect({ beginBootstrapping: "soft" });
    
    2388 2378
         });
    
    2389 2379
     
    
    2390
    -    this._updateInternetStatus("unknown");
    
    2380
    +    this._updateInternetStatus();
    
    2391 2381
         this._updateTorConnectionStatus();
    
    2392 2382
     
    
    2393 2383
         Services.obs.addObserver(this, TorConnectTopics.StageChange);
    
    2384
    +    Services.obs.addObserver(this, TorConnectTopics.InternetStatusChange);
    
    2394 2385
       },
    
    2395 2386
     
    
    2396 2387
       /**
    
    ... ... @@ -2398,98 +2389,42 @@ const gNetworkStatus = {
    2398 2389
        */
    
    2399 2390
       uninit() {
    
    2400 2391
         Services.obs.removeObserver(this, TorConnectTopics.StageChange);
    
    2392
    +    Services.obs.removeObserver(this, TorConnectTopics.InternetStatusChange);
    
    2401 2393
       },
    
    2402 2394
     
    
    2403 2395
       observe(subject, topic) {
    
    2404 2396
         switch (topic) {
    
    2405 2397
           // triggered when tor connect state changes and we may
    
    2406 2398
           // need to update the messagebox
    
    2407
    -      case TorConnectTopics.StageChange: {
    
    2399
    +      case TorConnectTopics.StageChange:
    
    2408 2400
             this._updateTorConnectionStatus();
    
    2409 2401
             break;
    
    2410
    -      }
    
    2411
    -    }
    
    2412
    -  },
    
    2413
    -
    
    2414
    -  /**
    
    2415
    -   * Whether the test should be disabled.
    
    2416
    -   *
    
    2417
    -   * @type {boolean}
    
    2418
    -   */
    
    2419
    -  _internetTestDisabled: false,
    
    2420
    -  /**
    
    2421
    -   * Start the internet test.
    
    2422
    -   */
    
    2423
    -  async _startInternetTest() {
    
    2424
    -    if (this._internetTestDisabled) {
    
    2425
    -      return;
    
    2426
    -    }
    
    2427
    -    this._internetTestDisabled = true;
    
    2428
    -    // We use "aria-disabled" rather than the "disabled" attribute so that the
    
    2429
    -    // button can remain focusable during the test.
    
    2430
    -    // TODO: Replace with moz-button when it handles this for us. See
    
    2431
    -    // tor-browser#43275.
    
    2432
    -    this._internetTestButton.setAttribute("aria-disabled", "true");
    
    2433
    -    this._internetTestButton.classList.add("spoof-button-disabled");
    
    2434
    -    this._internetTestButton.tabIndex = -1;
    
    2435
    -    try {
    
    2436
    -      this._updateInternetStatus("testing");
    
    2437
    -      const mrpc = new MoatRPC();
    
    2438
    -      let status = null;
    
    2439
    -      try {
    
    2440
    -        await mrpc.init();
    
    2441
    -        status = await mrpc.testInternetConnection();
    
    2442
    -      } catch (err) {
    
    2443
    -        log.error("Error while checking the Internet connection", err);
    
    2444
    -      } finally {
    
    2445
    -        mrpc.uninit();
    
    2446
    -      }
    
    2447
    -      if (status) {
    
    2448
    -        this._updateInternetStatus(status.successful ? "online" : "offline");
    
    2449
    -      } else {
    
    2450
    -        this._updateInternetStatus("unknown");
    
    2451
    -      }
    
    2452
    -    } finally {
    
    2453
    -      this._internetTestButton.removeAttribute("aria-disabled");
    
    2454
    -      this._internetTestButton.classList.remove("spoof-button-disabled");
    
    2455
    -      this._internetTestButton.tabIndex = 0;
    
    2456
    -      this._internetTestDisabled = false;
    
    2402
    +      case TorConnectTopics.InternetStatusChange:
    
    2403
    +        this._updateInternetStatus();
    
    2404
    +        break;
    
    2457 2405
         }
    
    2458 2406
       },
    
    2459 2407
     
    
    2460 2408
       /**
    
    2461 2409
        * Update the shown internet status.
    
    2462
    -   *
    
    2463
    -   * @param {string} stateName - The name of the state to show.
    
    2464 2410
        */
    
    2465
    -  _updateInternetStatus(stateName) {
    
    2411
    +  _updateInternetStatus() {
    
    2466 2412
         let l10nId;
    
    2467
    -    switch (stateName) {
    
    2468
    -      case "testing":
    
    2469
    -        l10nId = "tor-connection-internet-status-testing";
    
    2470
    -        break;
    
    2471
    -      case "offline":
    
    2413
    +    let isOffline = false;
    
    2414
    +    switch (TorConnect.internetStatus) {
    
    2415
    +      case InternetStatus.Offline:
    
    2472 2416
             l10nId = "tor-connection-internet-status-offline";
    
    2417
    +        isOffline = true;
    
    2473 2418
             break;
    
    2474
    -      case "online":
    
    2419
    +      case InternetStatus.Online:
    
    2475 2420
             l10nId = "tor-connection-internet-status-online";
    
    2476 2421
             break;
    
    2422
    +      default:
    
    2423
    +        l10nId = "tor-connection-internet-status-unknown";
    
    2424
    +        break;
    
    2477 2425
         }
    
    2478
    -    if (l10nId) {
    
    2479
    -      this._internetResultEl.setAttribute("data-l10n-id", l10nId);
    
    2480
    -    } else {
    
    2481
    -      this._internetResultEl.removeAttribute("data-l10n-id");
    
    2482
    -      this._internetResultEl.textContent = "";
    
    2483
    -    }
    
    2484
    -
    
    2485
    -    this._internetAreaEl.classList.toggle(
    
    2486
    -      "status-loading",
    
    2487
    -      stateName === "testing"
    
    2488
    -    );
    
    2489
    -    this._internetAreaEl.classList.toggle(
    
    2490
    -      "status-offline",
    
    2491
    -      stateName === "offline"
    
    2492
    -    );
    
    2426
    +    this._internetResultEl.setAttribute("data-l10n-id", l10nId);
    
    2427
    +    this._internetAreaEl.classList.toggle("status-offline", isOffline);
    
    2493 2428
       },
    
    2494 2429
     
    
    2495 2430
       /**
    

  • browser/components/torpreferences/content/connectionPane.xhtml
    ... ... @@ -31,27 +31,15 @@
    31 31
           aria-labelledby="network-status-internet-area-label"
    
    32 32
         >
    
    33 33
           <html:img alt="" class="network-status-icon" />
    
    34
    -      <!-- Use an aria-live area to announce "Internet: Online" or
    
    35
    -         - "Internet: Offline". We only expect this to change when triggered by
    
    36
    -         - the user clicking the "Test" button, so shouldn't occur unexpectedly.
    
    37
    -         -->
    
    38
    -      <html:div
    
    39
    -        class="network-status-live-area"
    
    40
    -        aria-live="polite"
    
    41
    -        aria-atomic="true"
    
    42
    -      >
    
    43
    -        <html:span
    
    44
    -          id="network-status-internet-area-label"
    
    45
    -          class="network-status-label"
    
    46
    -          data-l10n-id="tor-connection-internet-status-label"
    
    47
    -        ></html:span>
    
    48
    -        <img alt="" class="network-status-loading-icon tor-loading-icon" />
    
    49
    -        <html:span class="network-status-result"></html:span>
    
    50
    -      </html:div>
    
    51
    -      <html:button
    
    52
    -        id="network-status-internet-test-button"
    
    53
    -        data-l10n-id="tor-connection-internet-status-test-button"
    
    54
    -      ></html:button>
    
    34
    +      <!-- NOTE: We do not wrap the label and status ("Internet: Offline", etc)
    
    35
    +         - in an aria-live area because it may be too noisey and may not be
    
    36
    +         - important to the user. -->
    
    37
    +      <html:span
    
    38
    +        id="network-status-internet-area-label"
    
    39
    +        class="network-status-label"
    
    40
    +        data-l10n-id="tor-connection-internet-status-label"
    
    41
    +      ></html:span>
    
    42
    +      <html:span class="network-status-result"></html:span>
    
    55 43
         </html:div>
    
    56 44
         <html:div
    
    57 45
           id="network-status-tor-area"
    
    ... ... @@ -60,8 +48,8 @@
    60 48
           aria-labelledby="network-status-tor-area-label"
    
    61 49
         >
    
    62 50
           <html:img alt="" class="network-status-icon" />
    
    63
    -      <!-- NOTE: Unlike #network-status-internet-area, we do not wrap the label
    
    64
    -         - and status ("Tor network: Not connected", etc) in an aria-live area.
    
    51
    +      <!-- NOTE: We do not wrap the label and status
    
    52
    +         - ("Tor network: Not connected", etc) in an aria-live area.
    
    65 53
              - This is not likely to change whilst this page has focus.
    
    66 54
              - Moreover, the status is already present in the torconnect status bar
    
    67 55
              - in the window tab bar. -->
    

  • browser/components/torpreferences/content/torPreferences.css
    ... ... @@ -90,24 +90,12 @@ button.spoof-button-disabled {
    90 90
       }
    
    91 91
     }
    
    92 92
     
    
    93
    -.network-status-live-area {
    
    94
    -  display: contents;
    
    95
    -}
    
    96
    -
    
    97 93
     .network-status-label {
    
    98 94
       font-weight: bold;
    
    99 95
       margin-inline-end: 0.75em;
    
    100 96
     }
    
    101 97
     
    
    102
    -.network-status-loading-icon {
    
    103
    -  margin-inline-end: 0.5em;
    
    104
    -}
    
    105
    -
    
    106
    -#network-status-internet-area:not(.status-loading) .network-status-loading-icon {
    
    107
    -  display: none;
    
    108
    -}
    
    109
    -
    
    110
    -.network-status-result:not(:empty) {
    
    98
    +.network-status-result {
    
    111 99
       margin-inline-end: 0.75em;
    
    112 100
     }
    
    113 101
     
    

  • toolkit/locales/en-US/toolkit/global/tor-browser.ftl
    ... ... @@ -65,19 +65,15 @@ tor-connection-quickstart-checkbox =
    65 65
     # Prefix before the internet connection status.
    
    66 66
     # "Internet" is not a proper noun, but is capitalized because it is the start of a sentence.
    
    67 67
     tor-connection-internet-status-label = Internet:
    
    68
    -# Button to test the internet connection.
    
    69
    -# Here "Test" is a verb, as in "test the internet connection".
    
    70
    -# Uses sentence case in English (US).
    
    71
    -tor-connection-internet-status-test-button = Test
    
    72
    -# Shown when testing the internet status.
    
    73
    -# Uses sentence case in English (US).
    
    74
    -tor-connection-internet-status-testing = Testing…
    
    75 68
     # Shown when the user is connected to the internet.
    
    76 69
     # Uses sentence case in English (US).
    
    77 70
     tor-connection-internet-status-online = Online
    
    78 71
     # Shown when the user is not connected to the internet.
    
    79 72
     # Uses sentence case in English (US).
    
    80 73
     tor-connection-internet-status-offline = Offline
    
    74
    +# Shown when the user has an unknown internet connection status.
    
    75
    +# Uses sentence case in English (US).
    
    76
    +tor-connection-internet-status-unknown = Unknown status
    
    81 77
     
    
    82 78
     # Prefix before the Tor network connection status.
    
    83 79
     # Uses sentence case in English (US).
    

  • toolkit/modules/Moat.sys.mjs
    ... ... @@ -23,57 +23,6 @@ const TorLauncherPrefs = Object.freeze({
    23 23
       moat_service: "extensions.torlauncher.moat_service",
    
    24 24
     });
    
    25 25
     
    
    26
    -/**
    
    27
    - * A special response listener that collects the received headers.
    
    28
    - */
    
    29
    -class InternetTestResponseListener {
    
    30
    -  #promise;
    
    31
    -  #resolve;
    
    32
    -  #reject;
    
    33
    -  constructor() {
    
    34
    -    this.#promise = new Promise((resolve, reject) => {
    
    35
    -      this.#resolve = resolve;
    
    36
    -      this.#reject = reject;
    
    37
    -    });
    
    38
    -  }
    
    39
    -
    
    40
    -  // callers wait on this for final response
    
    41
    -  get status() {
    
    42
    -    return this.#promise;
    
    43
    -  }
    
    44
    -
    
    45
    -  onStartRequest() {}
    
    46
    -
    
    47
    -  // resolve or reject our Promise
    
    48
    -  onStopRequest(request, status) {
    
    49
    -    try {
    
    50
    -      const statuses = {
    
    51
    -        components: status,
    
    52
    -        successful: Components.isSuccessCode(status),
    
    53
    -      };
    
    54
    -      try {
    
    55
    -        if (statuses.successful) {
    
    56
    -          statuses.http = request.responseStatus;
    
    57
    -          statuses.date = request.getResponseHeader("Date");
    
    58
    -        }
    
    59
    -      } catch (err) {
    
    60
    -        console.warn(
    
    61
    -          "Successful request, but could not get the HTTP status or date",
    
    62
    -          err
    
    63
    -        );
    
    64
    -      }
    
    65
    -      this.#resolve(statuses);
    
    66
    -    } catch (err) {
    
    67
    -      this.#reject(err);
    
    68
    -    }
    
    69
    -  }
    
    70
    -
    
    71
    -  onDataAvailable() {
    
    72
    -    // We do not care of the actual data, as long as we have a successful
    
    73
    -    // connection
    
    74
    -  }
    
    75
    -}
    
    76
    -
    
    77 26
     /**
    
    78 27
      * @typedef {Object} MoatBridges
    
    79 28
      *
    
    ... ... @@ -184,18 +133,6 @@ export class MoatRPC {
    184 133
         return { response, cancelled };
    
    185 134
       }
    
    186 135
     
    
    187
    -  async testInternetConnection() {
    
    188
    -    const uri = `${Services.prefs.getStringPref(
    
    189
    -      TorLauncherPrefs.moat_service
    
    190
    -    )}/circumvention/countries`;
    
    191
    -    const ch = this.#requestBuilder.buildHttpHandler(uri);
    
    192
    -    ch.requestMethod = "HEAD";
    
    193
    -
    
    194
    -    const listener = new InternetTestResponseListener();
    
    195
    -    ch.asyncOpen(listener, ch);
    
    196
    -    return listener.status;
    
    197
    -  }
    
    198
    -
    
    199 136
       /**
    
    200 137
        * Request a CAPTCHA challenge.
    
    201 138
        *
    

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -16,9 +16,16 @@ ChromeUtils.defineESModuleGetters(lazy, {
    16 16
       TorSettingsTopics: "resource://gre/modules/TorSettings.sys.mjs",
    
    17 17
     });
    
    18 18
     
    
    19
    +ChromeUtils.defineLazyGetter(lazy, "NetworkLinkService", () => {
    
    20
    +  return Cc["@mozilla.org/network/network-link-service;1"].getService(
    
    21
    +    Ci.nsINetworkLinkService
    
    22
    +  );
    
    23
    +});
    
    24
    +
    
    25
    +const NETWORK_LINK_TOPIC = "network:link-status-changed";
    
    26
    +
    
    19 27
     const TorConnectPrefs = Object.freeze({
    
    20 28
       censorship_level: "torbrowser.debug.censorship_level",
    
    21
    -  allow_internet_test: "torbrowser.bootstrap.allow_internet_test",
    
    22 29
       log_level: "torbrowser.bootstrap.log_level",
    
    23 30
       /* prompt_at_startup now controls whether the quickstart can trigger. */
    
    24 31
       prompt_at_startup: "extensions.torlauncher.prompt_at_startup",
    
    ... ... @@ -82,6 +89,7 @@ export const TorConnectTopics = Object.freeze({
    82 89
       // TODO: Remove torconnect:state-change when pages have switched to stage.
    
    83 90
       StateChange: "torconnect:state-change",
    
    84 91
       QuickstartChange: "torconnect:quickstart-change",
    
    92
    +  InternetStatusChange: "torconnect:internet-status-change",
    
    85 93
       BootstrapProgress: "torconnect:bootstrap-progress",
    
    86 94
       BootstrapComplete: "torconnect:bootstrap-complete",
    
    87 95
       // TODO: Remove torconnect:error when pages have switched to stage.
    
    ... ... @@ -108,10 +116,6 @@ export const TorConnectTopics = Object.freeze({
    108 116
      *   should be an Array of MoatBridges objects that match the bridge settings
    
    109 117
      *   accepted by TorSettings.bridges, plus you may add a "simulateCensorship"
    
    110 118
      *   property to make only their bootstrap attempts fail.
    
    111
    - * @property {boolean} [options.testInternet] - Whether to also test the
    
    112
    - *   internet connection.
    
    113
    - * @property {boolean} [options.simulateOffline] - Whether to simulate an
    
    114
    - *   offline test result. This will not cause the bootstrap to fail.
    
    115 119
      * @property {string} [options.regionCode] - The region code to use to fetch
    
    116 120
      *   auto-bootstrap settings, or "automatic" to automatically choose the region.
    
    117 121
      */
    
    ... ... @@ -141,18 +145,6 @@ class BootstrapAttempt {
    141 145
        * @type {?TorBootstrapRequest}
    
    142 146
        */
    
    143 147
       #bootstrap = null;
    
    144
    -  /**
    
    145
    -   * The error returned by the bootstrap request, if any.
    
    146
    -   *
    
    147
    -   * @type {?Error}
    
    148
    -   */
    
    149
    -  #bootstrapError = null;
    
    150
    -  /**
    
    151
    -   * The ongoing internet test, if any.
    
    152
    -   *
    
    153
    -   * @type {?InternetTest}
    
    154
    -   */
    
    155
    -  #internetTest = null;
    
    156 148
       /**
    
    157 149
        * The method to call to complete the `run` promise.
    
    158 150
        *
    
    ... ... @@ -192,13 +184,6 @@ class BootstrapAttempt {
    192 184
             return;
    
    193 185
           }
    
    194 186
           this.#resolved = true;
    
    195
    -      try {
    
    196
    -        // Should be ok to call this twice in the case where we "cancel" the
    
    197
    -        // bootstrap.
    
    198
    -        this.#internetTest?.cancel();
    
    199
    -      } catch (error) {
    
    200
    -        lazy.logger.error("Unexpected error in bootstrap cleanup", error);
    
    201
    -      }
    
    202 187
           if (arg.error) {
    
    203 188
             reject(arg.error);
    
    204 189
           } else {
    
    ... ... @@ -259,35 +244,7 @@ class BootstrapAttempt {
    259 244
           this.#resolveRun({ result: "complete" });
    
    260 245
         };
    
    261 246
         this.#bootstrap.onbootstraperror = error => {
    
    262
    -      if (this.#bootstrapError) {
    
    263
    -        lazy.logger.warn("Another bootstrap error", error);
    
    264
    -        return;
    
    265
    -      }
    
    266
    -      // We have to wait for the Internet test to finish before sending the
    
    267
    -      // bootstrap error
    
    268
    -      this.#bootstrapError = error;
    
    269
    -      this.#maybeTransitionToError();
    
    270
    -    };
    
    271
    -    if (options.testInternet) {
    
    272
    -      this.#internetTest = new InternetTest(options.simulateOffline);
    
    273
    -      this.#internetTest.onResult = () => {
    
    274
    -        this.#maybeTransitionToError();
    
    275
    -      };
    
    276
    -      this.#internetTest.onError = () => {
    
    277
    -        this.#maybeTransitionToError();
    
    278
    -      };
    
    279
    -    }
    
    280
    -
    
    281
    -    this.#bootstrap.bootstrap();
    
    282
    -  }
    
    283
    -
    
    284
    -  /**
    
    285
    -   * Callback for when we get a new bootstrap error or a change in the internet
    
    286
    -   * status.
    
    287
    -   */
    
    288
    -  #maybeTransitionToError() {
    
    289
    -    if (this.#resolved || this.#cancelled) {
    
    290
    -      if (this.#bootstrapError) {
    
    247
    +      if (this.#resolved || this.#cancelled) {
    
    291 248
             // We ignore this error since it occurred after cancelling (by the
    
    292 249
             // user), or we have already resolved. We assume the error is just a
    
    293 250
             // side effect of the cancelling.
    
    ... ... @@ -295,45 +252,16 @@ class BootstrapAttempt {
    295 252
             // "Building circuits: Establishing a Tor circuit failed".
    
    296 253
             // TODO: Maybe move this logic deeper in the process to know when to
    
    297 254
             // filter out such errors triggered by cancelling.
    
    298
    -        lazy.logger.warn("Post-complete error.", this.#bootstrapError);
    
    255
    +        lazy.logger.warn("Post-complete bootstrap error.", error);
    
    256
    +        return;
    
    299 257
           }
    
    300
    -      return;
    
    301
    -    }
    
    302 258
     
    
    303
    -    if (
    
    304
    -      this.#internetTest &&
    
    305
    -      this.#internetTest.status === InternetStatus.Unknown &&
    
    306
    -      this.#internetTest.error === null &&
    
    307
    -      this.#internetTest.enabled
    
    308
    -    ) {
    
    309
    -      // We have been called by a failed bootstrap, but the internet test has
    
    310
    -      // not run yet - force it to run immediately!
    
    311
    -      this.#internetTest.test();
    
    312
    -      // Return from this call, because the Internet test's callback will call
    
    313
    -      // us again.
    
    314
    -      return;
    
    315
    -    }
    
    316
    -    // Do not transition to "offline" until we are sure that also the bootstrap
    
    317
    -    // failed, in case Moat is down but the bootstrap can proceed anyway.
    
    318
    -    if (!this.#bootstrapError) {
    
    319
    -      return;
    
    320
    -    }
    
    321
    -    if (this.#internetTest?.status === InternetStatus.Offline) {
    
    322
    -      if (this.#bootstrapError) {
    
    323
    -        lazy.logger.info(
    
    324
    -          "Ignoring bootstrap error since offline.",
    
    325
    -          this.#bootstrapError
    
    326
    -        );
    
    327
    -      }
    
    328
    -      this.#resolveRun({ result: "offline" });
    
    329
    -      return;
    
    330
    -    }
    
    331
    -    this.#resolveRun({
    
    332
    -      error: new TorConnectError(
    
    333
    -        TorConnectError.BootstrapError,
    
    334
    -        this.#bootstrapError
    
    335
    -      ),
    
    336
    -    });
    
    259
    +      this.#resolveRun({
    
    260
    +        error: new TorConnectError(TorConnectError.BootstrapError, error),
    
    261
    +      });
    
    262
    +    };
    
    263
    +
    
    264
    +    this.#bootstrap.bootstrap();
    
    337 265
       }
    
    338 266
     
    
    339 267
       /**
    
    ... ... @@ -355,7 +283,6 @@ class BootstrapAttempt {
    355 283
         // cancelled. In particular, there is a small chance that the bootstrap
    
    356 284
         // completes, in which case we want to be able to resolve with a success
    
    357 285
         // instead.
    
    358
    -    this.#internetTest?.cancel();
    
    359 286
         await this.#bootstrap?.cancel();
    
    360 287
         this.#resolveRun({ result: "cancelled" });
    
    361 288
       }
    
    ... ... @@ -729,117 +656,6 @@ export const InternetStatus = Object.freeze({
    729 656
       Online: 1,
    
    730 657
     });
    
    731 658
     
    
    732
    -class InternetTest {
    
    733
    -  #enabled;
    
    734
    -  #status = InternetStatus.Unknown;
    
    735
    -  #error = null;
    
    736
    -  #pending = false;
    
    737
    -  #canceled = false;
    
    738
    -  #timeout = 0;
    
    739
    -  #simulateOffline = false;
    
    740
    -
    
    741
    -  constructor(simulateOffline) {
    
    742
    -    this.#simulateOffline = simulateOffline;
    
    743
    -
    
    744
    -    this.#enabled = Services.prefs.getBoolPref(
    
    745
    -      TorConnectPrefs.allow_internet_test,
    
    746
    -      true
    
    747
    -    );
    
    748
    -    if (this.#enabled) {
    
    749
    -      this.#timeout = setTimeout(() => {
    
    750
    -        this.#timeout = 0;
    
    751
    -        this.test();
    
    752
    -      }, this.#timeoutRand());
    
    753
    -    }
    
    754
    -    this.onResult = _online => {};
    
    755
    -    this.onError = _error => {};
    
    756
    -  }
    
    757
    -
    
    758
    -  /**
    
    759
    -   * Perform the internet test.
    
    760
    -   *
    
    761
    -   * While this is an async method, the callers are not expected to await it,
    
    762
    -   * as we are also using callbacks.
    
    763
    -   */
    
    764
    -  async test() {
    
    765
    -    if (this.#pending || !this.#enabled) {
    
    766
    -      return;
    
    767
    -    }
    
    768
    -    this.cancel();
    
    769
    -    this.#pending = true;
    
    770
    -    this.#canceled = false;
    
    771
    -
    
    772
    -    lazy.logger.info("Starting the Internet test");
    
    773
    -
    
    774
    -    if (this.#simulateOffline) {
    
    775
    -      await new Promise(res => setTimeout(res, 500));
    
    776
    -
    
    777
    -      this.#status = InternetStatus.Offline;
    
    778
    -
    
    779
    -      if (this.#canceled) {
    
    780
    -        return;
    
    781
    -      }
    
    782
    -      this.onResult(this.#status);
    
    783
    -      return;
    
    784
    -    }
    
    785
    -
    
    786
    -    const mrpc = new lazy.MoatRPC();
    
    787
    -    try {
    
    788
    -      await mrpc.init();
    
    789
    -      const status = await mrpc.testInternetConnection();
    
    790
    -      this.#status = status.successful
    
    791
    -        ? InternetStatus.Online
    
    792
    -        : InternetStatus.Offline;
    
    793
    -      // TODO: We could consume the date we got from the HTTP request to detect
    
    794
    -      // big clock skews that might prevent a successfull bootstrap.
    
    795
    -      lazy.logger.info(`Performed Internet test, outcome ${this.#status}`);
    
    796
    -    } catch (err) {
    
    797
    -      lazy.logger.error("Error while checking the Internet connection", err);
    
    798
    -      this.#error = err;
    
    799
    -      this.#pending = false;
    
    800
    -    } finally {
    
    801
    -      mrpc.uninit();
    
    802
    -    }
    
    803
    -
    
    804
    -    if (this.#canceled) {
    
    805
    -      return;
    
    806
    -    }
    
    807
    -    if (this.#error) {
    
    808
    -      this.onError(this.#error);
    
    809
    -    } else {
    
    810
    -      this.onResult(this.#status);
    
    811
    -    }
    
    812
    -  }
    
    813
    -
    
    814
    -  cancel() {
    
    815
    -    this.#canceled = true;
    
    816
    -    if (this.#timeout) {
    
    817
    -      clearTimeout(this.#timeout);
    
    818
    -      this.#timeout = 0;
    
    819
    -    }
    
    820
    -  }
    
    821
    -
    
    822
    -  get status() {
    
    823
    -    return this.#status;
    
    824
    -  }
    
    825
    -
    
    826
    -  get error() {
    
    827
    -    return this.#error;
    
    828
    -  }
    
    829
    -
    
    830
    -  get enabled() {
    
    831
    -    return this.#enabled;
    
    832
    -  }
    
    833
    -
    
    834
    -  // We randomize the Internet test timeout to make fingerprinting it harder, at
    
    835
    -  // least a little bit...
    
    836
    -  #timeoutRand() {
    
    837
    -    const offset = 30000;
    
    838
    -    const randRange = 5000;
    
    839
    -    return offset + randRange * (Math.random() * 2 - 1);
    
    840
    -  }
    
    841
    -}
    
    842
    -
    
    843 659
     export const TorConnectStage = Object.freeze({
    
    844 660
       Disabled: "Disabled",
    
    845 661
       Loading: "Loading",
    
    ... ... @@ -903,6 +719,13 @@ export const TorConnect = {
    903 719
        */
    
    904 720
       simulateBootstrapOptions: {},
    
    905 721
     
    
    722
    +  /**
    
    723
    +   * Whether to simulate being offline.
    
    724
    +   *
    
    725
    +   * @type {boolean}
    
    726
    +   */
    
    727
    +  simulateOffline: false,
    
    728
    +
    
    906 729
       /**
    
    907 730
        * The name of the current stage the user is in.
    
    908 731
        *
    
    ... ... @@ -1004,10 +827,6 @@ export const TorConnect = {
    1004 827
         })()
    
    1005 828
       ),
    
    1006 829
     
    
    1007
    -  // This is used as a helper to make the state of about:torconnect persistent
    
    1008
    -  // during a session, but TorConnect does not use this data at all.
    
    1009
    -  _uiState: {},
    
    1010
    -
    
    1011 830
       /**
    
    1012 831
        * The status of the most recent bootstrap attempt.
    
    1013 832
        *
    
    ... ... @@ -1029,6 +848,36 @@ export const TorConnect = {
    1029 848
         );
    
    1030 849
       },
    
    1031 850
     
    
    851
    +  /**
    
    852
    +   * The current internet status. One of the InternetStatus values.
    
    853
    +   *
    
    854
    +   * @type {integer}
    
    855
    +   */
    
    856
    +  _internetStatus: InternetStatus.Unknown,
    
    857
    +
    
    858
    +  get internetStatus() {
    
    859
    +    return this._internetStatus;
    
    860
    +  },
    
    861
    +
    
    862
    +  /**
    
    863
    +   * Update the _internetStatus value.
    
    864
    +   */
    
    865
    +  _updateInternetStatus() {
    
    866
    +    let newStatus;
    
    867
    +    if (lazy.NetworkLinkService.linkStatusKnown) {
    
    868
    +      newStatus = lazy.NetworkLinkService.isLinkUp
    
    869
    +        ? InternetStatus.Online
    
    870
    +        : InternetStatus.Offline;
    
    871
    +    } else {
    
    872
    +      newStatus = InternetStatus.Unknown;
    
    873
    +    }
    
    874
    +    if (newStatus === this._internetStatus) {
    
    875
    +      return;
    
    876
    +    }
    
    877
    +    this._internetStatus = newStatus;
    
    878
    +    Services.obs.notifyObservers(null, TorConnectTopics.InternetStatusChange);
    
    879
    +  },
    
    880
    +
    
    1032 881
       // init should be called by TorStartupService
    
    1033 882
       init() {
    
    1034 883
         lazy.logger.debug("TorConnect.init()");
    
    ... ... @@ -1048,6 +897,9 @@ export const TorConnect = {
    1048 897
         observeTopic(lazy.TorProviderTopics.ProcessExited);
    
    1049 898
         observeTopic(lazy.TorProviderTopics.HasWarnOrErr);
    
    1050 899
         observeTopic(lazy.TorSettingsTopics.SettingsChanged);
    
    900
    +    observeTopic(NETWORK_LINK_TOPIC);
    
    901
    +
    
    902
    +    this._updateInternetStatus();
    
    1051 903
     
    
    1052 904
         // NOTE: At this point, _requestedStage should still be `null`.
    
    1053 905
         this._setStage(TorConnectStage.Start);
    
    ... ... @@ -1109,6 +961,9 @@ export const TorConnect = {
    1109 961
               this._makeStageRequest(TorConnectStage.Start);
    
    1110 962
             }
    
    1111 963
             break;
    
    964
    +      case NETWORK_LINK_TOPIC:
    
    965
    +        this._updateInternetStatus();
    
    966
    +        break;
    
    1112 967
         }
    
    1113 968
       },
    
    1114 969
     
    
    ... ... @@ -1330,9 +1185,6 @@ export const TorConnect = {
    1330 1185
           bootstrapOptions.simulateDelay =
    
    1331 1186
             this.simulateBootstrapOptions.simulateDelay;
    
    1332 1187
         }
    
    1333
    -    if (this.simulateBootstrapOptions.simulateOffline) {
    
    1334
    -      bootstrapOptions.simulateOffline = true;
    
    1335
    -    }
    
    1336 1188
         if (this.simulateBootstrapOptions.simulateMoatResponse) {
    
    1337 1189
           bootstrapOptions.simulateMoatResponse =
    
    1338 1190
             this.simulateBootstrapOptions.simulateMoatResponse;
    
    ... ... @@ -1446,12 +1298,6 @@ export const TorConnect = {
    1446 1298
           ? new AutoBootstrapAttempt()
    
    1447 1299
           : new BootstrapAttempt();
    
    1448 1300
     
    
    1449
    -    if (!regionCode) {
    
    1450
    -      // Only test internet for the first bootstrap attempt.
    
    1451
    -      // TODO: Remove this since we do not have user consent. tor-browser#42605.
    
    1452
    -      bootstrapOptions.testInternet = true;
    
    1453
    -    }
    
    1454
    -
    
    1455 1301
         this._addSimulateOptions(bootstrapOptions, regionCode);
    
    1456 1302
     
    
    1457 1303
         // NOTE: The only `await` in this method is for `bootstrapAttempt.run`.
    
    ... ... @@ -1523,22 +1369,27 @@ export const TorConnect = {
    1523 1369
           return;
    
    1524 1370
         }
    
    1525 1371
     
    
    1526
    -    if (
    
    1527
    -      result === "offline" &&
    
    1528
    -      (beginStage === TorConnectStage.Start ||
    
    1529
    -        beginStage === TorConnectStage.Offline)
    
    1530
    -    ) {
    
    1531
    -      this._tryAgain = true;
    
    1532
    -      this._signalError(new TorConnectError(TorConnectError.Offline));
    
    1533
    -
    
    1534
    -      this._setStage(TorConnectStage.Offline);
    
    1535
    -      return;
    
    1536
    -    }
    
    1537
    -
    
    1538 1372
         if (error) {
    
    1539 1373
           lazy.logger.info("Bootstrap attempt error", error);
    
    1540
    -
    
    1541 1374
           this._tryAgain = true;
    
    1375
    +
    
    1376
    +      if (
    
    1377
    +        (beginStage === TorConnectStage.Start ||
    
    1378
    +          beginStage === TorConnectStage.Offline) &&
    
    1379
    +        (this.internetStatus === InternetStatus.Offline || this.simulateOffline)
    
    1380
    +      ) {
    
    1381
    +        // If we are currently offline, we assume the bootstrap error is caused
    
    1382
    +        // by a general internet connection problem, so we show an "Offline"
    
    1383
    +        // stage instead.
    
    1384
    +        // NOTE: In principle, we may determine that we are offline prior to the
    
    1385
    +        // bootstrap being thrown, but we do not want to cancel a bootstrap
    
    1386
    +        // attempt prematurely in case the offline state is intermittent or
    
    1387
    +        // incorrect.
    
    1388
    +        this._signalError(new TorConnectError(TorConnectError.Offline));
    
    1389
    +        this._setStage(TorConnectStage.Offline);
    
    1390
    +        return;
    
    1391
    +      }
    
    1392
    +
    
    1542 1393
           this._potentiallyBlocked = true;
    
    1543 1394
           // Disable quickstart until we have a successful bootstrap.
    
    1544 1395
           Services.prefs.setBoolPref(TorConnectPrefs.prompt_at_startup, true);