morgan pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser

Commits:

5 changed files:

Changes:

  • toolkit/components/torconnect/TorConnectParent.sys.mjs
    ... ... @@ -13,6 +13,9 @@ ChromeUtils.defineESModuleGetters(lazy, {
    13 13
       HomePage: "resource:///modules/HomePage.sys.mjs",
    
    14 14
     });
    
    15 15
     
    
    16
    +const userHasEverClickedConnectPref =
    
    17
    +  "torbrowser.about_torconnect.user_has_ever_clicked_connect";
    
    18
    +
    
    16 19
     /*
    
    17 20
     This object is basically a marshalling interface between the TorConnect module
    
    18 21
     and a particular about:torconnect page
    
    ... ... @@ -117,6 +120,9 @@ export class TorConnectParent extends JSWindowActorParent {
    117 120
             TorConnect.chooseRegion();
    
    118 121
             break;
    
    119 122
           case "torconnect:begin-bootstrapping":
    
    123
    +        if (message.data.userClickedConnect) {
    
    124
    +          Services.prefs.setBoolPref(userHasEverClickedConnectPref, true);
    
    125
    +        }
    
    120 126
             TorConnect.beginBootstrapping(message.data.regionCode);
    
    121 127
             break;
    
    122 128
           case "torconnect:cancel-bootstrapping":
    
    ... ... @@ -130,6 +136,10 @@ export class TorConnectParent extends JSWindowActorParent {
    130 136
               Direction: Services.locale.isAppLocaleRTL ? "rtl" : "ltr",
    
    131 137
               CountryNames: TorConnect.countryNames,
    
    132 138
               stage: TorConnect.stage,
    
    139
    +          userHasEverClickedConnect: Services.prefs.getBoolPref(
    
    140
    +            userHasEverClickedConnectPref,
    
    141
    +            false
    
    142
    +          ),
    
    133 143
               quickstartEnabled: TorConnect.quickstart,
    
    134 144
             };
    
    135 145
           case "torconnect:get-frequent-regions":
    

  • toolkit/components/torconnect/content/aboutTorConnect.css
    ... ... @@ -15,6 +15,11 @@ html {
    15 15
       height: 100%;
    
    16 16
     }
    
    17 17
     
    
    18
    +body:not(.loaded) {
    
    19
    +  /* Keep blank whilst loading. */
    
    20
    +  display: none;
    
    21
    +}
    
    22
    +
    
    18 23
     #breadcrumbs {
    
    19 24
       display: flex;
    
    20 25
       align-items: center;
    
    ... ... @@ -93,6 +98,11 @@ html {
    93 98
       display: none;
    
    94 99
     }
    
    95 100
     
    
    101
    +#tor-connect-heading {
    
    102
    +  /* Do not show the focus outline. */
    
    103
    +  outline: none;
    
    104
    +}
    
    105
    +
    
    96 106
     #connect-to-tor {
    
    97 107
       margin-inline-start: 0;
    
    98 108
     }
    

  • toolkit/components/torconnect/content/aboutTorConnect.html
    ... ... @@ -50,7 +50,7 @@
    50 50
           </div>
    
    51 51
           <div id="text-container">
    
    52 52
             <div class="title">
    
    53
    -          <h1 class="title-text"></h1>
    
    53
    +          <h1 id="tor-connect-heading" class="title-text" tabindex="-1"></h1>
    
    54 54
             </div>
    
    55 55
             <div id="connectLongContent">
    
    56 56
               <p id="connectLongContentText"></p>
    

  • toolkit/components/torconnect/content/aboutTorConnect.js
    ... ... @@ -32,7 +32,6 @@ class AboutTorConnect {
    32 32
       selectors = Object.freeze({
    
    33 33
         textContainer: {
    
    34 34
           title: "div.title",
    
    35
    -      titleText: "h1.title-text",
    
    36 35
           longContentText: "#connectLongContentText",
    
    37 36
         },
    
    38 37
         progress: {
    
    ... ... @@ -77,7 +76,7 @@ class AboutTorConnect {
    77 76
     
    
    78 77
       elements = Object.freeze({
    
    79 78
         title: document.querySelector(this.selectors.textContainer.title),
    
    80
    -    titleText: document.querySelector(this.selectors.textContainer.titleText),
    
    79
    +    heading: document.getElementById("tor-connect-heading"),
    
    81 80
         longContentText: document.querySelector(
    
    82 81
           this.selectors.textContainer.longContentText
    
    83 82
         ),
    
    ... ... @@ -138,18 +137,44 @@ class AboutTorConnect {
    138 137
     
    
    139 138
       locations = {};
    
    140 139
     
    
    141
    -  beginBootstrapping() {
    
    142
    -    RPMSendAsyncMessage("torconnect:begin-bootstrapping", {});
    
    140
    +  /**
    
    141
    +   * Whether the user requested a cancellation of the bootstrap from *this*
    
    142
    +   * page.
    
    143
    +   *
    
    144
    +   * @type {boolean}
    
    145
    +   */
    
    146
    +  userCancelled = false;
    
    147
    +
    
    148
    +  /**
    
    149
    +   * Start a normal bootstrap attempt.
    
    150
    +   *
    
    151
    +   * @param {boolean} userClickedConnect - Whether this request was triggered by
    
    152
    +   *   the user clicking the "Connect" button on the "Start" page.
    
    153
    +   */
    
    154
    +  beginBootstrapping(userClickedConnect) {
    
    155
    +    RPMSendAsyncMessage("torconnect:begin-bootstrapping", {
    
    156
    +      userClickedConnect,
    
    157
    +    });
    
    143 158
       }
    
    144 159
     
    
    160
    +  /**
    
    161
    +   * Start an auto bootstrap attempt.
    
    162
    +   *
    
    163
    +   * @param {string} regionCode - The region code to use for the bootstrap, or
    
    164
    +   *   "automatic".
    
    165
    +   */
    
    145 166
       beginAutoBootstrapping(regionCode) {
    
    146 167
         RPMSendAsyncMessage("torconnect:begin-bootstrapping", {
    
    147 168
           regionCode,
    
    148 169
         });
    
    149 170
       }
    
    150 171
     
    
    172
    +  /**
    
    173
    +   * Try and cancel the current bootstrap attempt.
    
    174
    +   */
    
    151 175
       cancelBootstrapping() {
    
    152 176
         RPMSendAsyncMessage("torconnect:cancel-bootstrapping");
    
    177
    +    this.userCancelled = true;
    
    153 178
       }
    
    154 179
     
    
    155 180
       /*
    
    ... ... @@ -260,7 +285,7 @@ class AboutTorConnect {
    260 285
       }
    
    261 286
     
    
    262 287
       setTitle(title, className) {
    
    263
    -    this.elements.titleText.textContent = title;
    
    288
    +    this.elements.heading.textContent = title;
    
    264 289
         this.elements.title.className = "title";
    
    265 290
         if (className) {
    
    266 291
           this.elements.title.classList.add(className);
    
    ... ... @@ -349,18 +374,88 @@ class AboutTorConnect {
    349 374
         }
    
    350 375
       }
    
    351 376
     
    
    377
    +  /**
    
    378
    +   * The connect button that was focused just prior to a bootstrap attempt, if
    
    379
    +   * any.
    
    380
    +   *
    
    381
    +   * @type {?Element}
    
    382
    +   */
    
    383
    +  preBootstrappingFocus = null;
    
    384
    +
    
    385
    +  /**
    
    386
    +   * The stage that was shown on this page just prior to a bootstrap attempt.
    
    387
    +   *
    
    388
    +   * @type {?string}
    
    389
    +   */
    
    390
    +  preBootstrappingStage = null;
    
    391
    +
    
    352 392
       /*
    
    353 393
       These methods update the UI based on the current TorConnect state
    
    354 394
       */
    
    355 395
     
    
    356
    -  updateStage(stage) {
    
    396
    +  /**
    
    397
    +   * Update the shown stage.
    
    398
    +   *
    
    399
    +   * @param {ConnectStage} stage - The new stage to show.
    
    400
    +   * @param {boolean} [focusConnect=false] - Whether to try and focus the
    
    401
    +   *   connect button, if we are in the Start stage.
    
    402
    +   */
    
    403
    +  updateStage(stage, focusConnect = false) {
    
    357 404
         if (stage.name === this.shownStage) {
    
    358 405
           return;
    
    359 406
         }
    
    360 407
     
    
    408
    +    const prevStage = this.shownStage;
    
    361 409
         this.shownStage = stage.name;
    
    362 410
         this.selectedLocation = stage.defaultRegion;
    
    363 411
     
    
    412
    +    // By default we want to reset the focus to the top of the page when
    
    413
    +    // changing the displayed page since we want a user to read the new page
    
    414
    +    // before activating a control.
    
    415
    +    let moveFocus = this.elements.heading;
    
    416
    +
    
    417
    +    if (stage.name === "Bootstrapping") {
    
    418
    +      this.preBootstrappingStage = prevStage;
    
    419
    +      this.preBootstrappingFocus = null;
    
    420
    +      if (focusConnect && stage.isQuickstart) {
    
    421
    +        // If this is the initial automatic bootstrap triggered by the
    
    422
    +        // quickstart preference, treat as if the previous shown stage was
    
    423
    +        // "Start" and the user clicked the "Connect" button.
    
    424
    +        // Then, if the user cancels, the focus should still move to the
    
    425
    +        // "Connect" button.
    
    426
    +        this.preBootstrappingStage = "Start";
    
    427
    +        this.preBootstrappingFocus = this.elements.connectButton;
    
    428
    +      } else if (this.elements.connectButton.contains(document.activeElement)) {
    
    429
    +        this.preBootstrappingFocus = this.elements.connectButton;
    
    430
    +      } else if (
    
    431
    +        this.elements.tryBridgeButton.contains(document.activeElement)
    
    432
    +      ) {
    
    433
    +        this.preBootstrappingFocus = this.elements.tryBridgeButton;
    
    434
    +      }
    
    435
    +    } else {
    
    436
    +      if (
    
    437
    +        this.userCancelled &&
    
    438
    +        prevStage === "Bootstrapping" &&
    
    439
    +        stage.name === this.preBootstrappingStage &&
    
    440
    +        this.preBootstrappingFocus &&
    
    441
    +        this.elements.cancelButton.contains(document.activeElement)
    
    442
    +      ) {
    
    443
    +        // If returning back to the same stage after the user tried to cancel
    
    444
    +        // bootstrapping from within this page, then we restore the focus to the
    
    445
    +        // connect button to allow the user to quickly re-try.
    
    446
    +        // If the bootstrap was cancelled for any other reason, we reset the
    
    447
    +        // focus as usual.
    
    448
    +        moveFocus = this.preBootstrappingFocus;
    
    449
    +      }
    
    450
    +      // Clear the Bootstrapping variables.
    
    451
    +      this.preBootstrappingStage = null;
    
    452
    +      this.preBootstrappingFocus = null;
    
    453
    +    }
    
    454
    +
    
    455
    +    // Clear the recording of the cancellation request.
    
    456
    +    this.userCancelled = false;
    
    457
    +
    
    458
    +    let isLoaded = true;
    
    364 459
         let showProgress = false;
    
    365 460
         let showLog = false;
    
    366 461
         switch (stage.name) {
    
    ... ... @@ -368,14 +463,21 @@ class AboutTorConnect {
    368 463
             console.error("Should not be open when TorConnect is disabled");
    
    369 464
             break;
    
    370 465
           case "Loading":
    
    466
    +        // Unexpected for this page to open so early.
    
    467
    +        console.warn("Page opened whilst loading");
    
    468
    +        isLoaded = false;
    
    469
    +        break;
    
    371 470
           case "Start":
    
    372
    -        // Loading is not currnetly handled, treat the same as "Start", but UI
    
    373
    -        // will be unresponsive.
    
    374 471
             this.showStart(stage.tryAgain, stage.potentiallyBlocked);
    
    472
    +        if (focusConnect) {
    
    473
    +          moveFocus = this.elements.connectButton;
    
    474
    +        }
    
    375 475
             break;
    
    376 476
           case "Bootstrapping":
    
    377 477
             showProgress = true;
    
    378 478
             this.showBootstrapping(stage.bootstrapTrigger, stage.tryAgain);
    
    479
    +        // Always focus the cancel button.
    
    480
    +        moveFocus = this.elements.cancelButton;
    
    379 481
             break;
    
    380 482
           case "Offline":
    
    381 483
             showLog = true;
    
    ... ... @@ -419,6 +521,9 @@ class AboutTorConnect {
    419 521
         } else {
    
    420 522
           this.hide(this.elements.viewLogButton);
    
    421 523
         }
    
    524
    +
    
    525
    +    document.body.classList.toggle("loaded", isLoaded);
    
    526
    +    moveFocus.focus();
    
    422 527
       }
    
    423 528
     
    
    424 529
       updateBootstrappingStatus(data) {
    
    ... ... @@ -452,10 +557,9 @@ class AboutTorConnect {
    452 557
         this.show(this.elements.quickstartContainer);
    
    453 558
         this.show(this.elements.configureButton);
    
    454 559
         this.show(this.elements.connectButton, true);
    
    455
    -    this.elements.connectButton.focus();
    
    456
    -    if (tryAgain) {
    
    457
    -      this.elements.connectButton.textContent = TorStrings.torConnect.tryAgain;
    
    458
    -    }
    
    560
    +    this.elements.connectButton.textContent = tryAgain
    
    561
    +      ? TorStrings.torConnect.tryAgain
    
    562
    +      : TorStrings.torConnect.torConnectButton;
    
    459 563
         if (potentiallyBlocked) {
    
    460 564
           this.setBreadcrumbsStatus(
    
    461 565
             BreadcrumbStatus.Active,
    
    ... ... @@ -511,7 +615,6 @@ class AboutTorConnect {
    511 615
         }
    
    512 616
         this.hideButtons();
    
    513 617
         this.show(this.elements.cancelButton);
    
    514
    -    this.elements.cancelButton.focus();
    
    515 618
       }
    
    516 619
     
    
    517 620
       showOffline() {
    
    ... ... @@ -541,7 +644,6 @@ class AboutTorConnect {
    541 644
           BreadcrumbStatus.Disabled
    
    542 645
         );
    
    543 646
         this.showLocationForm(true, TorStrings.torConnect.tryBridge);
    
    544
    -    this.elements.tryBridgeButton.focus();
    
    545 647
       }
    
    546 648
     
    
    547 649
       showRegionNotFound() {
    
    ... ... @@ -557,7 +659,6 @@ class AboutTorConnect {
    557 659
           BreadcrumbStatus.Disabled
    
    558 660
         );
    
    559 661
         this.showLocationForm(false, TorStrings.torConnect.tryBridge);
    
    560
    -    this.elements.tryBridgeButton.focus();
    
    561 662
       }
    
    562 663
     
    
    563 664
       showConfirmRegion(error) {
    
    ... ... @@ -573,7 +674,6 @@ class AboutTorConnect {
    573 674
           BreadcrumbStatus.Active
    
    574 675
         );
    
    575 676
         this.showLocationForm(false, TorStrings.torConnect.tryAgain);
    
    576
    -    this.elements.tryBridgeButton.focus();
    
    577 677
       }
    
    578 678
     
    
    579 679
       showFinalError(error) {
    
    ... ... @@ -722,7 +822,8 @@ class AboutTorConnect {
    722 822
         this.elements.connectButton.textContent =
    
    723 823
           TorStrings.torConnect.torConnectButton;
    
    724 824
         this.elements.connectButton.addEventListener("click", () => {
    
    725
    -      this.beginBootstrapping();
    
    825
    +      // Record as userClickedConnect if we are in the Start stage.
    
    826
    +      this.beginBootstrapping(this.shownStage === "Start");
    
    726 827
         });
    
    727 828
     
    
    728 829
         this.populateLocations();
    
    ... ... @@ -802,7 +903,13 @@ class AboutTorConnect {
    802 903
         this.initObservers();
    
    803 904
         this.initKeyboardShortcuts();
    
    804 905
     
    
    805
    -    this.updateStage(args.stage);
    
    906
    +    // If we have previously opened about:torconnect and the user tried the
    
    907
    +    // "Connect" button we want to focus the "Connect" button for easy
    
    908
    +    // activation.
    
    909
    +    // Otherwise, we do not want to focus it for first time users so they can
    
    910
    +    // read the full page first.
    
    911
    +    const focusConnect = args.userHasEverClickedConnect;
    
    912
    +    this.updateStage(args.stage, focusConnect);
    
    806 913
         this.updateQuickstart(args.quickstartEnabled);
    
    807 914
       }
    
    808 915
     }
    

  • toolkit/modules/TorConnect.sys.mjs
    ... ... @@ -680,10 +680,14 @@ export const TorConnectStage = Object.freeze({
    680 680
      * A summary of the user stage.
    
    681 681
      * (This class is mirrored for Android in TorConnectStage.java. Changes should be mirrored there)
    
    682 682
      *
    
    683
    - * @property {string} name - The name of the stage.
    
    683
    + * @property {string} name - The name of the stage. One of the values in
    
    684
    + *   `TorConnectStage`.
    
    684 685
      * @property {string} defaultRegion - The default region to show in the UI.
    
    685
    - * @property {?string} bootstrapTrigger - The TorConnectStage prior to this
    
    686
    + * @property {?string} bootstrapTrigger - The name of the stage prior to this
    
    686 687
      *   bootstrap attempt. Only set during the "Bootstrapping" stage.
    
    688
    + * @property {boolean} isQuickstart - Whether the current bootstrap attempt was
    
    689
    + *   triggered by the `TorConnect.quickstart` preference. Will be `false`
    
    690
    + *   outside of the "Bootstrapping" stage.
    
    687 691
      * @property {?BootstrapError} error - The last bootstrapping error.
    
    688 692
      * @property {boolean} tryAgain - Whether a bootstrap attempt has failed, so
    
    689 693
      *   that a normal bootstrap should be shown as "Try Again" instead of
    
    ... ... @@ -752,6 +756,14 @@ export const TorConnect = {
    752 756
        */
    
    753 757
       _bootstrapTrigger: null,
    
    754 758
     
    
    759
    +  /**
    
    760
    +   * Whether the current bootstrapping attempt was triggered by the quickstart
    
    761
    +   * preference.
    
    762
    +   *
    
    763
    +   * @type {boolean}
    
    764
    +   */
    
    765
    +  _isQuickstart: false,
    
    766
    +
    
    755 767
       /**
    
    756 768
        * The alternative stage that we should move to after bootstrapping completes.
    
    757 769
        *
    
    ... ... @@ -807,6 +819,7 @@ export const TorConnect = {
    807 819
           name: this._stageName,
    
    808 820
           defaultRegion: this._defaultRegion,
    
    809 821
           bootstrapTrigger: this._bootstrapTrigger,
    
    822
    +      isQuickstart: this._isQuickstart,
    
    810 823
           error: this._errorDetails
    
    811 824
             ? {
    
    812 825
                 code: this._errorDetails.code,
    
    ... ... @@ -935,7 +948,7 @@ export const TorConnect = {
    935 948
           // And the previous bootstrap attempt must have succeeded.
    
    936 949
           !Services.prefs.getBoolPref(TorConnectPrefs.prompt_at_startup, true)
    
    937 950
         ) {
    
    938
    -      this.beginBootstrapping();
    
    951
    +      this._beginBootstrappingInternal(undefined, true);
    
    939 952
         }
    
    940 953
       },
    
    941 954
     
    
    ... ... @@ -1303,6 +1316,19 @@ export const TorConnect = {
    1303 1316
        *   an auto-bootstrap attempt.
    
    1304 1317
        */
    
    1305 1318
       async beginBootstrapping(regionCode) {
    
    1319
    +    await this._beginBootstrappingInternal(regionCode, false);
    
    1320
    +  },
    
    1321
    +
    
    1322
    +  /**
    
    1323
    +   * Begin a bootstrap attempt.
    
    1324
    +   *
    
    1325
    +   * @param {string} [regionCode] - An optional region code string to use, or
    
    1326
    +   *   "automatic" to automatically determine the region. If given, will start
    
    1327
    +   *   an auto-bootstrap attempt.
    
    1328
    +   * @param {boolean} isQuickstart - Whether this was triggered by the
    
    1329
    +   *   quickstart option.
    
    1330
    +   */
    
    1331
    +  async _beginBootstrappingInternal(regionCode, isQuickstart) {
    
    1306 1332
         lazy.logger.debug("TorConnect.beginBootstrapping()");
    
    1307 1333
     
    
    1308 1334
         if (!this._confirmBootstrapping(regionCode)) {
    
    ... ... @@ -1331,6 +1357,7 @@ export const TorConnect = {
    1331 1357
         }
    
    1332 1358
         this._requestedStage = null;
    
    1333 1359
         this._bootstrapTrigger = beginStage;
    
    1360
    +    this._isQuickstart = isQuickstart;
    
    1334 1361
         this._setStage(TorConnectStage.Bootstrapping);
    
    1335 1362
         this._bootstrapAttempt = bootstrapAttempt;
    
    1336 1363
     
    
    ... ... @@ -1349,6 +1376,7 @@ export const TorConnect = {
    1349 1376
         const requestedStage = this._requestedStage;
    
    1350 1377
         this._requestedStage = null;
    
    1351 1378
         this._bootstrapTrigger = null;
    
    1379
    +    this._isQuickstart = false;
    
    1352 1380
         this._bootstrapAttempt = null;
    
    1353 1381
     
    
    1354 1382
         if (bootstrapAttempt.detectedRegion) {