| 
Commits:
9d6ca8ac
 by Pier Angelo Vendrame   at 2023-12-05T11:00:25+01:00 
 fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.
Collect the browsers also on Android.
9e239230
 by Pier Angelo Vendrame   at 2023-12-05T11:08:42+01:00 
 fixup! Bug 40597: Implement TorSettings module
Moved the creation of Meek credentials to a function on its own that we
will reuse also with the Android version of MeekTransport.
6a649d7f
 by Pier Angelo Vendrame   at 2023-12-05T11:25:06+01:00 
 fixup! Bug 40933: Add tor-launcher functionality
Store the SOCKS settings as a member, so that we can customize them if
needed before applying them.
Also, cleanup the public interface of TorProcess.
E.g., removed TorProcess.connectionWorked, since we do not use this
information anymore.
Finally, added a TorLauncherUtil.isAndroid.
1ad1d839
 by Pier Angelo Vendrame   at 2023-12-05T13:48:35+01:00 
 fixup! Bug 27476: Implement about:torconnect captive portal within Tor Browser
HTML-ize about:torconnect.
Changed file extension, updated the HTML tag, removed short-tags on tags
that are expected to have a closing tag.
Also, removed the preprocessor and moved the onion pattern to this
commit, since it is used only here.
fbd5b4bb
 by Pier Angelo Vendrame   at 2023-12-05T13:48:42+01:00 
 fixup! Bug 2176: Rebrand Firefox to TorBrowser
Move the onion pattern files to the commit of about:torconnect.
3649cc3f
 by Pier Angelo Vendrame   at 2023-12-05T18:48:25+01:00 
 Temporary changes to about:torconnect for Android.
We are planning of tempoorarily using about:torconnect on Android, until
the native UX is ready.
 
14 changed files:
Changes:
docshell/base/nsAboutRedirector.cpp
 
