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 | *
|