Pier Angelo Vendrame pushed to branch tor-browser-128.6.0esr-14.5-1 at The Tor Project / Applications / Tor Browser
Commits:
-
4b7dc6cc
by Henry Wilkes at 2025-01-21T15:54:08+00:00
-
34cfa54a
by Henry Wilkes at 2025-01-21T15:54:13+00:00
-
1dc47dd9
by Henry Wilkes at 2025-01-21T15:54:14+00:00
-
fff7daea
by Henry Wilkes at 2025-01-21T15:54:14+00:00
4 changed files:
- toolkit/components/tor-launcher/TorBootstrapRequest.sys.mjs
- toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs
- toolkit/modules/TorAndroidIntegration.sys.mjs
- toolkit/modules/TorConnect.sys.mjs
Changes:
| ... | ... | @@ -90,6 +90,11 @@ export class TorBootstrapRequest { |
| 90 | 90 | // Wait for bootstrapping to begin and maybe handle error.
|
| 91 | 91 | // Notice that we do not resolve the promise here in case of success, but
|
| 92 | 92 | // we do it from the BootstrapStatus observer.
|
| 93 | + // NOTE: After TorProviderBuilder.build resolves, TorProvider.init will
|
|
| 94 | + // have completed. In particular, assuming no errors, the TorSettings will
|
|
| 95 | + // have been initialised and passed on to the provider via
|
|
| 96 | + // TorProvider.writeSettings. Therefore we should be safe to immediately
|
|
| 97 | + // call `connect` using the latest user settings.
|
|
| 93 | 98 | lazy.TorProviderBuilder.build()
|
| 94 | 99 | .then(provider => provider.connect())
|
| 95 | 100 | .catch(err => {
|
| ... | ... | @@ -54,14 +54,15 @@ export class TorProviderBuilder { |
| 54 | 54 | |
| 55 | 55 | /**
|
| 56 | 56 | * Initialize the provider of choice.
|
| 57 | - * Even though initialization is asynchronous, we do not expect the caller to
|
|
| 58 | - * await this method. The reason is that any call to build() will wait the
|
|
| 59 | - * initialization anyway (and re-throw any initialization error).
|
|
| 60 | 57 | */
|
| 61 | - static async init() {
|
|
| 58 | + static init() {
|
|
| 62 | 59 | switch (this.providerType) {
|
| 63 | 60 | case TorProviders.tor:
|
| 64 | - await this.#initTorProvider();
|
|
| 61 | + // Even though initialization of the initial TorProvider is
|
|
| 62 | + // asynchronous, we do not expect the caller to await it. The reason is
|
|
| 63 | + // that any call to build() will wait the initialization anyway (and
|
|
| 64 | + // re-throw any initialization error).
|
|
| 65 | + this.#initTorProvider();
|
|
| 65 | 66 | break;
|
| 66 | 67 | case TorProviders.none:
|
| 67 | 68 | lazy.TorLauncherUtil.setProxyConfiguration(
|
| ... | ... | @@ -74,7 +75,12 @@ export class TorProviderBuilder { |
| 74 | 75 | }
|
| 75 | 76 | }
|
| 76 | 77 | |
| 77 | - static async #initTorProvider() {
|
|
| 78 | + /**
|
|
| 79 | + * Replace #provider with a new instance.
|
|
| 80 | + *
|
|
| 81 | + * @returns {Promise<TorProvider>} The new instance.
|
|
| 82 | + */
|
|
| 83 | + static #initTorProvider() {
|
|
| 78 | 84 | if (!this.#exitObserver) {
|
| 79 | 85 | this.#exitObserver = this.#torExited.bind(this);
|
| 80 | 86 | Services.obs.addObserver(
|
| ... | ... | @@ -83,18 +89,39 @@ export class TorProviderBuilder { |
| 83 | 89 | );
|
| 84 | 90 | }
|
| 85 | 91 | |
| 92 | + // NOTE: We need to ensure that the #provider is set as soon
|
|
| 93 | + // TorProviderBuilder.init is called.
|
|
| 94 | + // I.e. it should be safe to call
|
|
| 95 | + // TorProviderBuilder.init();
|
|
| 96 | + // TorProviderBuilder.build();
|
|
| 97 | + // without any await.
|
|
| 98 | + //
|
|
| 99 | + // Therefore, we await the oldProvider within the Promise rather than make
|
|
| 100 | + // #initTorProvider async.
|
|
| 101 | + //
|
|
| 102 | + // In particular, this is needed by TorConnect when the user has selected
|
|
| 103 | + // quickstart, in which case `TorConnect.init` will immediately request the
|
|
| 104 | + // provider. See tor-browser#41921.
|
|
| 105 | + this.#provider = this.#replaceTorProvider(this.#provider);
|
|
| 106 | + return this.#provider;
|
|
| 107 | + }
|
|
| 108 | + |
|
| 109 | + /**
|
|
| 110 | + * Replace a TorProvider instance. Resolves once the TorProvider is
|
|
| 111 | + * initialised.
|
|
| 112 | + *
|
|
| 113 | + * @param {Promise<TorProvider>?} oldProvider - The previous's provider's
|
|
| 114 | + * promise, if any.
|
|
| 115 | + * @returns {TorProvider} The new TorProvider instance.
|
|
| 116 | + */
|
|
| 117 | + static async #replaceTorProvider(oldProvider) {
|
|
| 86 | 118 | try {
|
| 87 | - const old = await this.#provider;
|
|
| 88 | - old?.uninit();
|
|
| 119 | + // Uninitialise the old TorProvider, if there is any.
|
|
| 120 | + (await oldProvider)?.uninit();
|
|
| 89 | 121 | } catch {}
|
| 90 | - this.#provider = new Promise((resolve, reject) => {
|
|
| 91 | - const provider = new lazy.TorProvider();
|
|
| 92 | - provider
|
|
| 93 | - .init()
|
|
| 94 | - .then(() => resolve(provider))
|
|
| 95 | - .catch(reject);
|
|
| 96 | - });
|
|
| 97 | - await this.#provider;
|
|
| 122 | + const provider = new lazy.TorProvider();
|
|
| 123 | + await provider.init();
|
|
| 124 | + return provider;
|
|
| 98 | 125 | }
|
| 99 | 126 | |
| 100 | 127 | static uninit() {
|
| ... | ... | @@ -68,9 +68,12 @@ class TorAndroidIntegrationImpl { |
| 68 | 68 | Services.obs.addObserver(this, lazy.TorSettingsTopics[topic]);
|
| 69 | 69 | }
|
| 70 | 70 | |
| 71 | - lazy.TorProviderBuilder.init().finally(() => {
|
|
| 72 | - lazy.TorProviderBuilder.firstWindowLoaded();
|
|
| 73 | - });
|
|
| 71 | + lazy.TorProviderBuilder.init();
|
|
| 72 | + // On Android immediately call firstWindowLoaded. This should be safe to
|
|
| 73 | + // call since it will await the initialisation of the TorProvider set up
|
|
| 74 | + // by TorProviderBuilder.init.
|
|
| 75 | + lazy.TorProviderBuilder.firstWindowLoaded();
|
|
| 76 | + |
|
| 74 | 77 | try {
|
| 75 | 78 | await lazy.TorSettings.init();
|
| 76 | 79 | await lazy.TorConnect.init();
|
| ... | ... | @@ -10,7 +10,6 @@ ChromeUtils.defineESModuleGetters(lazy, { |
| 10 | 10 | BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
|
| 11 | 11 | MoatRPC: "resource://gre/modules/Moat.sys.mjs",
|
| 12 | 12 | TorBootstrapRequest: "resource://gre/modules/TorBootstrapRequest.sys.mjs",
|
| 13 | - TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
|
| 14 | 13 | TorProviderTopics: "resource://gre/modules/TorProviderBuilder.sys.mjs",
|
| 15 | 14 | TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
|
| 16 | 15 | TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
|
| ... | ... | @@ -436,15 +435,23 @@ class AutoBootstrapAttempt { |
| 436 | 435 | return;
|
| 437 | 436 | }
|
| 438 | 437 | this.#resolved = true;
|
| 439 | - try {
|
|
| 440 | - // Run cleanup before we resolve the promise to ensure two instances
|
|
| 441 | - // of AutoBootstrapAttempt are not trying to change the settings at
|
|
| 442 | - // the same time.
|
|
| 443 | - if (arg.result !== "complete") {
|
|
| 444 | - await lazy.TorSettings.clearTemporaryBridges();
|
|
| 438 | + |
|
| 439 | + if (lazy.TorSettings.initialized) {
|
|
| 440 | + // If not initialized, then we won't have applied any settings.
|
|
| 441 | + try {
|
|
| 442 | + // Run cleanup before we resolve the promise to ensure two instances
|
|
| 443 | + // of AutoBootstrapAttempt are not trying to change the settings at
|
|
| 444 | + // the same time.
|
|
| 445 | + if (arg.result !== "complete") {
|
|
| 446 | + // Should do nothing if we never called applyTemporaryBridges.
|
|
| 447 | + await lazy.TorSettings.clearTemporaryBridges();
|
|
| 448 | + }
|
|
| 449 | + } catch (error) {
|
|
| 450 | + lazy.logger.error(
|
|
| 451 | + "Unexpected error in auto-bootstrap cleanup",
|
|
| 452 | + error
|
|
| 453 | + );
|
|
| 445 | 454 | }
|
| 446 | - } catch (error) {
|
|
| 447 | - lazy.logger.error("Unexpected error in auto-bootstrap cleanup", error);
|
|
| 448 | 455 | }
|
| 449 | 456 | if (arg.error) {
|
| 450 | 457 | reject(arg.error);
|
| ... | ... | @@ -483,6 +490,16 @@ class AutoBootstrapAttempt { |
| 483 | 490 | * @param {BootstrapOptions} options - Options to apply to the bootstrap.
|
| 484 | 491 | */
|
| 485 | 492 | async #runInternal(progressCallback, options) {
|
| 493 | + // Wait for TorSettings to be initialised, which is used for the
|
|
| 494 | + // AutoBootstrapping set up.
|
|
| 495 | + await Promise.race([
|
|
| 496 | + lazy.TorSettings.initializedPromise,
|
|
| 497 | + this.#cancelledPromise,
|
|
| 498 | + ]);
|
|
| 499 | + if (this.#cancelled || this.#resolved) {
|
|
| 500 | + return;
|
|
| 501 | + }
|
|
| 502 | + |
|
| 486 | 503 | await this.#fetchBridges(options);
|
| 487 | 504 | if (this.#cancelled || this.#resolved) {
|
| 488 | 505 | return;
|
| ... | ... | @@ -1016,19 +1033,20 @@ export const TorConnect = { |
| 1016 | 1033 | lazy.logger.debug(`Observing topic '${addTopic}'`);
|
| 1017 | 1034 | };
|
| 1018 | 1035 | |
| 1019 | - // Wait for TorSettings, as we will need it.
|
|
| 1020 | - // We will wait for a TorProvider only after TorSettings is ready,
|
|
| 1021 | - // because the TorProviderBuilder initialization might not have finished
|
|
| 1022 | - // at this point, and TorSettings initialization is a prerequisite for
|
|
| 1023 | - // having a provider.
|
|
| 1024 | - // So, we prefer initializing TorConnect as soon as possible, so that
|
|
| 1025 | - // the UI will be able to detect it is in the Initializing state and act
|
|
| 1026 | - // consequently.
|
|
| 1027 | - lazy.TorSettings.initializedPromise.then(() => this._settingsInitialized());
|
|
| 1028 | - |
|
| 1029 | 1036 | // register the Tor topics we always care about
|
| 1030 | 1037 | observeTopic(lazy.TorProviderTopics.ProcessExited);
|
| 1031 | 1038 | observeTopic(lazy.TorProviderTopics.HasWarnOrErr);
|
| 1039 | + |
|
| 1040 | + // NOTE: At this point, _requestedStage should still be `null`.
|
|
| 1041 | + this._setStage(TorConnectStage.Start);
|
|
| 1042 | + if (
|
|
| 1043 | + // Quickstart setting is enabled.
|
|
| 1044 | + this.quickstart &&
|
|
| 1045 | + // And the previous bootstrap attempt must have succeeded.
|
|
| 1046 | + !Services.prefs.getBoolPref(TorConnectPrefs.prompt_at_startup, true)
|
|
| 1047 | + ) {
|
|
| 1048 | + this.beginBootstrapping();
|
|
| 1049 | + }
|
|
| 1032 | 1050 | },
|
| 1033 | 1051 | |
| 1034 | 1052 | async observe(subject, topic) {
|
| ... | ... | @@ -1058,26 +1076,6 @@ export const TorConnect = { |
| 1058 | 1076 | }
|
| 1059 | 1077 | },
|
| 1060 | 1078 | |
| 1061 | - async _settingsInitialized() {
|
|
| 1062 | - // TODO: Handle failures here, instead of the prompt to restart the
|
|
| 1063 | - // daemon when it exits (tor-browser#21053, tor-browser#41921).
|
|
| 1064 | - await lazy.TorProviderBuilder.build();
|
|
| 1065 | - |
|
| 1066 | - lazy.logger.debug("The TorProvider is ready, changing state.");
|
|
| 1067 | - // NOTE: If the tor process exits before this point, then
|
|
| 1068 | - // shouldQuickStart would be `false`.
|
|
| 1069 | - // NOTE: At this point, _requestedStage should still be `null`.
|
|
| 1070 | - this._setStage(TorConnectStage.Start);
|
|
| 1071 | - if (
|
|
| 1072 | - // Quickstart setting is enabled.
|
|
| 1073 | - this.quickstart &&
|
|
| 1074 | - // And the previous bootstrap attempt must have succeeded.
|
|
| 1075 | - !Services.prefs.getBoolPref(TorConnectPrefs.prompt_at_startup, true)
|
|
| 1076 | - ) {
|
|
| 1077 | - this.beginBootstrapping();
|
|
| 1078 | - }
|
|
| 1079 | - },
|
|
| 1080 | - |
|
| 1081 | 1079 | /**
|
| 1082 | 1080 | * Set the user stage.
|
| 1083 | 1081 | *
|