| ... | ... | @@ -169,7 +169,7 @@ static const RedirEntry kRedirMap[] = { |  
| 169 | 169 |  #endif
 |  
| 170 | 170 |      {"telemetry", "chrome://global/content/aboutTelemetry.xhtml",
 |  
| 171 | 171 |       nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::IS_SECURE_CHROME_UI},
 |  
| 172 |  | -    {"torconnect", "chrome://global/content/torconnect/aboutTorConnect.xhtml",
 |  
|  | 172 | +    {"torconnect", "chrome://global/content/torconnect/aboutTorConnect.html",
 |  
| 173 | 173 |       nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
 |  
| 174 | 174 |           nsIAboutModule::URI_CAN_LOAD_IN_CHILD | nsIAboutModule::ALLOW_SCRIPT |
 |  
| 175 | 175 |           nsIAboutModule::HIDE_FROM_ABOUTABOUT |
 |  toolkit/components/tor-launcher/TorDomainIsolator.sys.mjs
 
 
| ... | ... | @@ -475,9 +475,19 @@ class TorDomainIsolatorImpl { |  
| 475 | 475 |     * @returns {MozBrowser?} The browser the channel is associated to
 |  
| 476 | 476 |     */
 |  
| 477 | 477 |    #getBrowserForChannel(channel) {
 |  
|  | 478 | +    const currentBrowser =
 |  
|  | 479 | +      channel.loadInfo.browsingContext?.topChromeWindow?.browser;
 |  
|  | 480 | +    if (
 |  
|  | 481 | +      channel.loadInfo.browsingContext &&
 |  
|  | 482 | +      currentBrowser?.browsingContext === channel.loadInfo.browsingContext
 |  
|  | 483 | +    ) {
 |  
|  | 484 | +      // Android has only one browser, and does not have the browsers property.
 |  
|  | 485 | +      return currentBrowser;
 |  
|  | 486 | +    }
 |  
| 478 | 487 |      const browsers =
 |  
| 479 | 488 |        channel.loadInfo.browsingContext?.topChromeWindow?.gBrowser?.browsers;
 |  
| 480 | 489 |      if (!browsers || !channel.loadInfo.browsingContext?.browserId) {
 |  
|  | 490 | +      logger.debug("Missing data to associate to a browser", channel.loadInfo);
 |  
| 481 | 491 |        return null;
 |  
| 482 | 492 |      }
 |  
| 483 | 493 |      for (const browser of browsers) {
 |  toolkit/components/tor-launcher/TorLauncherUtil.sys.mjs
 
 
| ... | ... | @@ -325,6 +325,10 @@ class TorFile { |  
| 325 | 325 |  }
 |  
| 326 | 326 |  
 |  
| 327 | 327 |  export const TorLauncherUtil = Object.freeze({
 |  
|  | 328 | +  get isAndroid() {
 |  
|  | 329 | +    return Services.appinfo.OS === "Android";
 |  
|  | 330 | +  },
 |  
|  | 331 | +
 |  
| 328 | 332 |    get isMac() {
 |  
| 329 | 333 |      return Services.appinfo.OS === "Darwin";
 |  
| 330 | 334 |    },
 |  toolkit/components/tor-launcher/TorProcess.sys.mjs
 
 
| ... | ... | @@ -33,8 +33,6 @@ export class TorProcess { |  
| 33 | 33 |    #args = [];
 |  
| 34 | 34 |    #subprocess = null;
 |  
| 35 | 35 |    #status = TorProcessStatus.Unknown;
 |  
| 36 |  | -  // Have we ever made a connection on the control port?
 |  
| 37 |  | -  #didConnectToTorControlPort = false;
 |  
| 38 | 36 |  
 |  
| 39 | 37 |    onExit = exitCode => {};
 |  
| 40 | 38 |  
 |  
| ... | ... | @@ -69,10 +67,6 @@ export class TorProcess { |  
| 69 | 67 |      }
 |  
| 70 | 68 |    }
 |  
| 71 | 69 |  
 |  
| 72 |  | -  get status() {
 |  
| 73 |  | -    return this.#status;
 |  
| 74 |  | -  }
 |  
| 75 |  | -
 |  
| 76 | 70 |    get isRunning() {
 |  
| 77 | 71 |      return (
 |  
| 78 | 72 |        this.#status === TorProcessStatus.Starting ||
 |  
| ... | ... | @@ -102,7 +96,6 @@ export class TorProcess { |  
| 102 | 96 |        }
 |  
| 103 | 97 |  
 |  
| 104 | 98 |        this.#status = TorProcessStatus.Starting;
 |  
| 105 |  | -      this.#didConnectToTorControlPort = false;
 |  
| 106 | 99 |  
 |  
| 107 | 100 |        // useful for simulating slow tor daemon launch
 |  
| 108 | 101 |        const kPrefTorDaemonLaunchDelay = "extensions.torlauncher.launch_delay";
 |  
| ... | ... | @@ -155,13 +148,6 @@ export class TorProcess { |  
| 155 | 148 |      this.#status = TorProcessStatus.Exited;
 |  
| 156 | 149 |    }
 |  
| 157 | 150 |  
 |  
| 158 |  | -  // The owner of the process can use this function to tell us that they
 |  
| 159 |  | -  // successfully connected to the control port. This information will be used
 |  
| 160 |  | -  // only to decide which text to show in the confirmation dialog if tor exits.
 |  
| 161 |  | -  connectionWorked() {
 |  
| 162 |  | -    this.#didConnectToTorControlPort = true;
 |  
| 163 |  | -  }
 |  
| 164 |  | -
 |  
| 165 | 151 |    async #dumpStdout() {
 |  
| 166 | 152 |      let string;
 |  
| 167 | 153 |      while (
 |  
| ... | ... | @@ -201,20 +187,6 @@ export class TorProcess { |  
| 201 | 187 |    #processExitedUnexpectedly(exitCode) {
 |  
| 202 | 188 |      this.#subprocess = null;
 |  
| 203 | 189 |      this.#status = TorProcessStatus.Exited;
 |  
| 204 |  | -    // FIXME: We can probably drop #didConnectToTorControlPort and use only one
 |  
| 205 |  | -    // callback. Then we can let the provider actually distinguish between the
 |  
| 206 |  | -    // cases.
 |  
| 207 |  | -    if (!this.#didConnectToTorControlPort) {
 |  
| 208 |  | -      logger.warn("Tor exited before we could connect to its control port.");
 |  
| 209 |  | -      // tor might be misconfigured, because we could never connect to it.
 |  
| 210 |  | -      // Two instances of Tor Browser trying to use the same port numbers is
 |  
| 211 |  | -      // also a typical scenario for this.
 |  
| 212 |  | -      // This might happen very early, before the browser UI is actually
 |  
| 213 |  | -      // available. So, we will tell the process owner that the process exited,
 |  
| 214 |  | -      // without trying to restart it.
 |  
| 215 |  | -      this.onExit(exitCode);
 |  
| 216 |  | -      return;
 |  
| 217 |  | -    }
 |  
| 218 | 190 |      logger.warn("Tor exited suddenly.");
 |  
| 219 | 191 |      this.onExit(exitCode);
 |  
| 220 | 192 |    }
 |  toolkit/components/tor-launcher/TorProvider.sys.mjs
 
 
| ... | ... | @@ -35,6 +35,15 @@ const logger = new ConsoleAPI({ |  
| 35 | 35 |   * @property {string=} host The host to connect for a TCP control port
 |  
| 36 | 36 |   * @property {number=} port The port number to use for a TCP control port
 |  
| 37 | 37 |   */
 |  
|  | 38 | +/**
 |  
|  | 39 | + * @typedef {object} SocksSettings An object that includes the proxy settings to
 |  
|  | 40 | + * be configured in the browser.
 |  
|  | 41 | + * @property {boolean=} transproxy If true, no proxy is configured
 |  
|  | 42 | + * @property {nsIFile=} ipcFile The nsIFile object with the path to a Unix
 |  
|  | 43 | + * socket to use for an IPC proxy
 |  
|  | 44 | + * @property {string=} host The host to connect for a TCP proxy
 |  
|  | 45 | + * @property {number=} port The port number to use for a TCP proxy
 |  
|  | 46 | + */
 |  
| 38 | 47 |  /**
 |  
| 39 | 48 |   * @typedef {object} LogEntry An object with a log message
 |  
| 40 | 49 |   * @property {Date} date The date at which we received the message
 |  
| ... | ... | @@ -111,6 +120,13 @@ export class TorProvider { |  
| 111 | 120 |     */
 |  
| 112 | 121 |    #torProcess = null;
 |  
| 113 | 122 |  
 |  
|  | 123 | +  /**
 |  
|  | 124 | +   * The settings for the SOCKS proxy.
 |  
|  | 125 | +   *
 |  
|  | 126 | +   * @type {SocksSettings?}
 |  
|  | 127 | +   */
 |  
|  | 128 | +  #socksSettings = null;
 |  
|  | 129 | +
 |  
| 114 | 130 |    /**
 |  
| 115 | 131 |     * The logs we received over the control port.
 |  
| 116 | 132 |     * We store a finite number of log entries which can be configured with
 |  
| ... | ... | @@ -165,8 +181,9 @@ export class TorProvider { |  
| 165 | 181 |    async init() {
 |  
| 166 | 182 |      logger.debug("Initializing the Tor provider.");
 |  
| 167 | 183 |  
 |  
| 168 |  | -    const socksSettings = TorLauncherUtil.getPreferredSocksConfiguration();
 |  
| 169 |  | -    logger.debug("Requested SOCKS configuration", socksSettings);
 |  
|  | 184 | +    // These settings might be customized in the following steps.
 |  
|  | 185 | +    this.#socksSettings = TorLauncherUtil.getPreferredSocksConfiguration();
 |  
|  | 186 | +    logger.debug("Requested SOCKS configuration", this.#socksSettings);
 |  
| 170 | 187 |  
 |  
| 171 | 188 |      try {
 |  
| 172 | 189 |        await this.#setControlPortConfiguration();
 |  
| ... | ... | @@ -175,11 +192,11 @@ export class TorProvider { |  
| 175 | 192 |        throw e;
 |  
| 176 | 193 |      }
 |  
| 177 | 194 |  
 |  
| 178 |  | -    if (socksSettings.transproxy) {
 |  
|  | 195 | +    if (this.#socksSettings.transproxy) {
 |  
| 179 | 196 |        logger.info("Transparent proxy required, not starting a Tor daemon.");
 |  
| 180 | 197 |      } else if (this.ownsTorDaemon) {
 |  
| 181 | 198 |        try {
 |  
| 182 |  | -        await this.#startDaemon(socksSettings);
 |  
|  | 199 | +        await this.#startDaemon();
 |  
| 183 | 200 |        } catch (e) {
 |  
| 184 | 201 |          logger.error("Failed to start the tor daemon", e);
 |  
| 185 | 202 |          throw e;
 |  
| ... | ... | @@ -197,8 +214,7 @@ export class TorProvider { |  
| 197 | 214 |        throw e;
 |  
| 198 | 215 |      }
 |  
| 199 | 216 |  
 |  
| 200 |  | -    // We do not customize SOCKS settings, at least for now.
 |  
| 201 |  | -    TorLauncherUtil.setProxyConfiguration(socksSettings);
 |  
|  | 217 | +    TorLauncherUtil.setProxyConfiguration(this.#socksSettings);
 |  
| 202 | 218 |  
 |  
| 203 | 219 |      logger.info("The Tor provider is ready.");
 |  
| 204 | 220 |  
 |  
| ... | ... | @@ -464,7 +480,7 @@ export class TorProvider { |  
| 464 | 480 |  
 |  
| 465 | 481 |    // Process management
 |  
| 466 | 482 |  
 |  
| 467 |  | -  async #startDaemon(socksSettings) {
 |  
|  | 483 | +  async #startDaemon() {
 |  
| 468 | 484 |      // TorProcess should be instanced once, then always reused and restarted
 |  
| 469 | 485 |      // only through the prompt it exposes when the controlled process dies.
 |  
| 470 | 486 |      if (this.#torProcess) {
 |  
| ... | ... | @@ -476,7 +492,7 @@ export class TorProvider { |  
| 476 | 492 |  
 |  
| 477 | 493 |      this.#torProcess = new lazy.TorProcess(
 |  
| 478 | 494 |        this.#controlPortSettings,
 |  
| 479 |  | -      socksSettings
 |  
|  | 495 | +      this.#socksSettings
 |  
| 480 | 496 |      );
 |  
| 481 | 497 |      // Use a closure instead of bind because we reassign #cancelConnection.
 |  
| 482 | 498 |      // Also, we now assign an exit handler that cancels the first connection,
 |  
| ... | ... | @@ -619,7 +635,6 @@ export class TorProvider { |  
| 619 | 635 |          }
 |  
| 620 | 636 |          this.#openControlPort()
 |  
| 621 | 637 |            .then(controller => {
 |  
| 622 |  | -            this.#torProcess?.connectionWorked();
 |  
| 623 | 638 |              this.#cancelConnection = () => {};
 |  
| 624 | 639 |              // The cancel function should have already called reject.
 |  
| 625 | 640 |              if (!canceled) {
 |  toolkit/components/torconnect/content/aboutTorConnect.css
 
 
| ... | ... | @@ -10,6 +10,11 @@ |  
| 10 | 10 |    --onion-radius: 75px;
 |  
| 11 | 11 |  }
 |  
| 12 | 12 |  
 |  
|  | 13 | +html {
 |  
|  | 14 | +  width: 100%;
 |  
|  | 15 | +  height: 100%;
 |  
|  | 16 | +}
 |  
|  | 17 | +
 |  
| 13 | 18 |  input[type="checkbox"]:focus, select:focus {
 |  
| 14 | 19 |    outline:  none!important;
 |  
| 15 | 20 |    box-shadow: 0 0 0 3px var(--purple-30) !important;
 |  
| ... | ... | @@ -330,3 +335,166 @@ body { |  
| 330 | 335 |    background-image: url("chrome://global/content/torconnect/connection-location.svg");
 |  
| 331 | 336 |    stroke: var(--warning-color);
 |  
| 332 | 337 |  }
 |  
|  | 338 | +
 |  
|  | 339 | +.onion-pattern-container {
 |  
|  | 340 | +  flex: auto;           /* grow to consume remaining space on the page */
 |  
|  | 341 | +  display: flex;
 |  
|  | 342 | +  margin: 0 auto;
 |  
|  | 343 | +  width: 100%;
 |  
|  | 344 | +  /* two onions tall, 4x the radius */
 |  
|  | 345 | +  height: calc(4 * var(--onion-radius));
 |  
|  | 346 | +  max-height: calc(4 * var(--onion-radius));
 |  
|  | 347 | +  min-height: calc(4 * var(--onion-radius));
 |  
|  | 348 | +  direction: ltr;
 |  
|  | 349 | +}
 |  
|  | 350 | +
 |  
|  | 351 | +.onion-pattern-crop {
 |  
|  | 352 | +  height: 100%;
 |  
|  | 353 | +  width: 100%;
 |  
|  | 354 | +
 |  
|  | 355 | +  -moz-context-properties: fill;
 |  
|  | 356 | +  fill: var(--onion-color, currentColor);
 |  
|  | 357 | +  /* opacity of the entire div, not context-opacity */
 |  
|  | 358 | +  opacity: var(--onion-opacity, 1);
 |  
|  | 359 | +
 |  
|  | 360 | +  background-image: url("chrome://global/content/torconnect/onion-pattern.svg");
 |  
|  | 361 | +  background-repeat: repeat;
 |  
|  | 362 | +  background-attachment: local;
 |  
|  | 363 | +  background-position: center;
 |  
|  | 364 | +  /* svg source is 6 onions wide and 2 onions tall */
 |  
|  | 365 | +  background-size: calc(6 * 2 * var(--onion-radius)) calc(2 * 2 * var(--onion-radius));;
 |  
|  | 366 | +}
 |  
|  | 367 | +
 |  
|  | 368 | +:root {
 |  
|  | 369 | +  --android-dark-accents-buttons: #9059FF;
 |  
|  | 370 | +  --android-dark-background-secondary: #E1E0E7;
 |  
|  | 371 | +  --android-dark-text-primary: #FBFBFE;
 |  
|  | 372 | +  --android-light-text-primary: #15141A;
 |  
|  | 373 | +}
 |  
|  | 374 | +
 |  
|  | 375 | +[hidden=true] {
 |  
|  | 376 | +  display: none !important;
 |  
|  | 377 | +}
 |  
|  | 378 | +
 |  
|  | 379 | +body.android {
 |  
|  | 380 | +  --onion-color: var(--android-dark-text-primary);
 |  
|  | 381 | +  width: 100%;
 |  
|  | 382 | +  height: 100%;
 |  
|  | 383 | +  box-sizing: border-box;
 |  
|  | 384 | +  margin: 0;
 |  
|  | 385 | +  padding: 0 24px !important;
 |  
|  | 386 | +  color: var(--onion-color);
 |  
|  | 387 | +  background: linear-gradient(194deg, #692E9D -0.93%, #393270 48.91%);
 |  
|  | 388 | +  font: menu;
 |  
|  | 389 | +  font-size: 14px;
 |  
|  | 390 | +  display: flex;
 |  
|  | 391 | +}
 |  
|  | 392 | +
 |  
|  | 393 | +.android #connectPageContainer {
 |  
|  | 394 | +  max-width: none;
 |  
|  | 395 | +  display: flex;
 |  
|  | 396 | +  flex-direction: column;
 |  
|  | 397 | +  flex: 1;
 |  
|  | 398 | +}
 |  
|  | 399 | +
 |  
|  | 400 | +.android #breadcrumbs {
 |  
|  | 401 | +  display: none;
 |  
|  | 402 | +}
 |  
|  | 403 | +
 |  
|  | 404 | +.android #text-container {
 |  
|  | 405 | +  display: flex;
 |  
|  | 406 | +  flex-direction: column;
 |  
|  | 407 | +  flex: 1;
 |  
|  | 408 | +}
 |  
|  | 409 | +
 |  
|  | 410 | +.android .title {
 |  
|  | 411 | +  background-position: left 0;
 |  
|  | 412 | +  background-repeat: no-repeat;
 |  
|  | 413 | +  background-size: 40px;
 |  
|  | 414 | +  padding-top: 64px;
 |  
|  | 415 | +  font-size: 22px;
 |  
|  | 416 | +  line-height: 28px;
 |  
|  | 417 | +}
 |  
|  | 418 | +
 |  
|  | 419 | +.android h1 {
 |  
|  | 420 | +  font-weight: normal;
 |  
|  | 421 | +  font-size: 100%;
 |  
|  | 422 | +  margin: 0 0 16px 0;
 |  
|  | 423 | +}
 |  
|  | 424 | +
 |  
|  | 425 | +.android p {
 |  
|  | 426 | +  margin: 0;
 |  
|  | 427 | +  padding-bottom: 8px;
 |  
|  | 428 | +  line-height: 20px;
 |  
|  | 429 | +}
 |  
|  | 430 | +
 |  
|  | 431 | +.android #quickstartContainer {
 |  
|  | 432 | +  margin-top: 24px;
 |  
|  | 433 | +}
 |  
|  | 434 | +
 |  
|  | 435 | +.android .button-container {
 |  
|  | 436 | +  display: flex;
 |  
|  | 437 | +  flex: 1;
 |  
|  | 438 | +  flex-direction: column;
 |  
|  | 439 | +}
 |  
|  | 440 | +
 |  
|  | 441 | +.android #locationDropdown {
 |  
|  | 442 | +  width: 100%;
 |  
|  | 443 | +  max-width: none;
 |  
|  | 444 | +  margin: 0;
 |  
|  | 445 | +}
 |  
|  | 446 | +
 |  
|  | 447 | +.android select {
 |  
|  | 448 | +  background: transparent;
 |  
|  | 449 | +  border: none;
 |  
|  | 450 | +  border-bottom: 1px solid var(--android-dark-text-primary);
 |  
|  | 451 | +  color: var(--android-dark-text-primary);
 |  
|  | 452 | +  display: block;
 |  
|  | 453 | +  width: 100%;
 |  
|  | 454 | +  margin-top: 10px;
 |  
|  | 455 | +  padding: 8px;
 |  
|  | 456 | +}
 |  
|  | 457 | +
 |  
|  | 458 | +.android #buttonPadding {
 |  
|  | 459 | +  flex: 1;
 |  
|  | 460 | +}
 |  
|  | 461 | +
 |  
|  | 462 | +.android #connectButtonContainer {
 |  
|  | 463 | +  width: 100%;
 |  
|  | 464 | +  padding-bottom: 18px;
 |  
|  | 465 | +  display: grid;
 |  
|  | 466 | +}
 |  
|  | 467 | +
 |  
|  | 468 | +/* Be sure not to match the togglee */
 |  
|  | 469 | +.android #connectButtonContainer button {
 |  
|  | 470 | +  display: block;
 |  
|  | 471 | +  width: 100%;
 |  
|  | 472 | +  margin: 4px 0;
 |  
|  | 473 | +  padding: 11px 30px;
 |  
|  | 474 | +  font-size: 14px;
 |  
|  | 475 | +  font-weight: 500;
 |  
|  | 476 | +  border: none;
 |  
|  | 477 | +  border-radius: 4px;
 |  
|  | 478 | +}
 |  
|  | 479 | +
 |  
|  | 480 | +.android #connectButton, .android #tryBridgeButton, .android #configureButton.primary {
 |  
|  | 481 | +  color: var(--android-dark-text-primary);
 |  
|  | 482 | +  background-color: var(--android-dark-accents-buttons);
 |  
|  | 483 | +}
 |  
|  | 484 | +
 |  
|  | 485 | +.android #configureButton {
 |  
|  | 486 | +  order: 1;
 |  
|  | 487 | +}
 |  
|  | 488 | +
 |  
|  | 489 | +.android #restartButton {
 |  
|  | 490 | +  order: 2;
 |  
|  | 491 | +}
 |  
|  | 492 | +
 |  
|  | 493 | +.android #restartButton, .android #cancelButton, .android #configureButton {
 |  
|  | 494 | +  color: var(--android-light-text-primary);
 |  
|  | 495 | +  background-color: var(--android-dark-background-secondary);
 |  
|  | 496 | +}
 |  
