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

Commits:

13 changed files:

Changes:

  • mobile/android/components/geckoview/GeckoViewStartup.jsm
    ... ... @@ -5,6 +5,10 @@
    5 5
     
    
    6 6
     var EXPORTED_SYMBOLS = ["GeckoViewStartup"];
    
    7 7
     
    
    8
    +const { AppConstants } = ChromeUtils.importESModule(
    
    9
    +  "resource://gre/modules/AppConstants.sys.mjs"
    
    10
    +);
    
    11
    +
    
    8 12
     const { GeckoViewUtils } = ChromeUtils.importESModule(
    
    9 13
       "resource://gre/modules/GeckoViewUtils.sys.mjs"
    
    10 14
     );
    
    ... ... @@ -17,6 +21,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
    17 21
       PdfJs: "resource://pdf.js/PdfJs.sys.mjs",
    
    18 22
       Preferences: "resource://gre/modules/Preferences.sys.mjs",
    
    19 23
       RFPHelper: "resource://gre/modules/RFPHelper.sys.mjs",
    
    24
    +  TorAndroidIntegration: "resource://gre/modules/TorAndroidIntegration.sys.mjs",
    
    20 25
       TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
    
    21 26
     });
    
    22 27
     
    
    ... ... @@ -259,6 +264,7 @@ class GeckoViewStartup {
    259 264
               "GeckoView:SetLocale",
    
    260 265
             ]);
    
    261 266
     
    
    267
    +        lazy.TorAndroidIntegration.init();
    
    262 268
             lazy.TorDomainIsolator.init();
    
    263 269
     
    
    264 270
             Services.obs.addObserver(this, "browser-idle-startup-tasks-finished");
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntime.java
    ... ... @@ -167,7 +167,7 @@ public final class GeckoRuntime implements Parcelable {
    167 167
           if (!BuildConfig.TOR_BROWSER) {
    
    168 168
             GeckoNetworkManager.getInstance().start(GeckoAppShell.getApplicationContext());
    
    169 169
           } else {
    
    170
    -        Log.d(LOGTAG, "Tor Browser: skip GeckoNetworkManager startup"); 
    
    170
    +        Log.d(LOGTAG, "Tor Browser: skip GeckoNetworkManager startup");
    
    171 171
           }
    
    172 172
     
    
    173 173
           // Set settings that may have changed between last app opening
    
    ... ... @@ -230,6 +230,8 @@ public final class GeckoRuntime implements Parcelable {
    230 230
       private final ProfilerController mProfilerController;
    
    231 231
       private final GeckoScreenChangeListener mScreenChangeListener;
    
    232 232
     
    
    233
    +  private TorIntegrationAndroid mTorIntegration;
    
    234
    +
    
    233 235
       private GeckoRuntime() {
    
    234 236
         mWebExtensionController = new WebExtensionController(this);
    
    235 237
         mContentBlockingController = new ContentBlockingController();
    
    ... ... @@ -484,6 +486,8 @@ public final class GeckoRuntime implements Parcelable {
    484 486
           mScreenChangeListener.enable();
    
    485 487
         }
    
    486 488
     
    
    489
    +    mTorIntegration = new TorIntegrationAndroid(context);
    
    490
    +
    
    487 491
         mProfilerController.addMarker(
    
    488 492
             "GeckoView Initialization START", mProfilerController.getProfilerTime());
    
    489 493
         return true;
    
    ... ... @@ -600,6 +604,10 @@ public final class GeckoRuntime implements Parcelable {
    600 604
           mScreenChangeListener.disable();
    
    601 605
         }
    
    602 606
     
    
    607
    +    if (mTorIntegration != null) {
    
    608
    +      mTorIntegration.shutdown();
    
    609
    +    }
    
    610
    +
    
    603 611
         GeckoThread.forceQuit();
    
    604 612
       }
    
    605 613
     
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
    ... ... @@ -487,6 +487,11 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
    487 487
           getSettings().mPrioritizeOnions.set(flag);
    
    488 488
           return this;
    
    489 489
         }
    
    490
    +
    
    491
    +    public @NonNull Builder useNewBootstrap(final boolean flag) {
    
    492
    +      getSettings().mUseNewBootstrap.set(flag);
    
    493
    +      return this;
    
    494
    +    }
    
    490 495
       }
    
    491 496
     
    
    492 497
       private GeckoRuntime mRuntime;
    
    ... ... @@ -540,6 +545,8 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
    540 545
           new Pref<>("browser.security_level.security_slider", 4);
    
    541 546
       /* package */ final Pref<Boolean> mPrioritizeOnions =
    
    542 547
           new Pref<>("privacy.prioritizeonions.enabled", false);
    
    548
    +  /* package */ final Pref<Boolean> mUseNewBootstrap =
    
    549
    +      new Pref<>("browser.tor_android.use_new_bootstrap", false);
    
    543 550
     
    
    544 551
       /* package */ int mPreferredColorScheme = COLOR_SCHEME_SYSTEM;
    
    545 552
     
    
    ... ... @@ -1352,6 +1359,15 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
    1352 1359
         return this;
    
    1353 1360
       }
    
    1354 1361
     
    
    1362
    +  public boolean getUseNewBootstrap() {
    
    1363
    +    return mUseNewBootstrap.get();
    
    1364
    +  }
    
    1365
    +
    
    1366
    +  public @NonNull GeckoRuntimeSettings setUseNewBootstrap(final boolean flag) {
    
    1367
    +    mUseNewBootstrap.commit(flag);
    
    1368
    +    return this;
    
    1369
    +  }
    
    1370
    +
    
    1355 1371
       @Override // Parcelable
    
    1356 1372
       public void writeToParcel(final Parcel out, final int flags) {
    
    1357 1373
         super.writeToParcel(out, flags);
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
    ... ... @@ -2493,6 +2493,16 @@ public class GeckoSession {
    2493 2493
         return mEventDispatcher.queryBoolean("GeckoView:IsPdfJs");
    
    2494 2494
       }
    
    2495 2495
     
    
    2496
    +  /**
    
    2497
    +   * Try to get last circuit used in this session, if possible.
    
    2498
    +   *
    
    2499
    +   * @return The circuit information as a {@link GeckoResult} object.
    
    2500
    +   */
    
    2501
    +  @AnyThread
    
    2502
    +  public @NonNull GeckoResult<GeckoBundle> getTorCircuit() {
    
    2503
    +    return mEventDispatcher.queryBundle("GeckoView:GetTorCircuit");
    
    2504
    +  }
    
    2505
    +
    
    2496 2506
       /**
    
    2497 2507
        * Set this GeckoSession as active or inactive, which represents if the session is currently
    
    2498 2508
        * visible or not. Setting a GeckoSession to inactive will significantly reduce its memory
    

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorIntegrationAndroid.java
    1
    +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
    
    2
    + * vim: ts=4 sw=4 expandtab:
    
    3
    + * This Source Code Form is subject to the terms of the Mozilla Public
    
    4
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    
    5
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    6
    +
    
    7
    +package org.mozilla.geckoview;
    
    8
    +
    
    9
    +import android.content.Context;
    
    10
    +import android.util.Log;
    
    11
    +
    
    12
    +import java.io.BufferedReader;
    
    13
    +import java.io.File;
    
    14
    +import java.io.IOException;
    
    15
    +import java.io.InputStream;
    
    16
    +import java.io.InputStreamReader;
    
    17
    +import java.nio.file.Files;
    
    18
    +import java.nio.file.Path;
    
    19
    +import java.nio.file.Paths;
    
    20
    +import java.nio.file.StandardCopyOption;
    
    21
    +import java.nio.file.attribute.PosixFilePermission;
    
    22
    +import java.nio.file.attribute.PosixFilePermissions;
    
    23
    +import java.util.ArrayList;
    
    24
    +import java.util.HashMap;
    
    25
    +import java.util.Map;
    
    26
    +import java.util.Set;
    
    27
    +import java.util.UUID;
    
    28
    +
    
    29
    +import org.mozilla.gecko.EventDispatcher;
    
    30
    +import org.mozilla.gecko.GeckoAppShell;
    
    31
    +import org.mozilla.gecko.util.BundleEventListener;
    
    32
    +import org.mozilla.gecko.util.EventCallback;
    
    33
    +import org.mozilla.gecko.util.GeckoBundle;
    
    34
    +
    
    35
    +/* package */ class TorIntegrationAndroid implements BundleEventListener {
    
    36
    +    private static final String TAG = "TorIntegrationAndroid";
    
    37
    +
    
    38
    +    private static final String TOR_EVENT_START = "GeckoView:Tor:StartTor";
    
    39
    +    private static final String TOR_EVENT_STOP = "GeckoView:Tor:StopTor";
    
    40
    +    private static final String MEEK_EVENT_START = "GeckoView:Tor:StartMeek";
    
    41
    +    private static final String MEEK_EVENT_STOP = "GeckoView:Tor:StopMeek";
    
    42
    +
    
    43
    +    private static final String CONTROL_PORT_FILE = "/control-ipc";
    
    44
    +    private static final String SOCKS_FILE = "/socks-ipc";
    
    45
    +    private static final String COOKIE_AUTH_FILE = "/auth-file";
    
    46
    +
    
    47
    +    private final String mLibraryDir;
    
    48
    +    private final Path mCacheDir;
    
    49
    +    private final String mIpcDirectory;
    
    50
    +    private final String mDataDir;
    
    51
    +
    
    52
    +    private TorProcess mTorProcess = null;
    
    53
    +    /**
    
    54
    +     * The first time we run a Tor process in this session, we copy some configuration files to be
    
    55
    +     * sure we always have the latest version, but if we re-launch a tor process we do not need to
    
    56
    +     * copy them again.
    
    57
    +     */
    
    58
    +    private boolean mCopiedConfigFiles = false;
    
    59
    +    /**
    
    60
    +     * Allow multiple proxies to be started, even though it might not actually happen.
    
    61
    +     * The key should be positive (also 0 is not allowed).
    
    62
    +     */
    
    63
    +    private final HashMap<Integer, MeekTransport> mMeeks = new HashMap<>();
    
    64
    +    private int mMeekCounter;
    
    65
    +
    
    66
    +    public TorIntegrationAndroid(Context context) {
    
    67
    +        mLibraryDir = context.getApplicationInfo().nativeLibraryDir;
    
    68
    +        mCacheDir = context.getCacheDir().toPath();
    
    69
    +        mIpcDirectory = mCacheDir + "/tor-private";
    
    70
    +        mDataDir = context.getDataDir().getAbsolutePath() + "/tor";
    
    71
    +        registerListener();
    
    72
    +    }
    
    73
    +
    
    74
    +    public synchronized void shutdown() {
    
    75
    +        // FIXME: It seems this never gets called
    
    76
    +        if (mTorProcess != null) {
    
    77
    +            mTorProcess.shutdown();
    
    78
    +            mTorProcess = null;
    
    79
    +        }
    
    80
    +    }
    
    81
    +
    
    82
    +    private void registerListener() {
    
    83
    +        EventDispatcher.getInstance()
    
    84
    +                .registerUiThreadListener(
    
    85
    +                        this,
    
    86
    +                        TOR_EVENT_START,
    
    87
    +                        MEEK_EVENT_START,
    
    88
    +                        MEEK_EVENT_STOP);
    
    89
    +    }
    
    90
    +
    
    91
    +    @Override // BundleEventListener
    
    92
    +    public synchronized void handleMessage(
    
    93
    +            final String event, final GeckoBundle message, final EventCallback callback) {
    
    94
    +        if (TOR_EVENT_START.equals(event)) {
    
    95
    +            startDaemon(message, callback);
    
    96
    +        } else if (TOR_EVENT_STOP.equals(event)) {
    
    97
    +            stopDaemon(message, callback);
    
    98
    +        } else if (MEEK_EVENT_START.equals(event)) {
    
    99
    +            startMeek(message, callback);
    
    100
    +        } else if (MEEK_EVENT_STOP.equals(event)) {
    
    101
    +            stopMeek(message, callback);
    
    102
    +        }
    
    103
    +    }
    
    104
    +
    
    105
    +    private synchronized void startDaemon(final GeckoBundle message, final EventCallback callback) {
    
    106
    +        // Let JS generate this to possibly reduce the chance of race conditions.
    
    107
    +        String handle = message.getString("handle", "");
    
    108
    +        if (handle.isEmpty()) {
    
    109
    +            Log.e(TAG, "Requested to start a tor process without a handle.");
    
    110
    +            callback.sendError("Expected a handle for the new process.");
    
    111
    +            return;
    
    112
    +        }
    
    113
    +        Log.d(TAG, "Starting the a tor process with handle " + handle);
    
    114
    +
    
    115
    +        TorProcess previousProcess = mTorProcess;
    
    116
    +        if (previousProcess != null) {
    
    117
    +            Log.w(TAG, "We still have a running process: " + previousProcess.getHandle());
    
    118
    +        }
    
    119
    +        mTorProcess = new TorProcess(handle);
    
    120
    +
    
    121
    +        GeckoBundle bundle = new GeckoBundle(3);
    
    122
    +        bundle.putString("controlPortPath", mIpcDirectory + CONTROL_PORT_FILE);
    
    123
    +        bundle.putString("socksPath", mIpcDirectory + SOCKS_FILE);
    
    124
    +        bundle.putString("cookieFilePath", mIpcDirectory + COOKIE_AUTH_FILE);
    
    125
    +        callback.sendSuccess(bundle);
    
    126
    +    }
    
    127
    +
    
    128
    +    private synchronized void stopDaemon(final GeckoBundle message, final EventCallback callback) {
    
    129
    +        if (mTorProcess == null) {
    
    130
    +            if (callback != null) {
    
    131
    +                callback.sendSuccess(null);
    
    132
    +            }
    
    133
    +            return;
    
    134
    +        }
    
    135
    +        String handle = message.getString("handle", "");
    
    136
    +        if (!mTorProcess.getHandle().equals(handle)) {
    
    137
    +            GeckoBundle bundle = new GeckoBundle(1);
    
    138
    +            bundle.putString("error", "The requested process has not been found. It might have already been stopped.");
    
    139
    +            callback.sendError(bundle);
    
    140
    +            return;
    
    141
    +        }
    
    142
    +        mTorProcess.shutdown();
    
    143
    +        mTorProcess = null;
    
    144
    +        callback.sendSuccess(null);
    
    145
    +    }
    
    146
    +
    
    147
    +    class TorProcess extends Thread {
    
    148
    +        private static final String TOR_EVENT_STARTED = "GeckoView:Tor:TorStarted";
    
    149
    +        private static final String TOR_EVENT_START_FAILED = "GeckoView:Tor:TorStartFailed";
    
    150
    +        private static final String TOR_EVENT_EXITED = "GeckoView:Tor:TorExited";
    
    151
    +        private final String mHandle;
    
    152
    +        private Process mProcess = null;
    
    153
    +
    
    154
    +        TorProcess(String handle) {
    
    155
    +            mHandle = handle;
    
    156
    +            setName("tor-process-" + handle);
    
    157
    +            start();
    
    158
    +        }
    
    159
    +
    
    160
    +        @Override
    
    161
    +        public void run() {
    
    162
    +            cleanIpcDirectory();
    
    163
    +
    
    164
    +            final String ipcDir = TorIntegrationAndroid.this.mIpcDirectory;
    
    165
    +            final ArrayList<String> args = new ArrayList<>();
    
    166
    +            args.add(mLibraryDir + "/libTor.so");
    
    167
    +            args.add("DisableNetwork");
    
    168
    +            args.add("1");
    
    169
    +            args.add("+__ControlPort");
    
    170
    +            args.add("unix:" + ipcDir + CONTROL_PORT_FILE);
    
    171
    +            args.add("+__SocksPort");
    
    172
    +            args.add("unix:" + ipcDir + SOCKS_FILE + " IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth");
    
    173
    +            args.add("CookieAuthentication");
    
    174
    +            args.add("1");
    
    175
    +            args.add("CookieAuthFile");
    
    176
    +            args.add(ipcDir + COOKIE_AUTH_FILE);
    
    177
    +            args.add("DataDirectory");
    
    178
    +            args.add(mDataDir);
    
    179
    +            boolean copied = true;
    
    180
    +            try {
    
    181
    +                copyAndUseConfigFile("--defaults-torrc", "torrc-defaults", args);
    
    182
    +            } catch (IOException e) {
    
    183
    +                Log.w(TAG, "torrc-default cannot be created, pluggable transports will not be available", e);
    
    184
    +                copied = false;
    
    185
    +            }
    
    186
    +            try {
    
    187
    +                copyAndUseConfigFile("GeoIPFile", "geoip", args);
    
    188
    +                copyAndUseConfigFile("GeoIPv6File", "geoip6", args);
    
    189
    +            } catch (IOException e) {
    
    190
    +                Log.w(TAG, "GeoIP files cannot be created, this feature will not be available.", e);
    
    191
    +                copied = false;
    
    192
    +            }
    
    193
    +            mCopiedConfigFiles = copied;
    
    194
    +
    
    195
    +            Log.d(TAG, "Starting tor with the follwing args: " + args.toString());
    
    196
    +            final ProcessBuilder builder = new ProcessBuilder(args);
    
    197
    +            builder.directory(new File(mLibraryDir));
    
    198
    +            try {
    
    199
    +                mProcess = builder.start();
    
    200
    +            } catch (IOException e) {
    
    201
    +                Log.e(TAG, "Cannot start tor " + mHandle, e);
    
    202
    +                final GeckoBundle data = new GeckoBundle(2);
    
    203
    +                data.putString("handle", mHandle);
    
    204
    +                data.putString("error", e.getMessage());
    
    205
    +                EventDispatcher.getInstance().dispatch(TOR_EVENT_START_FAILED, data);
    
    206
    +                return;
    
    207
    +            }
    
    208
    +            Log.i(TAG, "Tor process " + mHandle + " started.");
    
    209
    +            {
    
    210
    +                final GeckoBundle data = new GeckoBundle(1);
    
    211
    +                data.putString("handle", mHandle);
    
    212
    +                EventDispatcher.getInstance().dispatch(TOR_EVENT_STARTED, data);
    
    213
    +            }
    
    214
    +            try {
    
    215
    +                BufferedReader reader = new BufferedReader(new InputStreamReader(mProcess.getInputStream()));
    
    216
    +                String line;
    
    217
    +                while ((line = reader.readLine()) != null) {
    
    218
    +                    Log.i(TAG, "[tor-" + mHandle + "] " + line);
    
    219
    +                }
    
    220
    +            } catch (IOException e) {
    
    221
    +                Log.e(TAG, "Failed to read stdout of the tor process " + mHandle, e);
    
    222
    +            }
    
    223
    +            Log.d(TAG, "Exiting the stdout loop for process " + mHandle);
    
    224
    +            final GeckoBundle data = new GeckoBundle(2);
    
    225
    +            data.putString("handle", mHandle);
    
    226
    +            try {
    
    227
    +                data.putInt("status", mProcess.waitFor());
    
    228
    +            } catch (InterruptedException e) {
    
    229
    +                Log.e(TAG, "Failed to wait for the tor process " + mHandle, e);
    
    230
    +                data.putInt("status", 0xdeadbeef);
    
    231
    +            }
    
    232
    +            // FIXME: We usually don't reach this when the application is killed!
    
    233
    +            // So, we don't do our cleanup.
    
    234
    +            Log.i(TAG, "Tor process " + mHandle + " has exited.");
    
    235
    +            EventDispatcher.getInstance().dispatch(TOR_EVENT_EXITED, data);
    
    236
    +        }
    
    237
    +
    
    238
    +        private void cleanIpcDirectory() {
    
    239
    +            File directory = new File(TorIntegrationAndroid.this.mIpcDirectory);
    
    240
    +            if (!Files.isDirectory(directory.toPath())) {
    
    241
    +                if (!directory.mkdirs()) {
    
    242
    +                    Log.e(TAG, "Failed to create the IPC directory.");
    
    243
    +                    return;
    
    244
    +                }
    
    245
    +                try {
    
    246
    +                    Set<PosixFilePermission> chmod = PosixFilePermissions.fromString("rwx------");
    
    247
    +                    Files.setPosixFilePermissions(directory.toPath(), chmod);
    
    248
    +                } catch (IOException e) {
    
    249
    +                    Log.e(TAG, "Could not set the permissions to the IPC directory.", e);
    
    250
    +                }
    
    251
    +                return;
    
    252
    +            }
    
    253
    +            // We assume we do not have child directories, only files
    
    254
    +            File[] maybeFiles = directory.listFiles();
    
    255
    +            if (maybeFiles != null) {
    
    256
    +                for (File file : maybeFiles) {
    
    257
    +                    if (!file.delete()) {
    
    258
    +                        Log.d(TAG, "Could not delete " + file);
    
    259
    +                    }
    
    260
    +                }
    
    261
    +            }
    
    262
    +        }
    
    263
    +
    
    264
    +        private void copyAndUseConfigFile(String option, String name, ArrayList<String> args) throws IOException {
    
    265
    +            final Path path = Paths.get(mCacheDir.toFile().getAbsolutePath(), name);
    
    266
    +            if (!mCopiedConfigFiles || !path.toFile().exists()) {
    
    267
    +                final Context context = GeckoAppShell.getApplicationContext();
    
    268
    +                final InputStream in = context.getAssets().open("common/" + name);
    
    269
    +                Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
    
    270
    +                in.close();
    
    271
    +            }
    
    272
    +            args.add(option);
    
    273
    +            args.add(path.toString());
    
    274
    +        }
    
    275
    +
    
    276
    +        public void shutdown() {
    
    277
    +            if (mProcess != null && mProcess.isAlive()) {
    
    278
    +                mProcess.destroy();
    
    279
    +            }
    
    280
    +            if (isAlive()) {
    
    281
    +                try {
    
    282
    +                    join();
    
    283
    +                } catch (InterruptedException e) {
    
    284
    +                    Log.e(TAG, "Cannot join the thread for tor process " + mHandle + ", possibly already terminated", e);
    
    285
    +                }
    
    286
    +            }
    
    287
    +        }
    
    288
    +
    
    289
    +        public String getHandle() {
    
    290
    +            return mHandle;
    
    291
    +        }
    
    292
    +    }
    
    293
    +
    
    294
    +    private synchronized void startMeek(final GeckoBundle message, final EventCallback callback) {
    
    295
    +        if (callback == null) {
    
    296
    +            Log.e(TAG, "Tried to start Meek without a callback.");
    
    297
    +            return;
    
    298
    +        }
    
    299
    +        mMeekCounter++;
    
    300
    +        mMeeks.put(new Integer(mMeekCounter), new MeekTransport(callback, mMeekCounter));
    
    301
    +    }
    
    302
    +
    
    303
    +    private synchronized void stopMeek(final GeckoBundle message, final EventCallback callback) {
    
    304
    +        final Integer key = message.getInteger("id");
    
    305
    +        final MeekTransport meek = mMeeks.remove(key);
    
    306
    +        if (meek != null) {
    
    307
    +            meek.shutdown();
    
    308
    +        }
    
    309
    +        if (callback != null) {
    
    310
    +            callback.sendSuccess(null);
    
    311
    +        }
    
    312
    +    }
    
    313
    +
    
    314
    +    private class MeekTransport extends Thread {
    
    315
    +        private static final String TRANSPORT = "meek_lite";
    
    316
    +        private Process mProcess;
    
    317
    +        private final EventCallback mCallback;
    
    318
    +        private final int mId;
    
    319
    +
    
    320
    +        MeekTransport(final EventCallback callback, int id) {
    
    321
    +            setName("meek-" + id);
    
    322
    +            final ProcessBuilder builder = new ProcessBuilder(mLibraryDir + "/libObfs4proxy.so");
    
    323
    +            {
    
    324
    +                final Map<String, String> env = builder.environment();
    
    325
    +                env.put("TOR_PT_MANAGED_TRANSPORT_VER", "1");
    
    326
    +                env.put("TOR_PT_STATE_LOCATION", mDataDir + "/pt_state");
    
    327
    +                env.put("TOR_PT_EXIT_ON_STDIN_CLOSE", "1");
    
    328
    +                env.put("TOR_PT_CLIENT_TRANSPORTS", TRANSPORT);
    
    329
    +            }
    
    330
    +            mCallback = callback;
    
    331
    +            mId = id;
    
    332
    +            try {
    
    333
    +                // We expect this process to be short-lived, therefore we do not bother with
    
    334
    +                // implementing this as a service.
    
    335
    +                mProcess = builder.start();
    
    336
    +            } catch (IOException e) {
    
    337
    +                Log.e(TAG, "Cannot start the PT", e);
    
    338
    +                callback.sendError(e.getMessage());
    
    339
    +                return;
    
    340
    +            }
    
    341
    +            start();
    
    342
    +        }
    
    343
    +
    
    344
    +        /**
    
    345
    +         * Parse the standard output of the pluggable transport to find the hostname and port it is
    
    346
    +         * listening on.
    
    347
    +         * <p>
    
    348
    +         * See also the specs for the IPC protocol at https://spec.torproject.org/pt-spec/ipc.html.
    
    349
    +         */
    
    350
    +        @Override
    
    351
    +        public void run() {
    
    352
    +            final String PROTOCOL_VERSION = "1";
    
    353
    +            String hostname = "";
    
    354
    +            boolean valid = false;
    
    355
    +            int port = 0;
    
    356
    +            String error = "Did not see a CMETHOD";
    
    357
    +            try {
    
    358
    +                InputStreamReader isr = new InputStreamReader(mProcess.getInputStream());
    
    359
    +                BufferedReader reader = new BufferedReader(isr);
    
    360
    +                String line;
    
    361
    +                while ((line = reader.readLine()) != null) {
    
    362
    +                    line = line.trim();
    
    363
    +                    Log.d(TAG, "Meek line: " + line);
    
    364
    +                    // Split produces always at least one item
    
    365
    +                    String[] tokens = line.split(" ");
    
    366
    +                    if ("VERSION".equals(tokens[0]) && (tokens.length != 2 || !PROTOCOL_VERSION.equals(tokens[1]))) {
    
    367
    +                        error = "Bad version: " + line;
    
    368
    +                        break;
    
    369
    +                    }
    
    370
    +                    if ("CMETHOD".equals(tokens[0])) {
    
    371
    +                        if (tokens.length != 4) {
    
    372
    +                            error = "Bad number of tokens in CMETHOD: " + line;
    
    373
    +                            break;
    
    374
    +                        }
    
    375
    +                        if (!tokens[1].equals(TRANSPORT)) {
    
    376
    +                            error = "Unexpected transport: " + tokens[1];
    
    377
    +                            break;
    
    378
    +                        }
    
    379
    +                        if (!"socks5".equals(tokens[2])) {
    
    380
    +                            error = "Unexpected proxy type: " + tokens[2];
    
    381
    +                            break;
    
    382
    +                        }
    
    383
    +                        String[] addr = tokens[3].split(":");
    
    384
    +                        if (addr.length != 2) {
    
    385
    +                            error = "Invalid address";
    
    386
    +                            break;
    
    387
    +                        }
    
    388
    +                        hostname = addr[0];
    
    389
    +                        try {
    
    390
    +                            port = Integer.parseInt(addr[1]);
    
    391
    +                        } catch (NumberFormatException e) {
    
    392
    +                            error = "Invalid port: " + e.getMessage();
    
    393
    +                            break;
    
    394
    +                        }
    
    395
    +                        if (port < 1 || port > 65535) {
    
    396
    +                            error = "Invalid port: out of bounds";
    
    397
    +                            break;
    
    398
    +                        }
    
    399
    +                        valid = true;
    
    400
    +                        break;
    
    401
    +                    }
    
    402
    +                    if (tokens[0].endsWith("-ERROR")) {
    
    403
    +                        error = "Seen an error: " + line;
    
    404
    +                        break;
    
    405
    +                    }
    
    406
    +                }
    
    407
    +            } catch (Exception e) {
    
    408
    +                error = e.getMessage();
    
    409
    +            }
    
    410
    +            if (valid) {
    
    411
    +                Log.d(TAG, "Setup a meek transport " + mId + ": " + hostname + ":" + port);
    
    412
    +                final GeckoBundle bundle = new GeckoBundle(3);
    
    413
    +                bundle.putInt("id", mId);
    
    414
    +                bundle.putString("address", hostname);
    
    415
    +                bundle.putInt("port", port);
    
    416
    +                mCallback.sendSuccess(bundle);
    
    417
    +            } else {
    
    418
    +                Log.e(TAG, "Failed to get a usable config from the PT: " + error);
    
    419
    +                mCallback.sendError(error);
    
    420
    +            }
    
    421
    +        }
    
    422
    +
    
    423
    +        void shutdown() {
    
    424
    +            if (mProcess != null) {
    
    425
    +                mProcess.destroy();
    
    426
    +                mProcess = null;
    
    427
    +            }
    
    428
    +            try {
    
    429
    +                join();
    
    430
    +            } catch (InterruptedException e) {
    
    431
    +                Log.e(TAG, "Could not join the meek thread", e);
    
    432
    +            }
    
    433
    +        }
    
    434
    +    }
    
    435
    +}

  • mobile/android/modules/geckoview/GeckoViewContent.sys.mjs
    ... ... @@ -4,6 +4,12 @@
    4 4
     
    
    5 5
     import { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs";
    
    6 6
     
    
    7
    +const lazy = {};
    
    8
    +
    
    9
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    10
    +  TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.sys.mjs",
    
    11
    +});
    
    12
    +
    
    7 13
     export class GeckoViewContent extends GeckoViewModule {
    
    8 14
       onInit() {
    
    9 15
         this.registerListener([
    
    ... ... @@ -22,6 +28,7 @@ export class GeckoViewContent extends GeckoViewModule {
    22 28
           "GeckoView:UpdateInitData",
    
    23 29
           "GeckoView:ZoomToInput",
    
    24 30
           "GeckoView:IsPdfJs",
    
    31
    +      "GeckoView:GetTorCircuit",
    
    25 32
         ]);
    
    26 33
       }
    
    27 34
     
    
    ... ... @@ -190,6 +197,21 @@ export class GeckoViewContent extends GeckoViewModule {
    190 197
           case "GeckoView:HasCookieBannerRuleForBrowsingContextTree":
    
    191 198
             this._hasCookieBannerRuleForBrowsingContextTree(aCallback);
    
    192 199
             break;
    
    200
    +      case "GeckoView:GetTorCircuit":
    
    201
    +        if (this.browser && aCallback) {
    
    202
    +          const domain = lazy.TorDomainIsolator.getDomainForBrowser(
    
    203
    +            this.browser
    
    204
    +          );
    
    205
    +          const nodes = lazy.TorDomainIsolator.getCircuit(
    
    206
    +            this.browser,
    
    207
    +            domain,
    
    208
    +            this.browser.contentPrincipal.originAttributes.userContextId
    
    209
    +          );
    
    210
    +          aCallback?.onSuccess({ domain, nodes });
    
    211
    +        } else {
    
    212
    +          aCallback?.onSuccess(null);
    
    213
    +        }
    
    214
    +        break;
    
    193 215
         }
    
    194 216
       }
    
    195 217
     
    

  • toolkit/components/tor-launcher/TorProcessAndroid.sys.mjs
    1
    +/* This Source Code Form is subject to the terms of the Mozilla Public
    
    2
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    
    3
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    4
    +
    
    5
    +import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
    
    6
    +
    
    7
    +const lazy = {};
    
    8
    +
    
    9
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    10
    +  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    11
    +});
    
    12
    +
    
    13
    +const logger = new ConsoleAPI({
    
    14
    +  maxLogLevel: "info",
    
    15
    +  prefix: "TorProcessAndroid",
    
    16
    +});
    
    17
    +
    
    18
    +const TorOutgoingEvents = Object.freeze({
    
    19
    +  start: "GeckoView:Tor:StartTor",
    
    20
    +  stop: "GeckoView:Tor:StopTor",
    
    21
    +});
    
    22
    +
    
    23
    +// The events we will listen to
    
    24
    +const TorIncomingEvents = Object.freeze({
    
    25
    +  started: "GeckoView:Tor:TorStarted",
    
    26
    +  startFailed: "GeckoView:Tor:TorStartFailed",
    
    27
    +  exited: "GeckoView:Tor:TorExited",
    
    28
    +});
    
    29
    +
    
    30
    +export class TorProcessAndroid {
    
    31
    +  /**
    
    32
    +   * The handle the Java counterpart uses to refer to the process we started.
    
    33
    +   * We use it to filter the exit events and make sure they refer to the daemon
    
    34
    +   * we are interested in.
    
    35
    +   */
    
    36
    +  #processHandle = null;
    
    37
    +  /**
    
    38
    +   * The promise resolver we call when the Java counterpart sends the event that
    
    39
    +   * tor has started.
    
    40
    +   */
    
    41
    +  #startResolve = null;
    
    42
    +  /**
    
    43
    +   * The promise resolver we call when the Java counterpart sends the event that
    
    44
    +   * it failed to start tor.
    
    45
    +   */
    
    46
    +  #startReject = null;
    
    47
    +
    
    48
    +  onExit = () => {};
    
    49
    +
    
    50
    +  get isRunning() {
    
    51
    +    return !!this.#processHandle;
    
    52
    +  }
    
    53
    +
    
    54
    +  async start() {
    
    55
    +    // Generate the handle on the JS side so that it's ready in case it takes
    
    56
    +    // less to start the process than to propagate the success.
    
    57
    +    this.#processHandle = crypto.randomUUID();
    
    58
    +    logger.info(`Starting new process with handle ${this.#processHandle}`);
    
    59
    +    // Let's declare it immediately, so that the Java side can do its stuff in
    
    60
    +    // an async manner and we avoid possible race conditions (at most we await
    
    61
    +    // an already resolved/rejected promise.
    
    62
    +    const startEventPromise = new Promise((resolve, reject) => {
    
    63
    +      this.#startResolve = resolve;
    
    64
    +      this.#startReject = reject;
    
    65
    +    });
    
    66
    +    lazy.EventDispatcher.instance.registerListener(
    
    67
    +      this,
    
    68
    +      Object.values(TorIncomingEvents)
    
    69
    +    );
    
    70
    +    let config;
    
    71
    +    try {
    
    72
    +      config = await lazy.EventDispatcher.instance.sendRequestForResult({
    
    73
    +        type: TorOutgoingEvents.start,
    
    74
    +        handle: this.#processHandle,
    
    75
    +      });
    
    76
    +      logger.debug("Sent the start event.");
    
    77
    +    } catch (e) {
    
    78
    +      this.forget();
    
    79
    +      throw e;
    
    80
    +    }
    
    81
    +    await startEventPromise;
    
    82
    +    return config;
    
    83
    +  }
    
    84
    +
    
    85
    +  forget() {
    
    86
    +    // Processes usually exit when we close the control port connection to them.
    
    87
    +    logger.trace(`Forgetting process ${this.#processHandle}`);
    
    88
    +    lazy.EventDispatcher.instance.sendRequestForResult({
    
    89
    +      type: TorOutgoingEvents.stop,
    
    90
    +      handle: this.#processHandle,
    
    91
    +    });
    
    92
    +    logger.debug("Sent the start event.");
    
    93
    +    this.#processHandle = null;
    
    94
    +    lazy.EventDispatcher.instance.unregisterListener(
    
    95
    +      this,
    
    96
    +      Object.values(TorIncomingEvents)
    
    97
    +    );
    
    98
    +  }
    
    99
    +
    
    100
    +  onEvent(event, data, callback) {
    
    101
    +    if (data?.handle !== this.#processHandle) {
    
    102
    +      logger.debug(`Ignoring event ${event} with another handle`, data);
    
    103
    +      return;
    
    104
    +    }
    
    105
    +    logger.info(`Received an event ${event}`, data);
    
    106
    +    switch (event) {
    
    107
    +      case TorIncomingEvents.started:
    
    108
    +        this.#startResolve();
    
    109
    +        break;
    
    110
    +      case TorIncomingEvents.startFailed:
    
    111
    +        this.#startReject(new Error(data.error));
    
    112
    +        break;
    
    113
    +      case TorIncomingEvents.exited:
    
    114
    +        this.forget();
    
    115
    +        if (this.#startReject !== null) {
    
    116
    +          this.#startReject();
    
    117
    +        }
    
    118
    +        this.onExit(data.status);
    
    119
    +        break;
    
    120
    +    }
    
    121
    +  }
    
    122
    +}

  • toolkit/components/tor-launcher/TorProvider.sys.mjs
    ... ... @@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
    14 14
       FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
    
    15 15
       TorController: "resource://gre/modules/TorControlPort.sys.mjs",
    
    16 16
       TorProcess: "resource://gre/modules/TorProcess.sys.mjs",
    
    17
    +  TorProcessAndroid: "resource://gre/modules/TorProcessAndroid.sys.mjs",
    
    17 18
     });
    
    18 19
     
    
    19 20
     const logger = new ConsoleAPI({
    
    ... ... @@ -182,8 +183,12 @@ export class TorProvider {
    182 183
         logger.debug("Initializing the Tor provider.");
    
    183 184
     
    
    184 185
         // These settings might be customized in the following steps.
    
    185
    -    this.#socksSettings = TorLauncherUtil.getPreferredSocksConfiguration();
    
    186
    -    logger.debug("Requested SOCKS configuration", this.#socksSettings);
    
    186
    +    if (TorLauncherUtil.isAndroid) {
    
    187
    +      this.#socksSettings = { transproxy: false };
    
    188
    +    } else {
    
    189
    +      this.#socksSettings = TorLauncherUtil.getPreferredSocksConfiguration();
    
    190
    +      logger.debug("Requested SOCKS configuration", this.#socksSettings);
    
    191
    +    }
    
    187 192
     
    
    188 193
         try {
    
    189 194
           await this.#setControlPortConfiguration();
    
    ... ... @@ -490,10 +495,14 @@ export class TorProvider {
    490 495
           return;
    
    491 496
         }
    
    492 497
     
    
    493
    -    this.#torProcess = new lazy.TorProcess(
    
    494
    -      this.#controlPortSettings,
    
    495
    -      this.#socksSettings
    
    496
    -    );
    
    498
    +    if (TorLauncherUtil.isAndroid) {
    
    499
    +      this.#torProcess = new lazy.TorProcessAndroid();
    
    500
    +    } else {
    
    501
    +      this.#torProcess = new lazy.TorProcess(
    
    502
    +        this.#controlPortSettings,
    
    503
    +        this.#socksSettings
    
    504
    +      );
    
    505
    +    }
    
    497 506
         // Use a closure instead of bind because we reassign #cancelConnection.
    
    498 507
         // Also, we now assign an exit handler that cancels the first connection,
    
    499 508
         // so that a sudden exit before the first connection is completed might
    
    ... ... @@ -507,7 +516,17 @@ export class TorProvider {
    507 516
         };
    
    508 517
     
    
    509 518
         logger.debug("Trying to start the tor process.");
    
    510
    -    await this.#torProcess.start();
    
    519
    +    const res = await this.#torProcess.start();
    
    520
    +    if (TorLauncherUtil.isAndroid) {
    
    521
    +      this.#controlPortSettings = {
    
    522
    +        ipcFile: new lazy.FileUtils.File(res.controlPortPath),
    
    523
    +        cookieFilePath: res.cookieFilePath,
    
    524
    +      };
    
    525
    +      this.#socksSettings = {
    
    526
    +        transproxy: false,
    
    527
    +        ipcFile: new lazy.FileUtils.File(res.socksPath),
    
    528
    +      };
    
    529
    +    }
    
    511 530
         logger.info("Started a tor process");
    
    512 531
       }
    
    513 532
     
    
    ... ... @@ -521,6 +540,11 @@ export class TorProvider {
    521 540
         logger.debug("Reading the control port configuration");
    
    522 541
         const settings = {};
    
    523 542
     
    
    543
    +    if (TorLauncherUtil.isAndroid) {
    
    544
    +      // We will populate the settings after having started the daemon.
    
    545
    +      return;
    
    546
    +    }
    
    547
    +
    
    524 548
         const isWindows = Services.appinfo.OS === "WINNT";
    
    525 549
         // Determine how Tor Launcher will connect to the Tor control port.
    
    526 550
         // Environment variables get top priority followed by preferences.
    

  • toolkit/components/tor-launcher/moz.build
    ... ... @@ -5,6 +5,7 @@ EXTRA_JS_MODULES += [
    5 5
         "TorLauncherUtil.sys.mjs",
    
    6 6
         "TorParsers.sys.mjs",
    
    7 7
         "TorProcess.sys.mjs",
    
    8
    +    "TorProcessAndroid.sys.mjs",
    
    8 9
         "TorProvider.sys.mjs",
    
    9 10
         "TorProviderBuilder.sys.mjs",
    
    10 11
         "TorStartupService.sys.mjs",
    

  • toolkit/modules/Moat.sys.mjs
    ... ... @@ -10,6 +10,7 @@ import {
    10 10
     const lazy = {};
    
    11 11
     
    
    12 12
     ChromeUtils.defineESModuleGetters(lazy, {
    
    13
    +  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    13 14
       Subprocess: "resource://gre/modules/Subprocess.sys.mjs",
    
    14 15
       TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
    
    15 16
       TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    ... ... @@ -290,6 +291,48 @@ class MeekTransport {
    290 291
       }
    
    291 292
     }
    
    292 293
     
    
    294
    +class MeekTransportAndroid {
    
    295
    +  // These members are used by consumers to setup the proxy to do requests over
    
    296
    +  // meek. They are passed to newProxyInfoWithAuth.
    
    297
    +  proxyType = null;
    
    298
    +  proxyAddress = null;
    
    299
    +  proxyPort = 0;
    
    300
    +  proxyUsername = null;
    
    301
    +  proxyPassword = null;
    
    302
    +
    
    303
    +  #id = 0;
    
    304
    +
    
    305
    +  async init() {
    
    306
    +    // ensure we haven't already init'd
    
    307
    +    if (this.#id) {
    
    308
    +      throw new Error("MeekTransport: Already initialized");
    
    309
    +    }
    
    310
    +    const details = await lazy.EventDispatcher.instance.sendRequestForResult({
    
    311
    +      type: "GeckoView:Tor:StartMeek",
    
    312
    +    });
    
    313
    +    this.#id = details.id;
    
    314
    +    this.proxyType = "socks";
    
    315
    +    this.proxyAddress = details.address;
    
    316
    +    this.proxyPort = details.port;
    
    317
    +    [this.proxyUsername, this.proxyPassword] = makeMeekCredentials(
    
    318
    +      this.proxyType
    
    319
    +    );
    
    320
    +  }
    
    321
    +
    
    322
    +  async uninit() {
    
    323
    +    lazy.EventDispatcher.instance.sendRequest({
    
    324
    +      type: "GeckoView:Tor:StopMeek",
    
    325
    +      id: this.#id,
    
    326
    +    });
    
    327
    +    this.#id = 0;
    
    328
    +    this.proxyType = null;
    
    329
    +    this.proxyAddress = null;
    
    330
    +    this.proxyPort = 0;
    
    331
    +    this.proxyUsername = null;
    
    332
    +    this.proxyPassword = null;
    
    333
    +  }
    
    334
    +}
    
    335
    +
    
    293 336
     //
    
    294 337
     // Callback object with a cached promise for the returned Moat data
    
    295 338
     //
    
    ... ... @@ -407,7 +450,10 @@ export class MoatRPC {
    407 450
           throw new Error("MoatRPC: Already initialized");
    
    408 451
         }
    
    409 452
     
    
    410
    -    const meekTransport = new MeekTransport();
    
    453
    +    const meekTransport =
    
    454
    +      Services.appinfo.OS === "Android"
    
    455
    +        ? new MeekTransportAndroid()
    
    456
    +        : new MeekTransport();
    
    411 457
         await meekTransport.init();
    
    412 458
         this.#meekTransport = meekTransport;
    
    413 459
         this.#inited = true;
    

  • toolkit/modules/TorAndroidIntegration.sys.mjs
    1
    +/* This Source Code Form is subject to the terms of the Mozilla Public
    
    2
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    
    3
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    4
    +
    
    5
    +import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
    
    6
    +
    
    7
    +const lazy = {};
    
    8
    +ChromeUtils.defineESModuleGetters(lazy, {
    
    9
    +  EventDispatcher: "resource://gre/modules/Messaging.sys.mjs",
    
    10
    +  TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
    
    11
    +  TorProviderBuilder: "resource://gre/modules/TorProviderBuilder.sys.mjs",
    
    12
    +  TorSettings: "resource://gre/modules/TorSettings.sys.mjs",
    
    13
    +});
    
    14
    +
    
    15
    +const Prefs = Object.freeze({
    
    16
    +  useNewBootstrap: "browser.tor_android.use_new_bootstrap",
    
    17
    +  logLevel: "browser.tor_android.log_level",
    
    18
    +});
    
    19
    +
    
    20
    +const logger = new ConsoleAPI({
    
    21
    +  maxLogLevel: "info",
    
    22
    +  maxLogLevelPref: Prefs.logLevel,
    
    23
    +  prefix: "TorAndroidIntegration",
    
    24
    +});
    
    25
    +
    
    26
    +const ListenedEvents = Object.freeze({
    
    27
    +  settingsGet: "GeckoView:Tor:SettingsGet",
    
    28
    +  settingsSet: "GeckoView:Tor:SettingsSet",
    
    29
    +  settingsApply: "GeckoView:Tor:SettingsApply",
    
    30
    +  settingsSave: "GeckoView:Tor:SettingsSave",
    
    31
    +});
    
    32
    +
    
    33
    +class TorAndroidIntegrationImpl {
    
    34
    +  #initialized = false;
    
    35
    +
    
    36
    +  init() {
    
    37
    +    lazy.EventDispatcher.instance.registerListener(
    
    38
    +      this,
    
    39
    +      Object.values(ListenedEvents)
    
    40
    +    );
    
    41
    +
    
    42
    +    this.#bootstrapMethodReset();
    
    43
    +    Services.prefs.addObserver(Prefs.useNewBootstrap, this);
    
    44
    +  }
    
    45
    +
    
    46
    +  async #initNewBootstrap() {
    
    47
    +    if (this.#initialized) {
    
    48
    +      return;
    
    49
    +    }
    
    50
    +    this.#initialized = true;
    
    51
    +
    
    52
    +    lazy.TorProviderBuilder.init().finally(() => {
    
    53
    +      lazy.TorProviderBuilder.firstWindowLoaded();
    
    54
    +    });
    
    55
    +    try {
    
    56
    +      await lazy.TorSettings.init();
    
    57
    +      await lazy.TorConnect.init();
    
    58
    +    } catch (e) {
    
    59
    +      logger.error("Cannot initialize TorSettings or TorConnect", e);
    
    60
    +    }
    
    61
    +  }
    
    62
    +
    
    63
    +  observe(subj, topic, data) {
    
    64
    +    switch (topic) {
    
    65
    +      case "nsPref:changed":
    
    66
    +        if (data === Prefs.useNewBootstrap) {
    
    67
    +          this.#bootstrapMethodReset();
    
    68
    +        }
    
    69
    +        break;
    
    70
    +    }
    
    71
    +  }
    
    72
    +
    
    73
    +  async onEvent(event, data, callback) {
    
    74
    +    logger.debug(`Received event ${event}`, data);
    
    75
    +    try {
    
    76
    +      switch (event) {
    
    77
    +        case settingsGet:
    
    78
    +          callback?.onSuccess(lazy.TorSettings.getSettings());
    
    79
    +          return;
    
    80
    +        case settingsSet:
    
    81
    +          // This does not throw, so we do not have any way to report the error!
    
    82
    +          lazy.TorSettings.setSettings(data);
    
    83
    +          break;
    
    84
    +        case settingsApply:
    
    85
    +          await lazy.TorSettings.applySettings();
    
    86
    +          break;
    
    87
    +        case settingsSave:
    
    88
    +          await lazy.TorSettings.saveSettings();
    
    89
    +          break;
    
    90
    +      }
    
    91
    +      callback?.onSuccess();
    
    92
    +    } catch (e) {
    
    93
    +      logger.error();
    
    94
    +      callback?.sendError(e);
    
    95
    +    }
    
    96
    +  }
    
    97
    +
    
    98
    +  #bootstrapMethodReset() {
    
    99
    +    if (Services.prefs.getBoolPref(Prefs.useNewBootstrap, false)) {
    
    100
    +      this.#initNewBootstrap();
    
    101
    +    } else {
    
    102
    +      Services.prefs.clearUserPref("network.proxy.socks");
    
    103
    +      Services.prefs.clearUserPref("network.proxy.socks_port");
    
    104
    +    }
    
    105
    +  }
    
    106
    +}
    
    107
    +
    
    108
    +export const TorAndroidIntegration = new TorAndroidIntegrationImpl();

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -793,6 +793,9 @@ export const TorConnect = (() => {
    793 793
     
    
    794 794
                   TorConnect._errorMessage = errorMessage;
    
    795 795
                   TorConnect._errorDetails = errorDetails;
    
    796
    +              console.error(
    
    797
    +                `[TorConnect] Entering error state (${errorMessage}, ${errorDetails})`
    
    798
    +              );
    
    796 799
     
    
    797 800
                   Services.obs.notifyObservers(
    
    798 801
                     { message: errorMessage, details: errorDetails },
    

  • toolkit/modules/moz.build
    ... ... @@ -215,6 +215,7 @@ EXTRA_JS_MODULES += [
    215 215
         "Sqlite.sys.mjs",
    
    216 216
         "SubDialog.sys.mjs",
    
    217 217
         "Timer.sys.mjs",
    
    218
    +    "TorAndroidIntegration.sys.mjs",
    
    218 219
         "TorConnect.sys.mjs",
    
    219 220
         "TorSettings.sys.mjs",
    
    220 221
         "TorStrings.sys.mjs",