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
-
dda5c0f2
by Henry Wilkes at 2026-07-29T11:05:04+01:00
-
3d9e8cbc
by Henry Wilkes at 2026-07-29T11:05:05+01:00
-
3dbd891f
by Henry Wilkes at 2026-07-29T11:05:06+01:00
9 changed files:
- browser/components/torconnect/TorConnectParent.sys.mjs
- browser/components/torconnect/content/aboutTorConnect.css
- browser/components/torconnect/content/aboutTorConnect.html
- browser/components/torconnect/content/aboutTorConnect.js
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/components/tor-launcher/tor-launcher.manifest
- toolkit/locales/en-US/toolkit/global/tor-browser.ftl
- toolkit/modules/RemotePageAccessManager.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
Changes:
| ... | ... | @@ -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); |
| ... | ... | @@ -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. */
|
| ... | ... | @@ -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
|
| ... | ... | @@ -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 | });
|
| ... | ... | @@ -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 | /**
|
| 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 |
| ... | ... | @@ -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.
|
| ... | ... | @@ -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"],
|
| ... | ... | @@ -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 | */
|