|  | 497 | +
 |  
|  | 498 | +.android .onion-pattern-container {
 |  
|  | 499 | +  display: none;
 |  
|  | 500 | +} |  toolkit/components/torconnect/content/aboutTorConnect.xhtml
→
toolkit/components/torconnect/content/aboutTorConnect.html
 
 
| 1 | 1 |  <!-- Copyright (c) 2021, The Tor Project, Inc. -->
 |  
| 2 | 2 |  <!DOCTYPE html>
 |  
| 3 |  | -<html xmlns="http://www.w3.org/1999/xhtml">
 |  
|  | 3 | +<html>
 |  
| 4 | 4 |    <head>
 |  
| 5 | 5 |      <meta
 |  
| 6 | 6 |        http-equiv="Content-Security-Policy"
 |  
| 7 | 7 |        content="default-src chrome:; object-src 'none'"
 |  
| 8 | 8 |      />
 |  
| 9 |  | -    <link
 |  
| 10 |  | -      rel="stylesheet"
 |  
| 11 |  | -      href="">"chrome://global/skin/onionPattern.css"
 |  
| 12 |  | -      type="text/css"
 |  
| 13 |  | -      media="all"
 |  
| 14 |  | -    />
 |  
|  | 9 | +    <meta name="viewport" content="width=device-width">
 |  
