[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-153.0esr-16.0-1] 4 commits: fixup! TB 40933: Add tor-launcher functionality



Title: GitLab

henry pushed to branch tor-browser-153.0esr-16.0-1 at The Tor Project / Applications / Tor Browser

Commits:

  • e6adf55b
    by Henry Wilkes at 2026-07-29T11:02:33+01:00
    fixup! TB 40933: Add tor-launcher functionality
    
    TB 43186: Stop using `TorProviderBuilder` to prompt users on desktop.
    
    Also replace `settledState` with `currentState` and `settled`.
    
  • dda5c0f2
    by Henry Wilkes at 2026-07-29T11:05:04+01:00
    fixup! TB 40597: Implement TorSettings module
    
    TB 43186: TorConnect handles TorProvider initialisation errors.
    
    + Add the `"ProviderStopped"` stage, which we enter whenever the
      current `TorProvider` needs replacing.
    + In the `init` method we check `TorProvider.settledState` to see if the
      current provider is still running at startup. After this, we listen
      for the `ProviderStateChanged` event to see if it stops running later
      on.
    + We expose a `restartProvider` method to request a new provider. The
      `ProviderStatusChange` event allow consumers to update the UI.
    
  • 3d9e8cbc
    by Henry Wilkes at 2026-07-29T11:05:05+01:00
    fixup! TB 27476: Implement about:torconnect captive portal within Tor Browser
    
    TB 43186: Add a `"ProviderStopped"` page to about:torconnect.
    
  • 3dbd891f
    by Henry Wilkes at 2026-07-29T11:05:06+01:00
    fixup! Tor Browser strings
    
    TB 43186 - Add strings for the new `"ProviderStopped"` stage.
    

9 changed files:

Changes:

  • browser/components/torconnect/TorConnectParent.sys.mjs
    ... ... @@ -3,6 +3,7 @@
    3 3
     import { TorStrings } from "moz-src:///toolkit/modules/TorStrings.sys.mjs";
    
    4 4
     import {
    
    5 5
       TorConnect,
    
    6
    +  TorConnectStage,
    
    6 7
       TorConnectTopics,
    
    7 8
     } from "moz-src:///toolkit/modules/TorConnect.sys.mjs";
    
    8 9
     
    
    ... ... @@ -26,9 +27,7 @@ and a particular about:torconnect page
    26 27
      * It adapts and relays the messages from and to the TorConnect module.
    
    27 28
      */
    
    28 29
     export class TorConnectParent extends JSWindowActorParent {
    
    29
    -  constructor(...args) {
    
    30
    -    super(...args);
    
    31
    -
    
    30
    +  actorCreated() {
    
    32 31
         const self = this;
    
    33 32
     
    
    34 33
         // JSWindowActiveParent derived objects cannot observe directly, so create a
    
    ... ... @@ -44,6 +43,9 @@ export class TorConnectParent extends JSWindowActorParent {
    44 43
               case TorConnectTopics.StageChange:
    
    45 44
                 self.sendAsyncMessage("torconnect:stage-change", obj);
    
    46 45
                 break;
    
    46
    +          case TorConnectTopics.ProviderStatusChange:
    
    47
    +            self.sendAsyncMessage("torconnect:provider-status-change", obj);
    
    48
    +            break;
    
    47 49
               case TorConnectTopics.BootstrapProgress:
    
    48 50
                 self.sendAsyncMessage("torconnect:bootstrap-progress", obj);
    
    49 51
                 break;
    
    ... ... @@ -64,6 +66,10 @@ export class TorConnectParent extends JSWindowActorParent {
    64 66
           this.torConnectObserver,
    
    65 67
           TorConnectTopics.StageChange
    
    66 68
         );
    
    69
    +    Services.obs.addObserver(
    
    70
    +      this.torConnectObserver,
    
    71
    +      TorConnectTopics.ProviderStatusChange
    
    72
    +    );
    
    67 73
         Services.obs.addObserver(
    
    68 74
           this.torConnectObserver,
    
    69 75
           TorConnectTopics.BootstrapProgress
    
    ... ... @@ -124,6 +130,9 @@ export class TorConnectParent extends JSWindowActorParent {
    124 130
               Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit
    
    125 131
             );
    
    126 132
             break;
    
    133
    +      case "torconnect:restart-provider":
    
    134
    +        TorConnect.restartProvider();
    
    135
    +        break;
    
    127 136
           case "torconnect:start-again":
    
    128 137
             TorConnect.startAgain();
    
    129 138
             break;
    
    ... ... @@ -267,3 +276,13 @@ export class TorConnectParent extends JSWindowActorParent {
    267 276
         );
    
    268 277
       }
    
    269 278
     }
    
    279
    +
    
    280
    +Services.obs.addObserver((_subject, topic) => {
    
    281
    +  if (topic !== TorConnectTopics.StageChange) {
    
    282
    +    return;
    
    283
    +  }
    
    284
    +  if (TorConnect.stageName === TorConnectStage.ProviderStopped) {
    
    285
    +    // Make sure we have an open tab to show the ProviderStopped stage.
    
    286
    +    TorConnectParent.open();
    
    287
    +  }
    
    288
    +}, TorConnectTopics.StageChange);

  • browser/components/torconnect/content/aboutTorConnect.css
    ... ... @@ -235,40 +235,57 @@ form#locationDropdown select {
    235 235
       stroke: var(--icon-color);
    
    236 236
       /* Make non-interactive (non-draggable). */
    
    237 237
       pointer-events: none;
    
    238
    -}
    
    239 238
     
    
    240
    -.torconnect-icon.offline-icon {
    
    241
    -  content: url("chrome://browser/content/torconnect/network-broken.svg");
    
    242
    -}
    
    239
    +  &.offline-icon {
    
    240
    +    content: url("chrome://browser/content/torconnect/network-broken.svg");
    
    241
    +  }
    
    243 242
     
    
    244
    -.torconnect-icon.assist-icon {
    
    245
    -  content: url("chrome://browser/content/torconnect/tor-connect-broken.svg");
    
    246
    -}
    
    243
    +  &.assist-icon {
    
    244
    +    content: url("chrome://browser/content/torconnect/tor-connect-broken.svg");
    
    245
    +  }
    
    247 246
     
    
    248
    -.torconnect-icon.location-icon {
    
    249
    -  content: url("chrome://browser/content/torconnect/connection-location.svg");
    
    250
    -  stroke: var(--icon-color-warning);
    
    247
    +  &.location-icon {
    
    248
    +    content: url("chrome://browser/content/torconnect/connection-location.svg");
    
    249
    +    stroke: var(--icon-color-warning);
    
    250
    +  }
    
    251 251
     }
    
    252 252
     
    
    253 253
     .torconnect-heading {
    
    254 254
       grid-area: heading;
    
    255 255
       /* Do not show the focus outline. */
    
    256 256
       outline: none;
    
    257
    -  font-size: var(--font-size-xxxlarge);
    
    257
    +  font-size: var(--font-size-xxlarge);
    
    258 258
       margin: 0;
    
    259 259
       margin-block-end: var(--space-xxlarge);
    
    260 260
     }
    
    261 261
     
    
    262 262
     .torconnect-text-container {
    
    263 263
       grid-area: text-container;
    
    264
    +
    
    265
    +  & > *:not(:last-child) {
    
    266
    +    margin-block-end: var(--space-large);
    
    267
    +  }
    
    268
    +
    
    269
    +  & :is(p, ul) {
    
    270
    +    margin: 0;
    
    271
    +  }
    
    272
    +
    
    273
    +  & ul {
    
    274
    +    padding-inline-start: var(--space-xxlarge);
    
    275
    +  }
    
    276
    +
    
    277
    +  & li:not(:last-child) {
    
    278
    +    margin-block-end: var(--space-medium);
    
    279
    +  }
    
    264 280
     }
    
    265 281
     
    
    266
    -.torconnect-text-container p {
    
    267
    -  margin: 0;
    
    282
    +.torconnect-list-intro {
    
    283
    +  font-weight: var(--font-weight-semibold);
    
    268 284
     }
    
    269 285
     
    
    270
    -.torconnect-text-container > *:not(:last-child) {
    
    271
    -  margin-block-end: var(--space-large);
    
    286
    +body.hide-provider-config-debugging .provider-config-debugging,
    
    287
    +body:not(.hide-provider-config-debugging) .provider-generic-debugging {
    
    288
    +  display: none;
    
    272 289
     }
    
    273 290
     
    
    274 291
     .torconnect-controls-container {
    
    ... ... @@ -281,11 +298,33 @@ form#locationDropdown select {
    281 298
       align-items: center;
    
    282 299
       /* Gap between children and rows. */
    
    283 300
       gap: var(--space-small) var(--space-small);
    
    301
    +
    
    302
    +  & > * {
    
    303
    +    flex: 0 0 max-content;
    
    304
    +    margin: 0;
    
    305
    +  }
    
    284 306
     }
    
    285 307
     
    
    286
    -.torconnect-controls-container > * {
    
    287
    -  flex: 0 0 max-content;
    
    288
    -  margin: 0;
    
    308
    +#restart-provider-failed {
    
    309
    +  color: var(--text-color-error);
    
    310
    +  align-items: center;
    
    311
    +  gap: var(--space-xxsmall);
    
    312
    +
    
    313
    +  &:not([hidden]) {
    
    314
    +    display: flex;
    
    315
    +  }
    
    316
    +
    
    317
    +  & > * {
    
    318
    +    flex: 0 0 max-content;
    
    319
    +  }
    
    320
    +}
    
    321
    +
    
    322
    +#restart-provider-failed-icon {
    
    323
    +  width: var(--icon-size);
    
    324
    +  height: var(--icon-size);
    
    325
    +  -moz-context-properties: fill;
    
    326
    +  fill: var(--icon-color-critical);
    
    327
    +  content: url("chrome://global/skin/icons/error.svg");
    
    289 328
     }
    
    290 329
     
    
    291 330
     /* Temporary hacks for the connectPageContainer. */
    

  • browser/components/torconnect/content/aboutTorConnect.html
    ... ... @@ -15,12 +15,22 @@
    15 15
           href="">"chrome://browser/content/torconnect/aboutTorConnect.css"
    
    16 16
         />
    
    17 17
     
    
    18
    +    <link rel="localization" href="">"branding/brand.ftl" />
    
    18 19
         <link rel="localization" href="">"toolkit/global/tor-browser.ftl" />
    
    20
    +    <link rel="localization" href="">"toolkit/global/mozSupportLink.ftl" />
    
    19 21
     
    
    22
    +    <script
    
    23
    +      type="module"
    
    24
    +      src="">"chrome://global/content/elements/moz-button.mjs"
    
    25
    +    ></script>
    
    20 26
         <script
    
    21 27
           type="module"
    
    22 28
           src="">"chrome://global/content/elements/moz-toggle.mjs"
    
    23 29
         ></script>
    
    30
    +    <script
    
    31
    +      type="module"
    
    32
    +      src="">"chrome://global/content/elements/moz-support-link.mjs"
    
    33
    +    ></script>
    
    24 34
       </head>
    
    25 35
       <body class="onion-pattern-background">
    
    26 36
         <div id="progressBar" hidden="hidden">
    
    ... ... @@ -50,6 +60,86 @@
    50 60
             <span class="breadcrumb-label"></span>
    
    51 61
           </span>
    
    52 62
         </div>
    
    63
    +    <div class="torconnect-stage-content" data-stage-name="ProviderStopped">
    
    64
    +      <img class="torconnect-icon assist-icon" alt="" />
    
    65
    +      <h1
    
    66
    +        class="torconnect-heading"
    
    67
    +        tabindex="-1"
    
    68
    +        data-l10n-id="tor-connect-tor-not-working-heading"
    
    69
    +      ></h1>
    
    70
    +      <div class="torconnect-text-container">
    
    71
    +        <p data-l10n-id="tor-connect-tor-not-working-intro"></p>
    
    72
    +        <p
    
    73
    +          class="torconnect-list-intro"
    
    74
    +          data-l10n-id="tor-connect-tor-not-working-cause-intro"
    
    75
    +        ></p>
    
    76
    +        <ul>
    
    77
    +          <li
    
    78
    +            class="provider-config-debugging"
    
    79
    +            data-l10n-id="tor-connect-tor-not-working-cause-applications"
    
    80
    +          ></li>
    
    81
    +          <li
    
    82
    +            class="provider-config-debugging"
    
    83
    +            data-l10n-id="tor-connect-tor-not-working-cause-files"
    
    84
    +          ></li>
    
    85
    +          <li
    
    86
    +            class="provider-config-debugging"
    
    87
    +            data-l10n-id="tor-connect-tor-not-working-cause-other"
    
    88
    +          ></li>
    
    89
    +          <li
    
    90
    +            class="provider-generic-debugging"
    
    91
    +            data-l10n-id="tor-connect-tor-not-working-cause-generic"
    
    92
    +          ></li>
    
    93
    +        </ul>
    
    94
    +        <p
    
    95
    +          class="torconnect-list-intro"
    
    96
    +          data-l10n-id="tor-connect-tor-not-working-resolve-intro"
    
    97
    +        ></p>
    
    98
    +        <ul>
    
    99
    +          <li
    
    100
    +            class="provider-config-debugging"
    
    101
    +            data-l10n-id="tor-connect-tor-not-working-resolve-applications"
    
    102
    +          ></li>
    
    103
    +          <li
    
    104
    +            class="provider-config-debugging"
    
    105
    +            data-l10n-id="tor-connect-tor-not-working-resolve-files"
    
    106
    +          ></li>
    
    107
    +          <li
    
    108
    +            class="provider-config-debugging"
    
    109
    +            data-l10n-id="tor-connect-tor-not-working-resolve-restart-long"
    
    110
    +          ></li>
    
    111
    +          <li
    
    112
    +            class="provider-generic-debugging"
    
    113
    +            data-l10n-id="tor-connect-tor-not-working-resolve-restart-short"
    
    114
    +          ></li>
    
    115
    +        </ul>
    
    116
    +        <p data-l10n-id="tor-connect-tor-not-working-support">
    
    117
    +          <a
    
    118
    +            is="moz-support-link"
    
    119
    +            data-l10n-name="support-link"
    
    120
    +            support-page="tor-manual:get-in-touch__bug-or-feedback"
    
    121
    +          >
    
    122
    +          </a>
    
    123
    +        </p>
    
    124
    +        <a
    
    125
    +          is="moz-support-link"
    
    126
    +          support-page="tor-manual:encountering-issues__troubleshooting"
    
    127
    +        ></a>
    
    128
    +      </div>
    
    129
    +      <div class="torconnect-controls-container">
    
    130
    +        <div id="restart-provider-failed" role="alert">
    
    131
    +          <img id="restart-provider-failed-icon" alt="" />
    
    132
    +          <span
    
    133
    +            data-l10n-id="tor-connect-tor-not-working-restarting-failed"
    
    134
    +          ></span>
    
    135
    +        </div>
    
    136
    +        <moz-button
    
    137
    +          id="restart-provider-button"
    
    138
    +          type="primary"
    
    139
    +          data-l10n-id="tor-connect-tor-not-working-restart-button"
    
    140
    +        ></moz-button>
    
    141
    +      </div>
    
    142
    +    </div>
    
    53 143
         <div id="connectPageContainer" class="torconnect-stage-content">
    
    54 144
           <img id="tor-connect-icon" class="torconnect-icon" alt="" />
    
    55 145
           <h1
    

  • browser/components/torconnect/content/aboutTorConnect.js
    ... ... @@ -134,6 +134,8 @@ class AboutTorConnect {
    134 134
           "full-regions-option-group"
    
    135 135
         ),
    
    136 136
         tryBridgeButton: document.querySelector(this.selectors.buttons.tryBridge),
    
    137
    +    restartProviderButton: document.getElementById("restart-provider-button"),
    
    138
    +    restartProviderFailed: document.getElementById("restart-provider-failed"),
    
    137 139
       });
    
    138 140
     
    
    139 141
       /**
    
    ... ... @@ -418,6 +420,10 @@ class AboutTorConnect {
    418 420
             console.warn("Page opened whilst loading");
    
    419 421
             isLoaded = false;
    
    420 422
             break;
    
    423
    +      case "ProviderStopped":
    
    424
    +        this.hideBreadcrumbs();
    
    425
    +        this.updateProviderStatus(stage.providerStatus, true);
    
    426
    +        break;
    
    421 427
           case "Start":
    
    422 428
             this.showStart(stage.tryAgain, stage.potentiallyBlocked);
    
    423 429
             if (focusConnect) {
    
    ... ... @@ -479,6 +485,157 @@ class AboutTorConnect {
    479 485
         moveFocus.focus();
    
    480 486
       }
    
    481 487
     
    
    488
    +  /**
    
    489
    +   * The stored data for the `ProviderStopped` page content. `null` whilst
    
    490
    +   * uninitialized.
    
    491
    +   *
    
    492
    +   * @type {object}
    
    493
    +   */
    
    494
    +  _providerStoppedData = null;
    
    495
    +
    
    496
    +  /**
    
    497
    +   * Called whenever the tor provider status may have changed. Only relevant in
    
    498
    +   * the `ProviderStopped` stage.
    
    499
    +   *
    
    500
    +   * @param {ProviderStatus} providerStatus - The latest provider status.
    
    501
    +   * @param {boolean} enteringStage - Whether this is being called because we
    
    502
    +   *   have just entered the `ProviderStopped` stage as the initial stage of the
    
    503
    +   *   page, or transitioning from another stage.
    
    504
    +   */
    
    505
    +  updateProviderStatus(providerStatus, enteringStage) {
    
    506
    +    if (this.shownStage !== "ProviderStopped") {
    
    507
    +      // Ignore.
    
    508
    +      return;
    
    509
    +    }
    
    510
    +    if (enteringStage) {
    
    511
    +      // Reset the data:
    
    512
    +      // + When entering the stage.
    
    513
    +      // + When opening the page in this initial stage.
    
    514
    +      this._providerStoppedData = {
    
    515
    +        // Set to `null` to make sure we don't early exit below.
    
    516
    +        isRestarting: null,
    
    517
    +        forceShowRestarting: false,
    
    518
    +        canShowFailedAlert: false,
    
    519
    +        // We set the maybeConfigIssue when entering the stage, but do not attempt
    
    520
    +        // to update it between failed attempts.
    
    521
    +        maybeConfigIssue: providerStatus.maybeConfigIssue,
    
    522
    +      };
    
    523
    +    }
    
    524
    +
    
    525
    +    // We show the UI as restarting if the state is not "Stopped", which
    
    526
    +    // includes both "Starting" and "Running". In the latter case (which is
    
    527
    +    // unexpected) we still want to show the state as loading before we enter a
    
    528
    +    // different shownStage.
    
    529
    +    const restarting = providerStatus.state !== "Stopped";
    
    530
    +
    
    531
    +    const data = this._providerStoppedData;
    
    532
    +
    
    533
    +    if (restarting === data.isRestarting) {
    
    534
    +      // No change in the recorded state. Don't change the UI or adjust any
    
    535
    +      // timeouts.
    
    536
    +      return;
    
    537
    +    }
    
    538
    +
    
    539
    +    data.isRestarting = restarting;
    
    540
    +    if (restarting) {
    
    541
    +      // Whilst we remain in this `ProviderStopped` stage, we will now show the
    
    542
    +      // failed alert when we re-enter the "Stopped" state.
    
    543
    +      data.canShowFailedAlert = true;
    
    544
    +
    
    545
    +      if (!data.forceShowRestarting) {
    
    546
    +        // Force the restarting UI to show for at least a certain amount of
    
    547
    +        // time. Even if we fail early, we continue to show the restarting UI
    
    548
    +        // for a short time to make sure the user has enough time to visually
    
    549
    +        // see the restarting UI before we return to the error state.
    
    550
    +        data.forceShowRestarting = true;
    
    551
    +        setTimeout(() => {
    
    552
    +          if (
    
    553
    +            data !== this._providerStoppedData ||
    
    554
    +            this.shownStage !== "ProviderStopped"
    
    555
    +          ) {
    
    556
    +            // We left or re-entered this stage whilst waiting. Do nothing.
    
    557
    +            return;
    
    558
    +          }
    
    559
    +          data.forceShowRestarting = false;
    
    560
    +          this.updateProviderStoppedContent();
    
    561
    +        }, 1000);
    
    562
    +      }
    
    563
    +      // Else, we already have a pending timeout.
    
    564
    +      // E.g., consider the following timeline, where "R" represents entering
    
    565
    +      // the "Starting" state, "S" represents entering the "Stopped" state,
    
    566
    +      // "T0" represents the start of the forcedShowRestarting timeout, and "T1"
    
    567
    +      // represents the completion:
    
    568
    +      //
    
    569
    +      // |--------------+-----+-----+-----+----+------------------>
    
    570
    +      // S0             R0    S1    R1    S2
    
    571
    +      //                T0                     T1
    
    572
    +      // |--stopped UI--|--restarting UI-------|--stopped UI-------
    
    573
    +      //
    
    574
    +      //
    
    575
    +      // Similarly with S2 delayed:
    
    576
    +      //
    
    577
    +      // |--------------+-----+-----+----------+----+------------->
    
    578
    +      // S0             R0    S1    R1              S2
    
    579
    +      //                T0                     T1
    
    580
    +      // |--stopped UI--|--restarting UI------------|--stopped UI--
    
    581
    +      //
    
    582
    +      //
    
    583
    +      // I.e. we restart once (R0) which starts the timeout. Then we stop (S1) and
    
    584
    +      // restart (R1) again (though unlikely, this could happen if the user
    
    585
    +      // opens another `about:torconnect` tab after S1 and presses the restart
    
    586
    +      // button). Rather than create a new timeout, which would delay the
    
    587
    +      // restart display for even longer, we maintain the current one.
    
    588
    +    }
    
    589
    +
    
    590
    +    this.updateProviderStoppedContent();
    
    591
    +  }
    
    592
    +
    
    593
    +  /**
    
    594
    +   * Whether we are currently *showing* the tor provider as restarting in the
    
    595
    +   * UI.
    
    596
    +   *
    
    597
    +   * @type {boolean}
    
    598
    +   */
    
    599
    +  restartingProvider = false;
    
    600
    +
    
    601
    +  /**
    
    602
    +   * Called to update the page content for the `ProviderStopped` stage.
    
    603
    +   */
    
    604
    +  updateProviderStoppedContent() {
    
    605
    +    const {
    
    606
    +      isRestarting,
    
    607
    +      forceShowRestarting,
    
    608
    +      canShowFailedAlert,
    
    609
    +      maybeConfigIssue,
    
    610
    +    } = this._providerStoppedData;
    
    611
    +
    
    612
    +    // Hide any of the debugging information related to configuration issues if
    
    613
    +    // we do not suspect that this could be the cause.
    
    614
    +    document.body.classList.toggle(
    
    615
    +      "hide-provider-config-debugging",
    
    616
    +      !maybeConfigIssue
    
    617
    +    );
    
    618
    +
    
    619
    +    const showRestarting = isRestarting || forceShowRestarting;
    
    620
    +    this.elements.restartProviderFailed.hidden =
    
    621
    +      showRestarting || !canShowFailedAlert;
    
    622
    +
    
    623
    +    this.restartingProvider = showRestarting;
    
    624
    +    // The button is functionally disabled, but can still keep focus.
    
    625
    +    // TODO: tor-browser#45050. Disabled attribute is waiting on bugzilla bug
    
    626
    +    // 1927727.
    
    627
    +    // this.elements.restartProviderButton.disabled = showRestarting;
    
    628
    +    this.elements.restartProviderButton.iconSrc = showRestarting
    
    629
    +      ? "chrome://global/skin/icons/loading.svg"
    
    630
    +      : null;
    
    631
    +    document.l10n.setAttributes(
    
    632
    +      this.elements.restartProviderButton,
    
    633
    +      showRestarting
    
    634
    +        ? "tor-connect-tor-not-working-restarting-button"
    
    635
    +        : "tor-connect-tor-not-working-restart-button"
    
    636
    +    );
    
    637
    +  }
    
    638
    +
    
    482 639
       updateBootstrappingStatus(data) {
    
    483 640
         this.elements.progressMeter.style.setProperty(
    
    484 641
           "--progress-percent",
    
    ... ... @@ -816,6 +973,14 @@ class AboutTorConnect {
    816 973
           RPMSendAsyncMessage("torconnect:restart");
    
    817 974
         });
    
    818 975
     
    
    976
    +    this.elements.restartProviderButton.addEventListener("click", () => {
    
    977
    +      if (this.restartingProvider) {
    
    978
    +        // Ignore clicks whilst the button is shown as "Restarting".
    
    979
    +        return;
    
    980
    +      }
    
    981
    +      RPMSendAsyncMessage("torconnect:restart-provider");
    
    982
    +    });
    
    983
    +
    
    819 984
         this.elements.configureButton.textContent =
    
    820 985
           TorStrings.torConnect.torConfigure;
    
    821 986
         this.elements.configureButton.addEventListener("click", () => {
    
    ... ... @@ -896,6 +1061,9 @@ class AboutTorConnect {
    896 1061
         RPMAddMessageListener("torconnect:stage-change", ({ data }) => {
    
    897 1062
           this.updateStage(data);
    
    898 1063
         });
    
    1064
    +    RPMAddMessageListener("torconnect:provider-status-change", ({ data }) => {
    
    1065
    +      this.updateProviderStatus(data, false);
    
    1066
    +    });
    
    899 1067
         RPMAddMessageListener("torconnect:bootstrap-progress", ({ data }) => {
    
    900 1068
           this.updateBootstrappingStatus(data);
    
    901 1069
         });
    

  • toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
    ... ... @@ -204,13 +204,13 @@ export class TorProviderBuilder {
    204 204
         // I.e. it should be safe to call
    
    205 205
         //   TorProviderBuilder.init();
    
    206 206
         //   TorProviderBuilder.build();
    
    207
    -    //   TorProviderBuilder.settledState();
    
    207
    +    //   TorProviderBuilder.settled();
    
    208 208
         //   // etc
    
    209 209
         // without any await.
    
    210 210
         //
    
    211 211
         // In particular, this is needed by `TorConnect.init`, which will call
    
    212
    -    // `settledState`. It will also call `build` immediately if quickstart is
    
    213
    -    // set. See tor-browser#41921.
    
    212
    +    // `settled`. It will also call `build` immediately if quickstart is set.
    
    213
    +    // See tor-browser#41921.
    
    214 214
         if (this.#providerData) {
    
    215 215
           lazy.logger.info(
    
    216 216
             `Replacing the provider with a "${this.providerType}" provider.`
    
    ... ... @@ -372,21 +372,24 @@ export class TorProviderBuilder {
    372 372
       }
    
    373 373
     
    
    374 374
       /**
    
    375
    -   * Get the state of the current provider instance. Waits until the provider
    
    376
    -   * has finished initialisation first.
    
    375
    +   * Get the state of the current provider instance.
    
    377 376
        *
    
    378
    -   * If the provider has been replaced, the Stopped state will be returned.
    
    379
    -   *
    
    380
    -   * @returns {string} - The `TorProviderState` state for the provider that
    
    381
    -   *   existed when this method was called.
    
    377
    +   * @returns {string} - The `TorProviderState` state for the current provider.
    
    382 378
        */
    
    383
    -  static async settledState() {
    
    379
    +  static currentState() {
    
    380
    +    this.#checkActive();
    
    381
    +    return this.#providerData.provider.state;
    
    382
    +  }
    
    383
    +
    
    384
    +  /**
    
    385
    +   * Wait until the current provider instance has finished initializing, so it
    
    386
    +   * is no longer in the `Starting` state.
    
    387
    +   */
    
    388
    +  static async settled() {
    
    384 389
         this.#checkActive();
    
    385
    -    const { provider, initPromise } = this.#providerData;
    
    386 390
         try {
    
    387
    -      await initPromise;
    
    391
    +      await this.#providerData.initPromise;
    
    388 392
         } catch {}
    
    389
    -    return this.#checkProviderState(provider);
    
    390 393
       }
    
    391 394
     
    
    392 395
       /**
    

  • toolkit/components/tor-launcher/tor-launcher.manifest
    1 1
     category profile-after-change TorStartupService @torproject.org/tor-startup-service;1
    2
    -category browser-first-window-ready moz-src:///toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs TorProviderBuilder.firstWindowLoaded

  • toolkit/locales/en-US/toolkit/global/tor-browser.ftl
    ... ... @@ -15,6 +15,37 @@ appmenu-open-tor-manual =
    15 15
     # Here "Tor" refers to the Tor network.
    
    16 16
     tor-connect-page-title = Connect to Tor
    
    17 17
     
    
    18
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    19
    +tor-connect-tor-not-working-heading = The Tor process has stopped working
    
    20
    +# Here "process" is a noun, referring to the execution of a computer program.
    
    21
    +tor-connect-tor-not-working-intro = The underlying process that controls your connection to the Tor network has stopped working.
    
    22
    +tor-connect-tor-not-working-cause-intro = What could be causing this?
    
    23
    +# "-brand-short-name" will be substituted with the localized name for "Tor Browser".
    
    24
    +tor-connect-tor-not-working-cause-applications = Another application that uses the Tor network is conflicting with { -brand-short-name }.
    
    25
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    26
    +tor-connect-tor-not-working-cause-files = The files used by the Tor process have been modified or removed.
    
    27
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    28
    +tor-connect-tor-not-working-cause-other = The Tor process has stopped working due to a different technical issue.
    
    29
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    30
    +tor-connect-tor-not-working-cause-generic = The Tor process has stopped working due to a technical issue.
    
    31
    +tor-connect-tor-not-working-resolve-intro = What can you do about it?
    
    32
    +tor-connect-tor-not-working-resolve-applications = Quit any other applications that use the Tor network, or reconfigure them.
    
    33
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    34
    +tor-connect-tor-not-working-resolve-files = If you have modified the files used by the Tor process, undo these changes.
    
    35
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    36
    +tor-connect-tor-not-working-resolve-restart-long = Once you have followed these steps, or made any other change, try restarting the Tor process (this won’t close your browser tabs).
    
    37
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    38
    +tor-connect-tor-not-working-resolve-restart-short = Try restarting the Tor process (this won’t close your browser tabs).
    
    39
    +# The translation of "support channels" should also be wrapped in '<a data-l10n-name="support-link">' and '</a>', which will link to the corresponding support page.
    
    40
    +tor-connect-tor-not-working-support = If the problem remains, get in contact with us through one of our <a data-l10n-name="support-link">support channels</a>.
    
    41
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    42
    +tor-connect-tor-not-working-restart-button =
    
    43
    +    .label = Restart Tor process
    
    44
    +# Here "process" is a noun, referring to the execution of a computer program. "Tor process" specifically refers to the execution of the tor program, which is the program that handles the connection to the "Tor network".
    
    45
    +tor-connect-tor-not-working-restarting-button =
    
    46
    +    .label = Restarting Tor process…
    
    47
    +tor-connect-tor-not-working-restarting-failed = Restarting failed
    
    48
    +
    
    18 49
     ## Tor Browser home page.
    
    19 50
     
    
    20 51
     tor-browser-home-heading-stable = Explore. Privately.
    

  • toolkit/modules/RemotePageAccessManager.sys.mjs
    ... ... @@ -296,6 +296,7 @@ export let RemotePageAccessManager = {
    296 296
         "about:torconnect": {
    
    297 297
           RPMAddMessageListener: [
    
    298 298
             "torconnect:stage-change",
    
    299
    +        "torconnect:provider-status-change",
    
    299 300
             "torconnect:bootstrap-progress",
    
    300 301
             "torconnect:quickstart-change",
    
    301 302
             "torconnect:region-names-change",
    
    ... ... @@ -307,10 +308,12 @@ export let RemotePageAccessManager = {
    307 308
             "torconnect:set-quickstart",
    
    308 309
             "torconnect:view-tor-logs",
    
    309 310
             "torconnect:restart",
    
    311
    +        "torconnect:restart-provider",
    
    310 312
             "torconnect:start-again",
    
    311 313
             "torconnect:choose-region",
    
    312 314
           ],
    
    313 315
           RPMSendQuery: ["torconnect:get-init-args", "torconnect:get-regions"],
    
    316
    +      RPMGetFormatURLPref: ["app.support.baseURL"],
    
    314 317
         },
    
    315 318
         "about:welcome": {
    
    316 319
           RPMSendAsyncMessage: ["ActivityStream:ContentToMain"],
    

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -10,6 +10,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
    10 10
       MoatRPC: "moz-src:///toolkit/modules/Moat.sys.mjs",
    
    11 11
       TorBootstrapRequest:
    
    12 12
         "moz-src:///toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs",
    
    13
    +  TorProviderBuilder:
    
    14
    +    "moz-src:///toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs",
    
    13 15
       TorProviderState:
    
    14 16
         "moz-src:///toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs",
    
    15 17
       TorProviderTopics:
    
    ... ... @@ -81,11 +83,28 @@ ChromeUtils.defineLazyGetter(lazy, "logger", () =>
    81 83
     
    
    82 84
     /* Topics Notified by the TorConnect module */
    
    83 85
     export const TorConnectTopics = Object.freeze({
    
    86
    +  // Fired when the stage changes.
    
    87
    +  // The stage name from `TorConnectStage` is passed as the event subject.
    
    84 88
       StageChange: "torconnect:stage-change",
    
    89
    +  // Fired when the `TorConnect.quickstart` value changes.
    
    85 90
       QuickstartChange: "torconnect:quickstart-change",
    
    91
    +  // Fired when the `TorConnect.internetStatus` changes.
    
    86 92
       InternetStatusChange: "torconnect:internet-status-change",
    
    93
    +  // Fired when the `TorConnect.stage.providerStatus` changes.
    
    94
    +  // NOTE: The state of the provider may switch to `TorProviderState.Stopped`
    
    95
    +  // prior to `TorConnect` entering the stage `TorConnectStage.ProviderStopped`
    
    96
    +  // because a stage switch can be delayed. A consumer should always show the
    
    97
    +  // current `TorConnect` stage as the highest priority, and only react to this
    
    98
    +  // event when we are already in the `TorConnectStage.ProviderStopped` stage.
    
    99
    +  // The `ProviderStatus` object is passed as the event subject.
    
    100
    +  ProviderStatusChange: "torconnect:provider-status-change",
    
    101
    +  // Fired when the list of region names returned by `TorConnect.getRegionNames`
    
    102
    +  // may have changed.
    
    87 103
       RegionNamesChange: "torconnect:region-names-change",
    
    104
    +  // Fired when the `TorConnect.stage.bootstrapStatus` may have changed.
    
    105
    +  // The `BootstrapStatus` object is passed as the event subject.
    
    88 106
       BootstrapProgress: "torconnect:bootstrap-progress",
    
    107
    +  // Fired when we enter the `TorConnectStage.Bootstrapped` stage.
    
    89 108
       BootstrapComplete: "torconnect:bootstrap-complete",
    
    90 109
     });
    
    91 110
     
    
    ... ... @@ -655,6 +674,7 @@ export const InternetStatus = Object.freeze({
    655 674
     export const TorConnectStage = Object.freeze({
    
    656 675
       Disabled: "Disabled",
    
    657 676
       Loading: "Loading",
    
    677
    +  ProviderStopped: "ProviderStopped",
    
    658 678
       Start: "Start",
    
    659 679
       Bootstrapping: "Bootstrapping",
    
    660 680
       Offline: "Offline",
    
    ... ... @@ -689,6 +709,8 @@ export const TorConnectStage = Object.freeze({
    689 709
      *   have reached an error stage at some point before being bootstrapped.
    
    690 710
      * @property {BootstrappingStatus} bootstrappingStatus - The current
    
    691 711
      *   bootstrapping status.
    
    712
    + * @property {ProviderStatus} providerStatus - The current status of the Tor
    
    713
    + *   provider.
    
    692 714
      */
    
    693 715
     
    
    694 716
     /**
    
    ... ... @@ -714,6 +736,19 @@ export const TorConnectStage = Object.freeze({
    714 736
      * @property {?string} reason - The bootstrapping failure reason.
    
    715 737
      */
    
    716 738
     
    
    739
    +/**
    
    740
    + * @typedef {object} ProviderStatus
    
    741
    + *
    
    742
    + * The status of the current Tor provider.
    
    743
    + *
    
    744
    + * @property {string} state - The current `TorProviderState` state of the
    
    745
    + *   provider.
    
    746
    + * @property {boolean} maybeConfigIssue - Whether we suspect that a
    
    747
    + *   configuration issue (conflicting tor process or a mis-config) *may* have
    
    748
    + *   caused the provider to be stopped. This is a *soft* indicator for UI
    
    749
    + *   purposes to show additional debugging advice that might resolve the issue.
    
    750
    + */
    
    751
    +
    
    717 752
     export const TorConnect = {
    
    718 753
       /**
    
    719 754
        * Default bootstrap options for simulation.
    
    ... ... @@ -800,6 +835,52 @@ export const TorConnect = {
    800 835
        */
    
    801 836
       _potentiallyBlocked: false,
    
    802 837
     
    
    838
    +  /**
    
    839
    +   * The current `TorProviderState` state. `null` whilst we wait for
    
    840
    +   * `TorProviderBuilder.settled` to return.
    
    841
    +   *
    
    842
    +   * @type {?string}
    
    843
    +   */
    
    844
    +  _providerKnownState: null,
    
    845
    +
    
    846
    +  /**
    
    847
    +   * The current `TorProviderState` state. Whilst we wait for
    
    848
    +   * `TorProviderBuilder.settled` to return with the true state, we will assume
    
    849
    +   * that the provider is in the `TorProviderState.Running` state, as would be
    
    850
    +   * expected under normal circumstances.
    
    851
    +   */
    
    852
    +  get _providerState() {
    
    853
    +    return this._providerKnownState ?? lazy.TorProviderState.Running;
    
    854
    +  },
    
    855
    +
    
    856
    +  /**
    
    857
    +   * Convenience property for detecting when a provider is running.
    
    858
    +   *
    
    859
    +   * @type {boolean}
    
    860
    +   */
    
    861
    +  get _providerRunning() {
    
    862
    +    return this._providerState === lazy.TorProviderState.Running;
    
    863
    +  },
    
    864
    +
    
    865
    +  /**
    
    866
    +   * Whether we suspect a config issue cause the provider to fail.
    
    867
    +   *
    
    868
    +   * @type {boolean}
    
    869
    +   */
    
    870
    +  _maybeConfigIssue: false,
    
    871
    +
    
    872
    +  /**
    
    873
    +   * The latest provider status information for consumers.
    
    874
    +   *
    
    875
    +   * @returns {ProviderStatus} - The provider status.
    
    876
    +   */
    
    877
    +  _providerStatus() {
    
    878
    +    return {
    
    879
    +      state: this._providerState,
    
    880
    +      maybeConfigIssue: this._maybeConfigIssue,
    
    881
    +    };
    
    882
    +  },
    
    883
    +
    
    803 884
       /**
    
    804 885
        * Get a summary of the current user stage.
    
    805 886
        *
    
    ... ... @@ -822,6 +903,7 @@ export const TorConnect = {
    822 903
           tryAgain: this._tryAgain,
    
    823 904
           potentiallyBlocked: this._potentiallyBlocked,
    
    824 905
           bootstrappingStatus: structuredClone(this._bootstrappingStatus),
    
    906
    +      providerStatus: this._providerStatus(),
    
    825 907
         };
    
    826 908
       },
    
    827 909
     
    
    ... ... @@ -928,6 +1010,23 @@ export const TorConnect = {
    928 1010
     
    
    929 1011
         this._updateInternetStatus();
    
    930 1012
     
    
    1013
    +    // Do not await the `TorProviderBuilder` initialisation before we move to
    
    1014
    +    // the Start stage so that the UI can load quickly. We presume it will not
    
    1015
    +    // fail, but will switch to `ProviderStopped` if necessary.
    
    1016
    +    lazy.TorProviderBuilder.settled().then(() => {
    
    1017
    +      // NOTE: Assuming `TorConnect` is the only consumer of
    
    1018
    +      // `TorProviderBuilder` that will replace the provider, then the current
    
    1019
    +      // provider should be the *first* provider.
    
    1020
    +      const state = lazy.TorProviderBuilder.currentState();
    
    1021
    +      lazy.logger.debug(`Init state: ${state}`);
    
    1022
    +      this._setProviderState(state, true);
    
    1023
    +    });
    
    1024
    +
    
    1025
    +    if (!this._providerRunning) {
    
    1026
    +      // Promise resolved immediately, and failed. Should have already requested
    
    1027
    +      // the ProviderStopped stage.
    
    1028
    +      return;
    
    1029
    +    }
    
    931 1030
         // NOTE: At this point, _requestedStage should still be `null`.
    
    932 1031
         this._setStage(TorConnectStage.Start);
    
    933 1032
         if (
    
    ... ... @@ -955,19 +1054,13 @@ export const TorConnect = {
    955 1054
             }
    
    956 1055
             break;
    
    957 1056
           case lazy.TorProviderTopics.ProviderStateChanged:
    
    958
    -        if (data !== lazy.TorProviderState.Stopped) {
    
    959
    -          break;
    
    960
    -        }
    
    961
    -        lazy.logger.info("Starting again since the tor process exited");
    
    962
    -        // Treat a failure as a possibly broken configuration.
    
    963
    -        // So, prevent quickstart at the next start.
    
    964
    -        Services.prefs.setBoolPref(TorConnectPrefs.prompt_at_startup, true);
    
    965
    -        this._makeStageRequest(TorConnectStage.Start, true);
    
    1057
    +        this._setProviderState(data, false);
    
    966 1058
             break;
    
    967 1059
           case lazy.TorSettingsTopics.SettingsChanged:
    
    968 1060
             if (
    
    969 1061
               this._stageName !== TorConnectStage.Bootstrapped &&
    
    970 1062
               this._stageName !== TorConnectStage.Loading &&
    
    1063
    +          this._stageName !== TorConnectStage.ProviderStopped &&
    
    971 1064
               this._stageName !== TorConnectStage.Start &&
    
    972 1065
               subject.wrappedJSObject.changes.some(propertyName =>
    
    973 1066
                 propertyName.startsWith("bridges.")
    
    ... ... @@ -1009,9 +1102,20 @@ export const TorConnect = {
    1009 1102
        * @param {string} name - The name of the stage to move to.
    
    1010 1103
        */
    
    1011 1104
       _setStage(name) {
    
    1105
    +    if (this._stageName === TorConnectStage.Disabled) {
    
    1106
    +      throw new Error(`Trying to set the stage to ${name} when disabled`);
    
    1107
    +    }
    
    1012 1108
         if (this._bootstrapAttempt) {
    
    1013 1109
           throw new Error(`Trying to set the stage to ${name} during a bootstrap`);
    
    1014 1110
         }
    
    1111
    +    if (!this._providerRunning && name !== TorConnectStage.ProviderStopped) {
    
    1112
    +      if (!lazy.TorLauncherUtil.isAndroid) {
    
    1113
    +        // TODO: Remove Android exception.
    
    1114
    +        throw new Error(
    
    1115
    +          `Trying to set the stage to ${name} when provider is not running`
    
    1116
    +        );
    
    1117
    +      }
    
    1118
    +    }
    
    1015 1119
     
    
    1016 1120
         lazy.logger.info(`Entering stage ${name}`);
    
    1017 1121
         this._stageName = name;
    
    ... ... @@ -1211,6 +1315,13 @@ export const TorConnect = {
    1211 1315
           return false;
    
    1212 1316
         }
    
    1213 1317
     
    
    1318
    +    if (!this._providerRunning) {
    
    1319
    +      lazy.logger.warn(
    
    1320
    +        `Tor provider is not running. Ignoring request with ${regionCode}.`
    
    1321
    +      );
    
    1322
    +      return false;
    
    1323
    +    }
    
    1324
    +
    
    1214 1325
         const currentStage = this._stageName;
    
    1215 1326
     
    
    1216 1327
         if (regionCode) {
    
    ... ... @@ -1315,7 +1426,7 @@ export const TorConnect = {
    1315 1426
           this._defaultRegion = bootstrapAttempt.detectedRegion;
    
    1316 1427
         }
    
    1317 1428
     
    
    1318
    -    if (result === "complete") {
    
    1429
    +    if (result === "complete" && this._providerRunning) {
    
    1319 1430
           // Reset tryAgain, potentiallyBlocked and errorDetails in case the tor
    
    1320 1431
           // process exists later on.
    
    1321 1432
           this._tryAgain = false;
    
    ... ... @@ -1351,15 +1462,10 @@ export const TorConnect = {
    1351 1462
           this._tryAgain = true;
    
    1352 1463
     
    
    1353 1464
           if (error instanceof lazy.TorProviderInitError) {
    
    1354
    -        // Treat like TorProviderTopics.ProviderStateChanged. We expect a user
    
    1355
    -        // notification when this happens.
    
    1356
    -        // Treat a failure as a possibly broken configuration.
    
    1357
    -        // So, prevent quickstart at the next start.
    
    1358
    -        Services.prefs.setBoolPref(TorConnectPrefs.prompt_at_startup, true);
    
    1359
    -        lazy.logger.info(
    
    1360
    -          "Starting again since the tor provider failed to initialise"
    
    1361
    -        );
    
    1362
    -        this._setStage(TorConnectStage.Start);
    
    1465
    +        // Unexpected. We expect TorProviderTopics.ProviderStateChanged to have
    
    1466
    +        // already been called. I.e. the requestStage should be set.
    
    1467
    +        lazy.logger.error("Unexpected TorProviderInitError");
    
    1468
    +        this._setProviderState(lazy.TorProviderBuilder.currentState(), false);
    
    1363 1469
             return;
    
    1364 1470
           }
    
    1365 1471
     
    
    ... ... @@ -1489,14 +1595,27 @@ export const TorConnect = {
    1489 1595
           lazy.logger.warn(`Cannot move to ${stage} when bootstrapped`);
    
    1490 1596
           return;
    
    1491 1597
         }
    
    1598
    +    if (!this._providerRunning && stage !== TorConnectStage.ProviderStopped) {
    
    1599
    +      if (!lazy.TorLauncherUtil.isAndroid) {
    
    1600
    +        // TODO: Remove Android exception.
    
    1601
    +        lazy.logger.warn(
    
    1602
    +          `Cannot move to ${stage} when provider is not running`
    
    1603
    +        );
    
    1604
    +        return;
    
    1605
    +      }
    
    1606
    +    }
    
    1492 1607
         if (this._stageName === TorConnectStage.Loading) {
    
    1493
    -      if (stage === TorConnectStage.Start) {
    
    1494
    -        // Will transition to "Start" stage when loading completes.
    
    1495
    -        lazy.logger.info("Still in the Loading stage");
    
    1608
    +      if (stage === TorConnectStage.ProviderStopped) {
    
    1609
    +        lazy.logger.info("Skipping straight from Loading to ProviderStopped");
    
    1496 1610
           } else {
    
    1497
    -        lazy.logger.warn(`Cannot move to ${stage} when Loading`);
    
    1611
    +        if (stage === TorConnectStage.Start) {
    
    1612
    +          // Will transition to "Start" stage when loading completes.
    
    1613
    +          lazy.logger.info("Still in the Loading stage");
    
    1614
    +        } else {
    
    1615
    +          lazy.logger.warn(`Cannot move to ${stage} when Loading`);
    
    1616
    +        }
    
    1617
    +        return;
    
    1498 1618
           }
    
    1499
    -      return;
    
    1500 1619
         }
    
    1501 1620
     
    
    1502 1621
         if (!this._bootstrapAttempt) {
    
    ... ... @@ -1519,6 +1638,116 @@ export const TorConnect = {
    1519 1638
         this._bootstrapAttempt?.cancel();
    
    1520 1639
       },
    
    1521 1640
     
    
    1641
    +  /**
    
    1642
    +   * Called when the current Tor provider's state changes.
    
    1643
    +   *
    
    1644
    +   * @param {string} state - The `TorProviderState` we are now in.
    
    1645
    +   * @param {boolean} initialState - Whether this is the initial provider's
    
    1646
    +   *   state.
    
    1647
    +   */
    
    1648
    +  _setProviderState(state, initialState) {
    
    1649
    +    if (this._providerKnownState === null && !initialState) {
    
    1650
    +      // Wait for `TorProviderBuilder.settled` to return first.
    
    1651
    +      // We stick to the assumed status for the time being.
    
    1652
    +      return;
    
    1653
    +    }
    
    1654
    +
    
    1655
    +    const wasRunning = this._providerRunning;
    
    1656
    +    const prevState = this._providerState;
    
    1657
    +    this._providerKnownState = state;
    
    1658
    +
    
    1659
    +    if (prevState === state) {
    
    1660
    +      // No change in state.
    
    1661
    +      // NOTE: If this is the initial state, then `prevState` will be
    
    1662
    +      // `TorProviderState.Running`, in which case this will *not* notify
    
    1663
    +      // consumers if the initial state is also `Running`.
    
    1664
    +      return;
    
    1665
    +    }
    
    1666
    +
    
    1667
    +    if (this._providerRunning) {
    
    1668
    +      // As soon as we get a provider that is running (has not immediately
    
    1669
    +      // exited), from now on we will no longer *suspect* that a configuration
    
    1670
    +      // issue (conflicting tor process, or a mis-config) *could* cause a tor
    
    1671
    +      // provider to stop. I.e. if we ever re-enter the "Stopped" or "Starting"
    
    1672
    +      // state, `maybeConfigIssue` will remain `false`.
    
    1673
    +      //
    
    1674
    +      // This is because we assume that the browser's established tor process
    
    1675
    +      // can not be killed by the spawning of a new conflicting non-browser tor
    
    1676
    +      // process, and any changes in the configuration (via files or otherwise)
    
    1677
    +      // mid session won't be read by the existing process.
    
    1678
    +      //
    
    1679
    +      // NOTE: In principle, if the browser tor process is killed for some other
    
    1680
    +      // reason, in the interim there is a time period where the configuration
    
    1681
    +      // could change or a new non-browser tor process is spawned that could
    
    1682
    +      // conflict, which may prevent any *new* browser tor process from
    
    1683
    +      // starting. I.e. the cause of failure switches from non-config issue to
    
    1684
    +      // a config issue. However, such a scenario is deemed unlikely, and the
    
    1685
    +      // consequences of not accounting for it are very mild since
    
    1686
    +      // `maybeConfigIssue` is only soft hint for the debugging UI.
    
    1687
    +      this._maybeConfigIssue = false;
    
    1688
    +      if (!wasRunning) {
    
    1689
    +        // Return to the Start stage to re-try a bootstrap.
    
    1690
    +        this._makeStageRequest(TorConnectStage.Start);
    
    1691
    +      }
    
    1692
    +    } else {
    
    1693
    +      if (initialState) {
    
    1694
    +        // This is the first time we learn the provider state. If the first
    
    1695
    +        // provider is not running after initialisation, then we *suspect* that
    
    1696
    +        // there *could* be a conflict with another tor process. E.g. an
    
    1697
    +        // existing tor process is already using the ports we want to use, so
    
    1698
    +        // the browser's tor provider will fail at initialisation.
    
    1699
    +        this._maybeConfigIssue = true;
    
    1700
    +      }
    
    1701
    +      if (wasRunning) {
    
    1702
    +        // Transitioning from the Running state to a non-Running state.
    
    1703
    +
    
    1704
    +        // Prevent quickstart firing at the next startup until we have another
    
    1705
    +        // successful bootstrap.
    
    1706
    +        Services.prefs.setBoolPref(TorConnectPrefs.prompt_at_startup, true);
    
    1707
    +        // We request the ProviderStopped stage, which may take some time to
    
    1708
    +        // reach if we need to cancel the current bootstrap first.
    
    1709
    +        // But other methods should take into account that _providerRunning is now
    
    1710
    +        // `false` to early return and guarantee that we enter this
    
    1711
    +        // ProviderStopped stage.
    
    1712
    +        if (lazy.TorLauncherUtil.isAndroid) {
    
    1713
    +          // TODO: Remove this Android path when android supports the
    
    1714
    +          // `ProviderStopped` stage.
    
    1715
    +          this._makeStageRequest(TorConnectStage.Start, true);
    
    1716
    +        } else {
    
    1717
    +          this._makeStageRequest(TorConnectStage.ProviderStopped, true);
    
    1718
    +        }
    
    1719
    +      }
    
    1720
    +    }
    
    1721
    +
    
    1722
    +    const providerStatus = this._providerStatus();
    
    1723
    +    lazy.logger.debug("New provider status", providerStatus);
    
    1724
    +    Services.obs.notifyObservers(
    
    1725
    +      providerStatus,
    
    1726
    +      TorConnectTopics.ProviderStatusChange
    
    1727
    +    );
    
    1728
    +  },
    
    1729
    +
    
    1730
    +  /**
    
    1731
    +   * Restart the Tor provider after it has stopped.
    
    1732
    +   */
    
    1733
    +  restartProvider() {
    
    1734
    +    lazy.logger.debug("Request to restart Tor provider");
    
    1735
    +    this._ensureEnabled();
    
    1736
    +
    
    1737
    +    if (this._stageName !== TorConnectStage.ProviderStopped) {
    
    1738
    +      lazy.logger.warn(
    
    1739
    +        `Cannot restart the provider in stage ${this._stageName}`
    
    1740
    +      );
    
    1741
    +      return;
    
    1742
    +    }
    
    1743
    +    if (this._providerState === lazy.TorProviderState.Starting) {
    
    1744
    +      lazy.logger.warn("Already restarting the Tor provider");
    
    1745
    +      return;
    
    1746
    +    }
    
    1747
    +
    
    1748
    +    lazy.TorProviderBuilder.replace();
    
    1749
    +  },
    
    1750
    +
    
    1522 1751
       /**
    
    1523 1752
        * Restart the TorConnect stage to the start.
    
    1524 1753
        */
    

  • _______________________________________________
    tor-commits mailing list -- tor-commits@xxxxxxxxxxxxxxxxxxxx
    To unsubscribe send an email to tor-commits-leave@xxxxxxxxxxxxxxxxxxxx