| 15 | 10 |      <link
 |  
| 16 | 11 |        rel="stylesheet"
 |  
| 17 | 12 |        href="">"chrome://global/content/torconnect/aboutTorConnect.css"
 |  
| ... | ... | @@ -21,64 +16,69 @@ |  
| 21 | 16 |    </head>
 |  
| 22 | 17 |    <body>
 |  
| 23 | 18 |      <div id="progressBar">
 |  
| 24 |  | -      <div id="progressBackground" />
 |  
| 25 |  | -      <div id="progressSolid" />
 |  
|  | 19 | +      <div id="progressBackground"></div>
 |  
|  | 20 | +      <div id="progressSolid"></div>
 |  
| 26 | 21 |      </div>
 |  
| 27 | 22 |      <div id="connectPageContainer" class="container">
 |  
| 28 | 23 |        <div id="breadcrumbs" class="hidden">
 |  
| 29 | 24 |          <span id="connect-to-tor" class="breadcrumb-item">
 |  
| 30 |  | -          <span id="connect-to-tor-icon" class="breadcrumb-icon" />
 |  
| 31 |  | -          <span class="breadcrumb-label" />
 |  
|  | 25 | +          <span id="connect-to-tor-icon" class="breadcrumb-icon"></span>
 |  
|  | 26 | +          <span class="breadcrumb-label"></span>
 |  
| 32 | 27 |          </span>
 |  
| 33 | 28 |          <span
 |  
| 34 | 29 |            id="connection-assist-separator"
 |  
| 35 | 30 |            class="breadcrumb-separator breadcrumb-icon"
 |  
| 36 |  | -        />
 |  
|  | 31 | +        ></span>
 |  
| 37 | 32 |          <span id="connection-assist" class="breadcrumb-item">
 |  
| 38 |  | -          <span id="connection-assist-icon" class="breadcrumb-icon" />
 |  
| 39 |  | -          <span class="breadcrumb-label" />
 |  
|  | 33 | +          <span id="connection-assist-icon" class="breadcrumb-icon"></span>
 |  
|  | 34 | +          <span class="breadcrumb-label"></span>
 |  
| 40 | 35 |          </span>
 |  
| 41 | 36 |          <span
 |  
| 42 | 37 |            id="try-bridge-separator"
 |  
| 43 | 38 |            class="breadcrumb-separator breadcrumb-icon"
 |  
| 44 |  | -        />
 |  
|  | 39 | +        ></span>
 |  
| 45 | 40 |          <span id="try-bridge" class="breadcrumb-item">
 |  
| 46 |  | -          <span id="try-bridge-icon" class="breadcrumb-icon" />
 |  
| 47 |  | -          <span class="breadcrumb-label" />
 |  
|  | 41 | +          <span id="try-bridge-icon" class="breadcrumb-icon"></span>
 |  
|  | 42 | +          <span class="breadcrumb-label"></span>
 |  
| 48 | 43 |          </span>
 |  
| 49 | 44 |        </div>
 |  
| 50 | 45 |        <div id="text-container">
 |  
| 51 | 46 |          <div class="title">
 |  
| 52 |  | -          <h1 class="title-text" />
 |  
|  | 47 | +          <h1 class="title-text"></h1>
 |  
| 53 | 48 |          </div>
 |  
| 54 | 49 |          <div id="connectLongContent">
 |  
| 55 |  | -          <p id="connectLongContentText" />
 |  
|  | 50 | +          <p id="connectLongContentText"></p>
 |  
| 56 | 51 |          </div>
 |  
| 57 | 52 |          <div id="connectShortDesc">
 |  
| 58 |  | -          <p id="connectShortDescText" />
 |  
|  | 53 | +          <p id="connectShortDescText"></p>
 |  
| 59 | 54 |          </div>
 |  
| 60 | 55 |  
 |  
| 61 | 56 |          <button id="viewLogButton"></button>
 |  
| 62 | 57 |  
 |  
| 63 | 58 |          <div id="quickstartContainer">
 |  
| 64 | 59 |            <input id="quickstartCheckbox" type="checkbox" />
 |  
| 65 |  | -          <label id="quickstartCheckboxLabel" for="">"quickstartCheckbox" />
 |  
|  | 60 | +          <label id="quickstartCheckboxLabel" for="">"quickstartCheckbox"></label>
 |  
| 66 | 61 |          </div>
 |  
| 67 | 62 |  
 |  
| 68 |  | -        <div id="connectButtonContainer" class="button-container">
 |  
| 69 |  | -          <button id="restartButton" hidden="true"></button>
 |  
| 70 |  | -          <button id="configureButton" hidden="true"></button>
 |  
| 71 |  | -          <button id="cancelButton" hidden="true"></button>
 |  
| 72 |  | -          <button id="connectButton" class="primary" hidden="true"></button>
 |  
| 73 |  | -          <label id="locationDropdownLabel" for="">"countries" />
 |  
|  | 63 | +        <div class="button-container">
 |  
|  | 64 | +          <label id="locationDropdownLabel" for="">"countries"></label>
 |  
| 74 | 65 |            <form id="locationDropdown" hidden="true">
 |  
| 75 | 66 |              <select id="countries"></select>
 |  
| 76 | 67 |            </form>
 |  
| 77 |  | -          <button id="tryBridgeButton" class="primary" hidden="true"></button>
 |  
|  | 68 | +          <span id="buttonPadding"></span>
 |  
|  | 69 | +          <span id="connectButtonContainer">
 |  
|  | 70 | +            <button id="restartButton" hidden="true"></button>
 |  
|  | 71 | +            <button id="configureButton" hidden="true"></button>
 |  
|  | 72 | +            <button id="cancelButton" hidden="true"></button>
 |  
|  | 73 | +            <button id="connectButton" class="primary" hidden="true"></button>
 |  
|  | 74 | +            <button id="tryBridgeButton" class="primary" hidden="true"></button>
 |  
|  | 75 | +          </span>
 |  
| 78 | 76 |          </div>
 |  
| 79 | 77 |        </div>
 |  
| 80 | 78 |      </div>
 |  
| 81 |  | -#include ../../../themes/shared/onionPattern.inc.xhtml
 |  
|  | 79 | +    <div class="onion-pattern-container">
 |  
|  | 80 | +      <div class="onion-pattern-crop"></div>
 |  
|  | 81 | +    </div>
 |  
|  | 82 | +    <script src="">"chrome://global/content/torconnect/aboutTorConnect.js"></script>
 |  
| 82 | 83 |    </body>
 |  
| 83 |  | -  <script src="">"chrome://global/content/torconnect/aboutTorConnect.js" />
 |  
| 84 | 84 |  </html> |  toolkit/components/torconnect/content/aboutTorConnect.js
 
 
| ... | ... | @@ -70,8 +70,8 @@ class AboutTorConnect { |  
| 70 | 70 |        connect: "button#connectButton",
 |  
| 71 | 71 |        tryBridge: "button#tryBridgeButton",
 |  
| 72 | 72 |        locationDropdownLabel: "#locationDropdownLabel",
 |  
| 73 |  | -      locationDropdown: "form#locationDropdown",
 |  
| 74 |  | -      locationDropdownSelect: "form#locationDropdown select",
 |  
|  | 73 | +      locationDropdown: "#locationDropdown",
 |  
|  | 74 | +      locationDropdownSelect: "#locationDropdown select",
 |  
| 75 | 75 |      },
 |  
| 76 | 76 |    });
 |  
| 77 | 77 |  
 |  
| ... | ... | @@ -666,6 +666,9 @@ class AboutTorConnect { |  
| 666 | 666 |    }
 |  
| 667 | 667 |  
 |  
| 668 | 668 |    initElements(direction) {
 |  
|  | 669 | +    const isAndroid = navigator.userAgent.indexOf("Android") !== -1;
 |  
|  | 670 | +    document.body.classList.toggle("android", isAndroid);
 |  
|  | 671 | +
 |  
| 669 | 672 |      document.documentElement.setAttribute("dir", direction);
 |  
| 670 | 673 |  
 |  
| 671 | 674 |      this.elements.connectToTorLink.addEventListener("click", event => {
 |  toolkit/themes/shared/onionPattern.svg
→
toolkit/components/torconnect/content/onion-pattern.svg
 
 toolkit/components/torconnect/jar.mn
 
 
| ... | ... | @@ -3,12 +3,13 @@ toolkit.jar: |  
| 3 | 3 |      content/global/torconnect/torConnectTitlebarStatus.js                    (content/torConnectTitlebarStatus.js)
 |  
| 4 | 4 |      content/global/torconnect/torConnectTitlebarStatus.css                   (content/torConnectTitlebarStatus.css)
 |  
| 5 | 5 |      content/global/torconnect/aboutTorConnect.css                            (content/aboutTorConnect.css)
 |  
| 6 |  | -*   content/global/torconnect/aboutTorConnect.xhtml                          (content/aboutTorConnect.xhtml)
 |  
|  | 6 | +    content/global/torconnect/aboutTorConnect.html                           (content/aboutTorConnect.html)
 |  
| 7 | 7 |      content/global/torconnect/aboutTorConnect.js                             (content/aboutTorConnect.js)
 |  
| 8 | 8 |      content/global/torconnect/arrow-right.svg                                (content/arrow-right.svg)
 |  
| 9 | 9 |      content/global/torconnect/bridge.svg                                     (content/bridge.svg)
 |  
| 10 | 10 |      content/global/torconnect/connection-failure.svg                         (content/connection-failure.svg)
 |  
| 11 | 11 |      content/global/torconnect/connection-location.svg                        (content/connection-location.svg)
 |  
|  | 12 | +    content/global/torconnect/onion-pattern.svg                              (content/onion-pattern.svg)
 |  
| 12 | 13 |      content/global/torconnect/tor-connect.svg                                (content/tor-connect.svg)
 |  
| 13 | 14 |      content/global/torconnect/tor-not-connected-to-connected-animated.svg    (content/tor-not-connected-to-connected-animated.svg)
 |  
| 14 | 15 |      content/global/torconnect/tor-connect-broken.svg                         (content/tor-connect-broken.svg) |  toolkit/modules/Moat.sys.mjs
 
 
| ... | ... | @@ -21,6 +21,56 @@ const TorLauncherPrefs = Object.freeze({ |  
| 21 | 21 |    moat_service: "extensions.torlauncher.moat_service",
 |  
| 22 | 22 |  });
 |  
| 23 | 23 |  
 |  
|  | 24 | +function makeMeekCredentials(proxyType) {
 |  
|  | 25 | +  // Construct the per-connection arguments.
 |  
|  | 26 | +  let meekClientEscapedArgs = "";
 |  
|  | 27 | +  const meekReflector = Services.prefs.getStringPref(
 |  
|  | 28 | +    TorLauncherPrefs.bridgedb_reflector
 |  
|  | 29 | +  );
 |  
|  | 30 | +
 |  
|  | 31 | +  // Escape aValue per section 3.5 of the PT specification:
 |  
|  | 32 | +  //   First the "<Key>=<Value>" formatted arguments MUST be escaped,
 |  
|  | 33 | +  //   such that all backslash, equal sign, and semicolon characters
 |  
|  | 34 | +  //   are escaped with a backslash.
 |  
|  | 35 | +  const escapeArgValue = aValue =>
 |  
|  | 36 | +    aValue
 |  
|  | 37 | +      ? aValue
 |  
|  | 38 | +          .replaceAll("\\", "\\\\")
 |  
|  | 39 | +          .replaceAll("=", "\\=")
 |  
|  | 40 | +          .replaceAll(";", "\\;")
 |  
|  | 41 | +      : "";
 |  
|  | 42 | +
 |  
|  | 43 | +  if (meekReflector) {
 |  
|  | 44 | +    meekClientEscapedArgs += "url="">";
 |  
|  | 45 | +    meekClientEscapedArgs += escapeArgValue(meekReflector);
 |  
|  | 46 | +  }
 |  
|  | 47 | +  const meekFront = Services.prefs.getStringPref(
 |  
|  | 48 | +    TorLauncherPrefs.bridgedb_front
 |  
|  | 49 | +  );
 |  
|  | 50 | +  if (meekFront) {
 |  
|  | 51 | +    if (meekClientEscapedArgs.length) {
 |  
|  | 52 | +      meekClientEscapedArgs += ";";
 |  
|  | 53 | +    }
 |  
|  | 54 | +    meekClientEscapedArgs += "front=";
 |  
|  | 55 | +    meekClientEscapedArgs += escapeArgValue(meekFront);
 |  
|  | 56 | +  }
 |  
|  | 57 | +
 |  
|  | 58 | +  // socks5
 |  
|  | 59 | +  if (proxyType === "socks") {
 |  
|  | 60 | +    if (meekClientEscapedArgs.length <= 255) {
 |  
|  | 61 | +      return [meekClientEscapedArgs, "\x00"];
 |  
|  | 62 | +    } else {
 |  
|  | 63 | +      return [
 |  
|  | 64 | +        meekClientEscapedArgs.substring(0, 255),
 |  
|  | 65 | +        meekClientEscapedArgs.substring(255),
 |  
|  | 66 | +      ];
 |  
|  | 67 | +    }
 |  
|  | 68 | +    // socks4
 |  
|  | 69 | +  } else {
 |  
|  | 70 | +    return [meekClientEscapedArgs, undefined];
 |  
|  | 71 | +  }
 |  
|  | 72 | +}
 |  
|  | 73 | +
 |  
| 24 | 74 |  //
 |  
| 25 | 75 |  // Launches and controls the PT process lifetime
 |  
| 26 | 76 |  //
 |  
| ... | ... | @@ -70,39 +120,6 @@ class MeekTransport { |  
| 70 | 120 |          proxy.pathToBinary = meekPath.path;
 |  
| 71 | 121 |        }
 |  
| 72 | 122 |  
 |  
| 73 |  | -      // Construct the per-connection arguments.
 |  
| 74 |  | -      let meekClientEscapedArgs = "";
 |  
| 75 |  | -      const meekReflector = Services.prefs.getStringPref(
 |  
| 76 |  | -        TorLauncherPrefs.bridgedb_reflector
 |  
| 77 |  | -      );
 |  
| 78 |  | -
 |  
| 79 |  | -      // Escape aValue per section 3.5 of the PT specification:
 |  
| 80 |  | -      //   First the "<Key>=<Value>" formatted arguments MUST be escaped,
 |  
| 81 |  | -      //   such that all backslash, equal sign, and semicolon characters
 |  
| 82 |  | -      //   are escaped with a backslash.
 |  
| 83 |  | -      const escapeArgValue = aValue =>
 |  
| 84 |  | -        aValue
 |  
| 85 |  | -          ? aValue
 |  
| 86 |  | -              .replaceAll("\\", "\\\\")
 |  
| 87 |  | -              .replaceAll("=", "\\=")
 |  
| 88 |  | -              .replaceAll(";", "\\;")
 |  
| 89 |  | -          : "";
 |  
| 90 |  | -
 |  
| 91 |  | -      if (meekReflector) {
 |  
| 92 |  | -        meekClientEscapedArgs += "url="">";
 |  
| 93 |  | -        meekClientEscapedArgs += escapeArgValue(meekReflector);
 |  
| 94 |  | -      }
 |  
| 95 |  | -      const meekFront = Services.prefs.getStringPref(
 |  
| 96 |  | -        TorLauncherPrefs.bridgedb_front
 |  
| 97 |  | -      );
 |  
| 98 |  | -      if (meekFront) {
 |  
| 99 |  | -        if (meekClientEscapedArgs.length) {
 |  
| 100 |  | -          meekClientEscapedArgs += ";";
 |  
| 101 |  | -        }
 |  
| 102 |  | -        meekClientEscapedArgs += "front=";
 |  
| 103 |  | -        meekClientEscapedArgs += escapeArgValue(meekFront);
 |  
| 104 |  | -      }
 |  
| 105 |  | -
 |  
| 106 | 123 |        // Setup env and start meek process
 |  
| 107 | 124 |        const ptStateDir = lazy.TorLauncherUtil.getTorFile("tordatadir", false);
 |  
| 108 | 125 |        ptStateDir.append("pt_state"); // Match what tor uses.
 |  
| ... | ... | @@ -247,22 +264,9 @@ class MeekTransport { |  
| 247 | 264 |          this.#meekClientProcess = null;
 |  
| 248 | 265 |          this.uninit();
 |  
| 249 | 266 |        });
 |  
| 250 |  | -
 |  
| 251 |  | -      // socks5
 |  
| 252 |  | -      if (this.proxyType === "socks") {
 |  
| 253 |  | -        if (meekClientEscapedArgs.length <= 255) {
 |  
| 254 |  | -          this.proxyUsername = meekClientEscapedArgs;
 |  
| 255 |  | -          this.proxyPassword = "\x00";
 |  
| 256 |  | -        } else {
 |  
| 257 |  | -          this.proxyUsername = meekClientEscapedArgs.substring(0, 255);
 |  
| 258 |  | -          this.proxyPassword = meekClientEscapedArgs.substring(255);
 |  
| 259 |  | -        }
 |  
| 260 |  | -        // socks4
 |  
| 261 |  | -      } else {
 |  
| 262 |  | -        this.proxyUsername = meekClientEscapedArgs;
 |  
| 263 |  | -        this.proxyPassword = undefined;
 |  
| 264 |  | -      }
 |  
| 265 |  | -
 |  
|  | 267 | +      [this.proxyUsername, this.proxyPassword] = makeMeekCredentials(
 |  
|  | 268 | +        this.proxyType
 |  
|  | 269 | +      );
 |  
| 266 | 270 |        this.#inited = true;
 |  
| 267 | 271 |      } catch (ex) {
 |  
| 268 | 272 |        if (this.#meekClientProcess) {
 |  
| ... | ... | @@ -403,7 +407,7 @@ export class MoatRPC { |  
| 403 | 407 |        throw new Error("MoatRPC: Already initialized");
 |  
| 404 | 408 |      }
 |  
| 405 | 409 |  
 |  
| 406 |  | -    let meekTransport = new MeekTransport();
 |  
|  | 410 | +    const meekTransport = new MeekTransport();
 |  
| 407 | 411 |      await meekTransport.init();
 |  
| 408 | 412 |      this.#meekTransport = meekTransport;
 |  
| 409 | 413 |      this.#inited = true;
 |  toolkit/themes/shared/minimal-toolkit.jar.inc.mn
 
 
| ... | ... | @@ -47,7 +47,3 @@ toolkit.jar: |  
| 47 | 47 |    skin/classic/global/media/textrecognition.css            (../../shared/media/textrecognition.css)
 |  
| 48 | 48 |  
 |  
| 49 | 49 |    skin/classic/global/browser-colors.css                   (../../shared/browser-colors.css) |  
| 50 |  | -
 |  
| 51 |  | -# Tor customization
 |  
| 52 |  | -  skin/classic/global/onionPattern.css                     (../../shared/onionPattern.css)
 |  
| 53 |  | -  skin/classic/global/onionPattern.svg                     (../../shared/onionPattern.svg) |  toolkit/themes/shared/onionPattern.css
deleted
 
 
| 1 |  | -/* Onion pattern */
 |  
| 2 |  | -
 |  
| 3 |  | -.onion-pattern-container {
 |  
| 4 |  | -
 |  
| 5 |  | -  flex: auto;           /* grow to consume remaining space on the page */
 |  
| 6 |  | -  display: flex;
 |  
| 7 |  | -  margin: 0 auto;
 |  
| 8 |  | -  width: 100%;
 |  
| 9 |  | -  /* two onions tall, 4x the radius */
 |  
| 10 |  | -  height: calc(4 * var(--onion-radius));
 |  
| 11 |  | -  max-height: calc(4 * var(--onion-radius));
 |  
| 12 |  | -  min-height: calc(4 * var(--onion-radius));
 |  
| 13 |  | -  direction: ltr;
 |  
| 14 |  | -}
 |  
| 15 |  | -
 |  
| 16 |  | -.onion-pattern-crop {
 |  
| 17 |  | -  height: 100%;
 |  
| 18 |  | -  width: 100%;
 |  
| 19 |  | -
 |  
| 20 |  | -  -moz-context-properties: fill;
 |  
| 21 |  | -  fill: var(--onion-color, currentColor);
 |  
| 22 |  | -  /* opacity of the entire div, not context-opacity */
 |  
| 23 |  | -  opacity: var(--onion-opacity, 1);
 |  
| 24 |  | -
 |  
| 25 |  | -  background-image: url("chrome://global/skin/onionPattern.svg");
 |  
| 26 |  | -  background-repeat: repeat;
 |  
| 27 |  | -  background-attachment: local;
 |  
| 28 |  | -  background-position: center;
 |  
| 29 |  | -  /* svg source is 6 onions wide and 2 onions tall */
 |  
| 30 |  | -  background-size: calc(6 * 2 * var(--onion-radius)) calc(2 * 2 * var(--onion-radius));;
 |  
| 31 |  | -} |  toolkit/themes/shared/onionPattern.inc.xhtml
deleted
 
 
| 1 |  | -<!--
 |  
| 2 |  | -  Container div that holds onionPattern.svg
 |  
| 3 |  | -  It is expected the includer of this xhtml file also includes onionPattern.css
 |  
| 4 |  | -  and define the following vars:
 |  
| 5 |  | -    onion-radius : radius of an onion
 |  
| 6 |  | -    onion-color : the base color of the onion pattern
 |  
| 7 |  | -    onion-opacity : the opacity of the entire repeating pattern
 |  
| 8 |  | --->
 |  
| 9 |  | -
 |  
| 10 |  | -<div class="onion-pattern-container">
 |  
| 11 |  | -  <div class="onion-pattern-crop"/>
 |  
| 12 |  | -</div> |  
|  |  | \ No newline at end of file |  
 |