[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-commits] [Git][tpo/applications/tor-browser][tor-browser-153.0esr-16.0-1] 3 commits: fixup! TB 31286: Implementation of bridge, proxy, and firewall settings in...



Title: GitLab

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

Commits:

  • 6aa05c11
    by Henry Wilkes at 2026-08-06T19:42:53+01:00
    fixup! TB 31286: Implementation of bridge, proxy, and firewall settings in about:preferences#connection
    
    TB 45196: Replace old connection settings with setting-group elements.
    
    These can eventually be dropped entirely after we switch to the settings
    redesign, but this allows us to do a lot of tidying in advance.
    
  • 6054bf5a
    by Henry Wilkes at 2026-08-06T19:42:55+01:00
    fixup! Tor Browser strings
    
    TB 45196: Drop old strings.
    
  • 492c55b8
    by Henry Wilkes at 2026-08-06T19:42:55+01:00
    fixup! Add TorStrings module for localization
    
    TB 45196: Drop some old strings.
    

9 changed files:

Changes:

  • browser/components/preferences/preferences.js
    ... ... @@ -12,7 +12,6 @@
    12 12
     /* import-globals-from findInPage.js */
    
    13 13
     /* import-globals-from /browser/base/content/utilityOverlay.js */
    
    14 14
     /* import-globals-from /toolkit/content/preferencesBindings.js */
    
    15
    -/* import-globals-from ../torpreferences/content/connectionPane.js */
    
    16 15
     
    
    17 16
     /** @import MozButton from "chrome://global/content/elements/moz-button.mjs" */
    
    18 17
     /** @import {SettingConfig, SettingEmitChange} from "chrome://global/content/preferences/Setting.mjs" */
    
    ... ... @@ -116,7 +115,7 @@ ChromeUtils.defineESModuleGetters(this, {
    116 115
         "resource:///modules/SelectionChangedMenulist.sys.mjs",
    
    117 116
       ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs",
    
    118 117
       SiteDataManager: "resource:///modules/SiteDataManager.sys.mjs",
    
    119
    -  TorConnect: "resource://gre/modules/TorConnect.sys.mjs",
    
    118
    +  TorConnect: "moz-src:///toolkit/modules/TorConnect.sys.mjs",
    
    120 119
       TransientPrefs: "resource:///modules/TransientPrefs.sys.mjs",
    
    121 120
       UIState: "resource://services-sync/UIState.sys.mjs",
    
    122 121
       UpdateUtils: "resource://gre/modules/UpdateUtils.sys.mjs",
    
    ... ... @@ -574,12 +573,22 @@ function init_all() {
    574 573
         register_module("paneSync", gSyncPane);
    
    575 574
       }
    
    576 575
       register_module("paneSearchResults", gSearchResultsPane);
    
    577
    -  if (gConnectionPane.enabled) {
    
    578
    -    document.getElementById("category-connection").hidden = false;
    
    579
    -    register_module("paneConnection", gConnectionPane);
    
    580
    -  } else {
    
    581
    -    // Remove the pane from the DOM so it doesn't get incorrectly included in search results.
    
    582
    -    document.getElementById("template-paneConnection").remove();
    
    576
    +  if (!redesignEnabled) {
    
    577
    +    if (TorConnect.enabled) {
    
    578
    +      register_module("paneConnection", {
    
    579
    +        init() {
    
    580
    +          ChromeUtils.importESModule(
    
    581
    +            "chrome://browser/content/torpreferences/config/connection.mjs",
    
    582
    +            { global: "current" }
    
    583
    +          );
    
    584
    +          initSettingGroup("connectionStatus");
    
    585
    +          initSettingGroup("torBridges");
    
    586
    +          initSettingGroup("torAdvanced");
    
    587
    +        },
    
    588
    +      });
    
    589
    +    } else {
    
    590
    +      document.getElementById("category-connection").remove();
    
    591
    +    }
    
    583 592
       }
    
    584 593
       for (let [id, config] of Object.entries(CONFIG_PANES)) {
    
    585 594
         // Skip over configs we do not want, including all its children.
    

  • browser/components/torpreferences/config/connection.mjs
    ... ... @@ -35,7 +35,6 @@ const TOR_BRIDGES_EMAIL = "bridges@xxxxxxxxxxxxxx";
    35 35
     
    
    36 36
     SettingGroupManager.registerGroups({
    
    37 37
       connectionStatus: {
    
    38
    -    inProgress: true,
    
    39 38
         l10nId: "tor-connection-internet-status-group",
    
    40 39
         supportPage: "tor-manual:getting-started__about-tor-browser",
    
    41 40
         headingLevel: 2,
    
    ... ... @@ -64,7 +63,6 @@ SettingGroupManager.registerGroups({
    64 63
         ],
    
    65 64
       },
    
    66 65
       torBridges: {
    
    67
    -    inProgress: true,
    
    68 66
         l10nId: "tor-bridges-group",
    
    69 67
         supportPage: "tor-manual:bridges",
    
    70 68
         headingLevel: 2,
    
    ... ... @@ -193,7 +191,6 @@ SettingGroupManager.registerGroups({
    193 191
         ],
    
    194 192
       },
    
    195 193
       torAdvanced: {
    
    196
    -    inProgress: true,
    
    197 194
         l10nId: "tor-advanced-group",
    
    198 195
         headingLevel: 2,
    
    199 196
         items: [
    

  • browser/components/torpreferences/content/connectionPane.inc.xhtml
    ... ... @@ -246,642 +246,30 @@
    246 246
       </html:fieldset>
    
    247 247
     </html:template>
    
    248 248
     
    
    249
    -<!-- Tor panel -->
    
    250
    -
    
    251
    -<script
    
    252
    -  type="application/_javascript_"
    
    253
    -  src="">"chrome://browser/content/torpreferences/connectionPane.js"
    
    249
    +<hbox
    
    250
    +  id="torPreferencesCategory"
    
    251
    +  class="subcategory"
    
    252
    +  data-category="paneConnection"
    
    253
    +  hidden="true"
    
    254
    +  data-srd-groupid="connectionStatus"
    
    255
    +>
    
    256
    +  <html:h1 data-l10n-id="tor-connection-settings-heading"></html:h1>
    
    257
    +</hbox>
    
    258
    +<html:setting-group
    
    259
    +  groupid="connectionStatus"
    
    260
    +  data-category="paneConnection"
    
    261
    +  hidden="hidden"
    
    262
    +  data-srd-migrated=""
    
    263
    +/>
    
    264
    +<html:setting-group
    
    265
    +  groupid="torBridges"
    
    266
    +  data-category="paneConnection"
    
    267
    +  hidden="hidden"
    
    268
    +  data-srd-migrated=""
    
    269
    +/>
    
    270
    +<html:setting-group
    
    271
    +  groupid="torAdvanced"
    
    272
    +  data-category="paneConnection"
    
    273
    +  hidden="hidden"
    
    274
    +  data-srd-migrated=""
    
    254 275
     />
    255
    -<html:template id="template-paneConnection">
    
    256
    -  <vbox
    
    257
    -    id="torPreferencesCategory"
    
    258
    -    class="subcategory"
    
    259
    -    data-category="paneConnection"
    
    260
    -    hidden="true"
    
    261
    -    data-srd-groupid="connectionStatus"
    
    262
    -  >
    
    263
    -    <html:h1 data-l10n-id="tor-connection-settings-heading"></html:h1>
    
    264
    -    <description class="description-deemphasized" flex="1">
    
    265
    -      <html:span data-l10n-id="tor-connection-overview"></html:span>
    
    266
    -      <label
    
    267
    -        class="learnMore text-link"
    
    268
    -        is="text-link"
    
    269
    -        href="about:manual#getting-started__about-tor-browser"
    
    270
    -        useoriginprincipal="true"
    
    271
    -        data-l10n-id="tor-connection-browser-learn-more-link"
    
    272
    -      />
    
    273
    -    </description>
    
    274
    -    <!-- Keep within #torPreferencesCategory so this won't appear in search
    
    275
    -       - results. -->
    
    276
    -    <html:div
    
    277
    -      id="network-status-internet-area"
    
    278
    -      class="network-status-area"
    
    279
    -      role="group"
    
    280
    -      aria-labelledby="network-status-internet-area-label"
    
    281
    -    >
    
    282
    -      <html:img alt="" class="network-status-icon" />
    
    283
    -      <!-- NOTE: We do not wrap the label and status ("Internet: Offline", etc)
    
    284
    -         - in an aria-live area because it may be too noisey and may not be
    
    285
    -         - important to the user. -->
    
    286
    -      <html:span
    
    287
    -        id="network-status-internet-area-label"
    
    288
    -        class="network-status-label"
    
    289
    -        data-l10n-id="tor-connection-internet-status-label"
    
    290
    -      ></html:span>
    
    291
    -      <html:span class="network-status-result"></html:span>
    
    292
    -    </html:div>
    
    293
    -    <html:div
    
    294
    -      id="network-status-tor-area"
    
    295
    -      class="network-status-area"
    
    296
    -      role="group"
    
    297
    -      aria-labelledby="network-status-tor-area-label"
    
    298
    -    >
    
    299
    -      <html:img alt="" class="network-status-icon" />
    
    300
    -      <!-- NOTE: We do not wrap the label and status
    
    301
    -         - ("Tor network: Not connected", etc) in an aria-live area.
    
    302
    -         - This is not likely to change whilst this page has focus.
    
    303
    -         - Moreover, the status is already present in the torconnect status bar
    
    304
    -         - in the window tab bar. -->
    
    305
    -      <html:span
    
    306
    -        id="network-status-tor-area-label"
    
    307
    -        class="network-status-label"
    
    308
    -        data-l10n-id="tor-connection-network-status-label"
    
    309
    -      ></html:span>
    
    310
    -      <html:span class="network-status-result"></html:span>
    
    311
    -      <html:button
    
    312
    -        id="network-status-tor-connect-button"
    
    313
    -        data-l10n-id="tor-connection-network-status-connect-button"
    
    314
    -      ></html:button>
    
    315
    -    </html:div>
    
    316
    -  </vbox>
    
    317
    -
    
    318
    -  <!-- Quickstart -->
    
    319
    -  <groupbox
    
    320
    -    data-category="paneConnection"
    
    321
    -    hidden="true"
    
    322
    -    data-srd-groupid="connectionStatus"
    
    323
    -  >
    
    324
    -    <label>
    
    325
    -      <html:h2 data-l10n-id="tor-connection-automatic-heading"></html:h2>
    
    326
    -    </label>
    
    327
    -    <description
    
    328
    -      class="description-deemphasized"
    
    329
    -      flex="1"
    
    330
    -      data-l10n-id="tor-connection-automatic-description"
    
    331
    -    />
    
    332
    -    <html:moz-toggle
    
    333
    -      id="tor-connection-quickstart-toggle"
    
    334
    -      class="tor-toggle"
    
    335
    -      data-l10n-id="tor-connection-quickstart-checkbox"
    
    336
    -      data-l10n-attrs="label"
    
    337
    -    />
    
    338
    -  </groupbox>
    
    339
    -
    
    340
    -  <!-- Bridges -->
    
    341
    -  <hbox
    
    342
    -    class="subcategory"
    
    343
    -    data-category="paneConnection"
    
    344
    -    hidden="true"
    
    345
    -    data-srd-groupid="connectionStatus"
    
    346
    -  >
    
    347
    -    <html:h1
    
    348
    -      id="tor-bridges-subcategory-heading-non-search"
    
    349
    -      class="tor-bridges-subcategory-heading tor-focusable-heading"
    
    350
    -      tabindex="-1"
    
    351
    -      data-l10n-id="tor-bridges-heading"
    
    352
    -    ></html:h1>
    
    353
    -  </hbox>
    
    354
    -  <groupbox
    
    355
    -    id="torPreferences-bridges-group"
    
    356
    -    data-category="paneConnection"
    
    357
    -    hidden="true"
    
    358
    -    aria-labelledby="tor-bridges-subcategory-heading-non-search"
    
    359
    -    data-srd-groupid="connectionStatus"
    
    360
    -  >
    
    361
    -    <!-- Add a search-header that only appears in search results as a substitute
    
    362
    -       - for the hidden h1 element. See tor-browser#43320.
    
    363
    -       - NOTE: Usually the first xul:label will act as the accessible name for
    
    364
    -       - a xul:groubbox element *if* it is not hidden. Since the search-header
    
    365
    -       - is sometimes hidden we need an explicit aria-labelledby anyway.
    
    366
    -       - However, we keep the wrapper xul:label for styling consistency with the
    
    367
    -       - other settings. -->
    
    368
    -    <label class="search-header" hidden="true">
    
    369
    -      <html:h2
    
    370
    -        class="tor-bridges-subcategory-heading tor-focusable-heading"
    
    371
    -        tabindex="-1"
    
    372
    -        data-l10n-id="tor-bridges-heading"
    
    373
    -      ></html:h2>
    
    374
    -    </label>
    
    375
    -    <description class="description-deemphasized" flex="1">
    
    376
    -      <html:span data-l10n-id="tor-bridges-overview"></html:span>
    
    377
    -      <label
    
    378
    -        class="learnMore text-link"
    
    379
    -        is="text-link"
    
    380
    -        href=""
    
    381
    -        useoriginprincipal="true"
    
    382
    -        data-l10n-id="tor-bridges-learn-more-link"
    
    383
    -      />
    
    384
    -    </description>
    
    385
    -    <hbox
    
    386
    -      align="center"
    
    387
    -      id="torPreferences-bridges-locationGroup"
    
    388
    -      hidden="true"
    
    389
    -    >
    
    390
    -      <label
    
    391
    -        id="torPreferences-bridges-locationLabel"
    
    392
    -        control="torPreferences-bridges-location"
    
    393
    -      />
    
    394
    -      <spacer flex="1" />
    
    395
    -      <menulist id="torPreferences-bridges-location">
    
    396
    -        <menupopup id="torPreferences-bridges-locationEntries" />
    
    397
    -      </menulist>
    
    398
    -      <button
    
    399
    -        id="torPreferences-bridges-buttonChooseBridgeForMe"
    
    400
    -        class="primary tor-button"
    
    401
    -      />
    
    402
    -    </hbox>
    
    403
    -    <html:moz-toggle
    
    404
    -      id="tor-bridges-enabled-toggle"
    
    405
    -      class="tor-toggle"
    
    406
    -      data-l10n-id="tor-bridges-use-bridges"
    
    407
    -      data-l10n-attrs="label"
    
    408
    -    />
    
    409
    -    <!-- Add an aria-live area where we can post notifications to screen
    
    410
    -       - reader users about changes to their list of bridges. This is to give
    
    411
    -       - these users some feedback for when the remove a bridge or change
    
    412
    -       - their bridges in other ways. I.e. whenever tor-bridges-grid-display
    
    413
    -       - changes its rows.
    
    414
    -       -
    
    415
    -       - If we change the text in #tor-bridges-update-area-text, a screen
    
    416
    -       - reader should speak out the text to the user, even when this area
    
    417
    -       - does not have focus.
    
    418
    -       -
    
    419
    -       - In fact, we don't really want the user to navigate to this element
    
    420
    -       - directly. But currently using an aria-live region in the DOM is the
    
    421
    -       - only way to effectively pass a notification to a screen reader user.
    
    422
    -       - Since it must be somewhere in the DOM, we logically place it just
    
    423
    -       - before the grid, where it is hopefully least confusing to stumble
    
    424
    -       - across.
    
    425
    -       -
    
    426
    -       - TODO: Instead of aria-live in the DOM, use the proposed ariaNotify
    
    427
    -       - API if it gets accepted into firefox and works with screen readers.
    
    428
    -       - See https://github.com/WICG/proposals/issues/112
    
    429
    -       -->
    
    430
    -    <!-- NOTE: This area is hidden by default, and is only shown temporarily
    
    431
    -       - when a notification is added. It should never match with search
    
    432
    -       - queries. -->
    
    433
    -    <html:div
    
    434
    -      id="tor-bridges-update-area"
    
    435
    -      hidden="hidden"
    
    436
    -      data-hidden-from-search="true"
    
    437
    -    >
    
    438
    -      <!-- NOTE: This first span's text content will *not* be read out as part
    
    439
    -         - of the notification because it does not have an aria-live
    
    440
    -         - attribute. Instead it is just here to give context to the following
    
    441
    -         - text in #tor-bridges-update-area-text if the user navigates to
    
    442
    -         - #tor-bridges-update-area manually whilst it is not hidden.
    
    443
    -         - I.e. it is just here to make it less confusing if a screen reader
    
    444
    -         - user stumbles across this.
    
    445
    -         -->
    
    446
    -      <html:span data-l10n-id="tor-bridges-update-area-intro"></html:span>
    
    447
    -      <!-- Whitespace between spans. -->
    
    448
    -      <!-- This second span is the area to place notification text in. -->
    
    449
    -      <html:span
    
    450
    -        id="tor-bridges-update-area-text"
    
    451
    -        aria-live="polite"
    
    452
    -      ></html:span>
    
    453
    -    </html:div>
    
    454
    -    <html:div id="tor-bridges-none" hidden="hidden">
    
    455
    -      <html:img id="tor-bridges-none-icon" alt="" />
    
    456
    -      <html:div data-l10n-id="tor-bridges-none-added"></html:div>
    
    457
    -    </html:div>
    
    458
    -    <html:div id="tor-bridges-current" class="tor-bridges-box" hidden="hidden">
    
    459
    -      <html:div id="tor-bridges-current-header-bar">
    
    460
    -        <html:h2
    
    461
    -          id="tor-bridges-current-heading-non-search"
    
    462
    -          class="tor-bridges-current-heading tor-focusable-heading tor-small-heading tor-non-search-heading"
    
    463
    -          tabindex="-1"
    
    464
    -          data-l10n-id="tor-bridges-your-bridges"
    
    465
    -        ></html:h2>
    
    466
    -        <!-- Add a duplicate search heading.
    
    467
    -           - In a search result the heading h1.tor-bridges-subcategory-heading
    
    468
    -           - will be hidden, and the h2.tor-bridges-subcategory-heading
    
    469
    -           - will be visible.
    
    470
    -           - As such, all headings below h2.tor-bridges-subcategory-heading also
    
    471
    -           - need to shift one lower in heading level to preseve the correct
    
    472
    -           - hierarchy of - heading levels.
    
    473
    -           - In this case we hide the <h2> heading and show the duplicate <h3>
    
    474
    -           - heading instead.
    
    475
    -           - See tor-browser#43320. -->
    
    476
    -        <html:h3
    
    477
    -          class="tor-bridges-current-heading tor-focusable-heading tor-small-heading tor-search-heading"
    
    478
    -          tabindex="-1"
    
    479
    -          data-l10n-id="tor-bridges-your-bridges"
    
    480
    -        ></html:h3>
    
    481
    -        <html:span
    
    482
    -          id="tor-bridges-user-label"
    
    483
    -          class="tor-bridges-source-label"
    
    484
    -          data-l10n-id="tor-bridges-source-user"
    
    485
    -        ></html:span>
    
    486
    -        <html:span
    
    487
    -          id="tor-bridges-built-in-label"
    
    488
    -          class="tor-bridges-source-label"
    
    489
    -          data-l10n-id="tor-bridges-source-built-in"
    
    490
    -        ></html:span>
    
    491
    -        <html:span
    
    492
    -          id="tor-bridges-requested-label"
    
    493
    -          class="tor-bridges-source-label"
    
    494
    -          data-l10n-id="tor-bridges-source-requested"
    
    495
    -        ></html:span>
    
    496
    -        <html:span id="tor-bridges-lox-label" class="tor-bridges-source-label">
    
    497
    -          <html:img id="tor-bridges-lox-label-icon" alt="" />
    
    498
    -          <html:span data-l10n-id="tor-bridges-source-lox"></html:span>
    
    499
    -        </html:span>
    
    500
    -        <html:button
    
    501
    -          id="tor-bridges-all-options-button"
    
    502
    -          class="tor-bridges-options-button"
    
    503
    -          aria-haspopup="menu"
    
    504
    -          aria-expanded="false"
    
    505
    -          aria-controls="tor-bridges-all-options-menu"
    
    506
    -          data-l10n-id="tor-bridges-options-button"
    
    507
    -        ></html:button>
    
    508
    -        <html:panel-list
    
    509
    -          id="tor-bridges-all-options-menu"
    
    510
    -          data-hidden-from-search="true"
    
    511
    -        >
    
    512
    -          <html:panel-item
    
    513
    -            id="tor-bridges-options-qr-all-menu-item"
    
    514
    -            data-l10n-attrs="accesskey"
    
    515
    -            data-l10n-id="tor-bridges-menu-item-qr-all-bridge-addresses"
    
    516
    -          ></html:panel-item>
    
    517
    -          <html:panel-item
    
    518
    -            id="tor-bridges-options-copy-all-menu-item"
    
    519
    -            data-l10n-attrs="accesskey"
    
    520
    -            data-l10n-id="tor-bridges-menu-item-copy-all-bridge-addresses"
    
    521
    -          ></html:panel-item>
    
    522
    -          <html:panel-item
    
    523
    -            id="tor-bridges-options-edit-all-menu-item"
    
    524
    -            data-l10n-attrs="accesskey"
    
    525
    -            data-l10n-id="tor-bridges-menu-item-edit-all-bridges"
    
    526
    -          ></html:panel-item>
    
    527
    -          <html:panel-item
    
    528
    -            id="tor-bridges-options-remove-all-menu-item"
    
    529
    -            data-l10n-attrs="accesskey"
    
    530
    -            data-l10n-id="tor-bridges-menu-item-remove-all-bridges"
    
    531
    -          ></html:panel-item>
    
    532
    -        </html:panel-list>
    
    533
    -      </html:div>
    
    534
    -      <html:div id="tor-bridges-built-in-display" hidden="hidden">
    
    535
    -        <html:div id="tor-bridges-built-in-type-name"></html:div>
    
    536
    -        <html:div
    
    537
    -          id="tor-bridges-built-in-connected"
    
    538
    -          class="bridge-status-badge"
    
    539
    -        >
    
    540
    -          <html:div class="bridge-status-icon"></html:div>
    
    541
    -          <html:span
    
    542
    -            data-l10n-id="tor-bridges-built-in-status-connected"
    
    543
    -          ></html:span>
    
    544
    -        </html:div>
    
    545
    -        <html:div id="tor-bridges-built-in-description"></html:div>
    
    546
    -      </html:div>
    
    547
    -      <html:div
    
    548
    -        id="tor-bridges-grid-display"
    
    549
    -        class="tor-bridges-grid"
    
    550
    -        role="grid"
    
    551
    -        aria-labelledby="tor-bridges-current-heading-non-search"
    
    552
    -        hidden="hidden"
    
    553
    -      ></html:div>
    
    554
    -      <html:template id="tor-bridges-grid-row-template">
    
    555
    -        <html:div class="tor-bridges-grid-row" role="row">
    
    556
    -          <!-- TODO: lox status cell for new bridges? -->
    
    557
    -          <html:span
    
    558
    -            class="tor-bridges-type-cell tor-bridges-grid-cell"
    
    559
    -            role="gridcell"
    
    560
    -          ></html:span>
    
    561
    -          <html:span class="tor-bridges-emojis-block" role="none"></html:span>
    
    562
    -          <html:span class="tor-bridges-grid-end-block" role="none">
    
    563
    -            <html:span
    
    564
    -              class="tor-bridges-address-cell tor-bridges-grid-cell"
    
    565
    -              role="gridcell"
    
    566
    -            >
    
    567
    -              <html:span class="tor-bridges-address-cell-text"></html:span>
    
    568
    -            </html:span>
    
    569
    -            <html:span
    
    570
    -              class="tor-bridges-status-cell tor-bridges-grid-cell"
    
    571
    -              role="gridcell"
    
    572
    -            >
    
    573
    -              <html:div class="bridge-status-badge">
    
    574
    -                <html:div class="bridge-status-icon"></html:div>
    
    575
    -                <html:span class="tor-bridges-status-cell-text"></html:span>
    
    576
    -              </html:div>
    
    577
    -            </html:span>
    
    578
    -            <html:span
    
    579
    -              class="tor-bridges-options-cell tor-bridges-grid-cell"
    
    580
    -              role="gridcell"
    
    581
    -            >
    
    582
    -              <html:button
    
    583
    -                class="tor-bridges-options-cell-button tor-bridges-options-button tor-bridges-grid-focus"
    
    584
    -                aria-haspopup="menu"
    
    585
    -                aria-expanded="false"
    
    586
    -                data-l10n-id="tor-bridges-individual-bridge-options-button"
    
    587
    -              ></html:button>
    
    588
    -              <html:panel-list
    
    589
    -                class="tor-bridges-individual-options-menu"
    
    590
    -                data-hidden-from-search="true"
    
    591
    -              >
    
    592
    -                <html:panel-item
    
    593
    -                  class="tor-bridges-options-qr-one-menu-item"
    
    594
    -                  data-l10n-attrs="accesskey"
    
    595
    -                  data-l10n-id="tor-bridges-menu-item-qr-address"
    
    596
    -                ></html:panel-item>
    
    597
    -                <html:panel-item
    
    598
    -                  class="tor-bridges-options-copy-one-menu-item"
    
    599
    -                  data-l10n-attrs="accesskey"
    
    600
    -                  data-l10n-id="tor-bridges-menu-item-copy-address"
    
    601
    -                ></html:panel-item>
    
    602
    -                <html:panel-item
    
    603
    -                  class="tor-bridges-options-remove-one-menu-item"
    
    604
    -                  data-l10n-attrs="accesskey"
    
    605
    -                  data-l10n-id="tor-bridges-menu-item-remove-bridge"
    
    606
    -                ></html:panel-item>
    
    607
    -              </html:panel-list>
    
    608
    -            </html:span>
    
    609
    -          </html:span>
    
    610
    -        </html:div>
    
    611
    -      </html:template>
    
    612
    -      <html:div
    
    613
    -        id="tor-bridges-share"
    
    614
    -        class="tor-bridges-details-box"
    
    615
    -        hidden="hidden"
    
    616
    -      >
    
    617
    -        <html:h3
    
    618
    -          class="tor-bridges-share-heading tor-small-heading tor-non-search-heading"
    
    619
    -          data-l10n-id="tor-bridges-share-heading"
    
    620
    -        ></html:h3>
    
    621
    -        <!-- Add a duplicate search heading. See tor-browser#43320. -->
    
    622
    -        <html:h4
    
    623
    -          class="tor-bridges-share-heading tor-small-heading tor-search-heading"
    
    624
    -          data-l10n-id="tor-bridges-share-heading"
    
    625
    -        ></html:h4>
    
    626
    -        <html:span
    
    627
    -          id="tor-bridges-share-description"
    
    628
    -          data-l10n-id="tor-bridges-share-description"
    
    629
    -        ></html:span>
    
    630
    -        <html:button
    
    631
    -          id="tor-bridges-copy-addresses-button"
    
    632
    -          data-l10n-id="tor-bridges-copy-addresses-button"
    
    633
    -        ></html:button>
    
    634
    -        <html:button
    
    635
    -          id="tor-bridges-qr-addresses-button"
    
    636
    -          data-l10n-id="tor-bridges-qr-addresses-button"
    
    637
    -        ></html:button>
    
    638
    -      </html:div>
    
    639
    -      <html:div id="tor-bridges-lox-status" hidden="hidden">
    
    640
    -        <html:div data-l10n-id="tor-bridges-lox-description"></html:div>
    
    641
    -        <html:div
    
    642
    -          id="tor-bridges-lox-details"
    
    643
    -          class="tor-bridges-details-box tor-bridges-lox-box"
    
    644
    -          hidden="hidden"
    
    645
    -        >
    
    646
    -          <html:img alt="" class="tor-bridges-lox-image-inner" />
    
    647
    -          <html:img alt="" class="tor-bridges-lox-image-outer" />
    
    648
    -          <html:h3
    
    649
    -            class="tor-bridges-lox-next-unlock-counter tor-small-heading tor-bridges-lox-intro tor-focusable-heading tor-non-search-heading"
    
    650
    -            tabindex="-1"
    
    651
    -          ></html:h3>
    
    652
    -          <!-- Add a duplicate search heading. See tor-browser#43320. -->
    
    653
    -          <html:h4
    
    654
    -            class="tor-bridges-lox-next-unlock-counter tor-small-heading tor-bridges-lox-intro tor-focusable-heading tor-search-heading"
    
    655
    -            tabindex="-1"
    
    656
    -          ></html:h4>
    
    657
    -          <html:ul class="tor-bridges-lox-list">
    
    658
    -            <html:li
    
    659
    -              id="tor-bridges-lox-next-unlock-gain-bridges"
    
    660
    -              class="tor-bridges-lox-list-item tor-bridges-lox-list-item-bridge"
    
    661
    -              data-l10n-id="tor-bridges-lox-unlock-two-bridges"
    
    662
    -              hidden="hidden"
    
    663
    -            ></html:li>
    
    664
    -            <html:li
    
    665
    -              id="tor-bridges-lox-next-unlock-first-invites"
    
    666
    -              class="tor-bridges-lox-list-item tor-bridges-lox-list-item-invite"
    
    667
    -              data-l10n-id="tor-bridges-lox-unlock-first-invites"
    
    668
    -              hidden="hidden"
    
    669
    -            ></html:li>
    
    670
    -            <html:li
    
    671
    -              id="tor-bridges-lox-next-unlock-more-invites"
    
    672
    -              class="tor-bridges-lox-list-item tor-bridges-lox-list-item-invite"
    
    673
    -              data-l10n-id="tor-bridges-lox-unlock-more-invites"
    
    674
    -              hidden="hidden"
    
    675
    -            ></html:li>
    
    676
    -          </html:ul>
    
    677
    -          <html:div
    
    678
    -            id="tor-bridges-lox-remaining-invites"
    
    679
    -            hidden="hidden"
    
    680
    -          ></html:div>
    
    681
    -          <html:button
    
    682
    -            id="tor-bridges-lox-show-invites-button"
    
    683
    -            class="tor-bridges-lox-button"
    
    684
    -            data-l10n-id="tor-bridges-lox-show-invites-button"
    
    685
    -            hidden="hidden"
    
    686
    -          ></html:button>
    
    687
    -        </html:div>
    
    688
    -        <html:div
    
    689
    -          id="tor-bridges-lox-unlock-alert"
    
    690
    -          role="alert"
    
    691
    -          class="tor-bridges-details-box tor-bridges-lox-box"
    
    692
    -          hidden="hidden"
    
    693
    -        >
    
    694
    -          <html:img alt="" class="tor-bridges-lox-image-inner" />
    
    695
    -          <html:img alt="" class="tor-bridges-lox-image-outer" />
    
    696
    -          <html:div
    
    697
    -            id="tor-bridge-unlock-alert-title"
    
    698
    -            class="tor-small-heading tor-bridges-lox-intro"
    
    699
    -          ></html:div>
    
    700
    -          <html:ul class="tor-bridges-lox-list">
    
    701
    -            <html:li
    
    702
    -              id="tor-bridges-lox-unlock-alert-gain-bridges"
    
    703
    -              class="tor-bridges-lox-list-item tor-bridges-lox-list-item-bridge"
    
    704
    -              data-l10n-id="tor-bridges-lox-gained-two-bridges"
    
    705
    -              hidden="hidden"
    
    706
    -            ></html:li>
    
    707
    -            <html:li
    
    708
    -              id="tor-bridges-lox-unlock-alert-new-bridges"
    
    709
    -              class="tor-bridges-lox-list-item tor-bridges-lox-list-item-bridge"
    
    710
    -              data-l10n-id="tor-bridges-lox-new-bridges"
    
    711
    -              hidden="hidden"
    
    712
    -            ></html:li>
    
    713
    -            <html:li
    
    714
    -              id="tor-bridges-lox-unlock-alert-invites"
    
    715
    -              class="tor-bridges-lox-list-item tor-bridges-lox-list-item-invite"
    
    716
    -              hidden="hidden"
    
    717
    -            ></html:li>
    
    718
    -          </html:ul>
    
    719
    -          <html:button
    
    720
    -            id="tor-bridges-lox-unlock-alert-button"
    
    721
    -            class="tor-bridges-lox-button"
    
    722
    -            data-l10n-id="tor-bridges-lox-got-it-button"
    
    723
    -          ></html:button>
    
    724
    -        </html:div>
    
    725
    -      </html:div>
    
    726
    -    </html:div>
    
    727
    -    <html:h2
    
    728
    -      class="tor-bridges-change-heading tor-medium-heading tor-non-search-heading"
    
    729
    -    ></html:h2>
    
    730
    -    <!-- Add a duplicate search heading. See tor-browser#43320.
    
    731
    -       - This has the same content, but a smaller font. -->
    
    732
    -    <html:h3
    
    733
    -      class="tor-bridges-change-heading tor-small-heading tor-search-heading"
    
    734
    -    ></html:h3>
    
    735
    -    <hbox align="center">
    
    736
    -      <description
    
    737
    -        flex="1"
    
    738
    -        data-l10n-id="tor-bridges-select-built-in-description"
    
    739
    -      />
    
    740
    -      <html:button
    
    741
    -        id="tor-bridges-open-built-in-dialog-button"
    
    742
    -        class="accessory-button"
    
    743
    -        data-l10n-id="tor-bridges-select-built-in-button"
    
    744
    -      ></html:button>
    
    745
    -    </hbox>
    
    746
    -    <hbox align="center">
    
    747
    -      <description id="tor-bridges-user-provide-description" flex="1" />
    
    748
    -      <html:button
    
    749
    -        id="tor-bridges-open-user-provide-dialog-button"
    
    750
    -        class="accessory-button"
    
    751
    -      ></html:button>
    
    752
    -    </hbox>
    
    753
    -    <html:h3
    
    754
    -      class="tor-bridges-provider-heading tor-medium-heading tor-non-search-heading"
    
    755
    -      data-l10n-id="tor-bridges-find-more-heading"
    
    756
    -    ></html:h3>
    
    757
    -    <!-- Add a duplicate search heading. See tor-browser#43320.
    
    758
    -       - This has the same content, but a smaller font. -->
    
    759
    -    <html:h4
    
    760
    -      class="tor-bridges-provider-heading tor-small-heading tor-search-heading"
    
    761
    -      data-l10n-id="tor-bridges-find-more-heading"
    
    762
    -    ></html:h4>
    
    763
    -    <description
    
    764
    -      data-l10n-id="tor-bridges-find-more-description"
    
    765
    -      class="description-deemphasized"
    
    766
    -    />
    
    767
    -    <html:div id="tor-bridges-provider-area">
    
    768
    -      <html:ul id="tor-bridges-provider-list">
    
    769
    -        <html:li class="tor-bridges-provider-item">
    
    770
    -          <html:img
    
    771
    -            id="tor-bridges-provider-icon-telegram"
    
    772
    -            class="tor-bridges-provider-icon"
    
    773
    -            alt=""
    
    774
    -          />
    
    775
    -          <html:div
    
    776
    -            class="tor-bridges-provider-name"
    
    777
    -            data-l10n-id="tor-bridges-provider-telegram-name"
    
    778
    -          ></html:div>
    
    779
    -          <html:div
    
    780
    -            id="tor-bridges-provider-instruction-telegram"
    
    781
    -            class="tor-bridges-provider-instruction"
    
    782
    -          >
    
    783
    -            <html:a data-l10n-name="user"></html:a>
    
    784
    -          </html:div>
    
    785
    -        </html:li>
    
    786
    -        <html:li class="tor-bridges-provider-item">
    
    787
    -          <html:img
    
    788
    -            id="tor-bridges-provider-icon-web"
    
    789
    -            class="tor-bridges-provider-icon"
    
    790
    -            alt=""
    
    791
    -          />
    
    792
    -          <html:div
    
    793
    -            class="tor-bridges-provider-name"
    
    794
    -            data-l10n-id="tor-bridges-provider-web-name"
    
    795
    -          ></html:div>
    
    796
    -          <html:div
    
    797
    -            class="tor-bridges-provider-instruction"
    
    798
    -            data-l10n-id="tor-bridges-provider-web-instruction"
    
    799
    -            data-l10n-args='{ "url": "bridges.torproject.org" }'
    
    800
    -          >
    
    801
    -            <html:a
    
    802
    -              href=""
    
    803
    -              data-l10n-name="url"
    
    804
    -            ></html:a>
    
    805
    -          </html:div>
    
    806
    -        </html:li>
    
    807
    -        <html:li class="tor-bridges-provider-item">
    
    808
    -          <html:img
    
    809
    -            id="tor-bridges-provider-icon-email"
    
    810
    -            class="tor-bridges-provider-icon"
    
    811
    -            alt=""
    
    812
    -          />
    
    813
    -          <html:div
    
    814
    -            class="tor-bridges-provider-name"
    
    815
    -            data-l10n-id="tor-bridges-provider-email-name"
    
    816
    -          ></html:div>
    
    817
    -          <html:div
    
    818
    -            class="tor-bridges-provider-instruction"
    
    819
    -            data-l10n-id="tor-bridges-provider-email-instruction"
    
    820
    -            data-l10n-args='{ "address": "bridges@xxxxxxxxxxxxxx" }'
    
    821
    -          ></html:div>
    
    822
    -        </html:li>
    
    823
    -      </html:ul>
    
    824
    -      <html:div id="tor-bridges-request-box" class="tor-bridges-box">
    
    825
    -        <html:img
    
    826
    -          alt=""
    
    827
    -          src=""
    
    828
    -        ></html:img>
    
    829
    -        <html:div
    
    830
    -          id="tor-bridges-request-description"
    
    831
    -          data-l10n-id="tor-bridges-request-from-browser"
    
    832
    -        ></html:div>
    
    833
    -        <html:button
    
    834
    -          id="tor-bridges-open-request-dialog-button"
    
    835
    -          data-l10n-id="tor-bridges-request-button"
    
    836
    -        ></html:button>
    
    837
    -      </html:div>
    
    838
    -    </html:div>
    
    839
    -  </groupbox>
    
    840
    -
    
    841
    -  <!-- Advanced -->
    
    842
    -  <hbox
    
    843
    -    class="subcategory"
    
    844
    -    data-category="paneConnection"
    
    845
    -    hidden="true"
    
    846
    -    data-srd-groupid="connectionStatus"
    
    847
    -  >
    
    848
    -    <html:h1
    
    849
    -      id="tor-advanced-subcategory-heading-non-search"
    
    850
    -      data-l10n-id="tor-advanced-settings-heading"
    
    851
    -    ></html:h1>
    
    852
    -  </hbox>
    
    853
    -  <groupbox
    
    854
    -    id="torPreferences-advanced-group"
    
    855
    -    data-category="paneConnection"
    
    856
    -    hidden="true"
    
    857
    -    aria-labelledby="tor-advanced-subcategory-heading-non-search"
    
    858
    -    data-srd-groupid="connectionStatus"
    
    859
    -  >
    
    860
    -    <!-- Add a search-header that only appears in search results as a substitute
    
    861
    -       - for the hidden h1 element. See tor-browser#43320.
    
    862
    -       - NOTE: Usually the first xul:label will act as the accessible name for
    
    863
    -       - a xul:groubbox element *if* it is not hidden. Since the search-header
    
    864
    -       - is sometimes hidden we need an explicit aria-labelledby anyway.
    
    865
    -       - However, we keep the wrapper xul:label for styling consistency with the
    
    866
    -       - other settings. -->
    
    867
    -    <label class="search-header" hidden="true">
    
    868
    -      <html:h2 data-l10n-id="tor-advanced-settings-heading"></html:h2>
    
    869
    -    </label>
    
    870
    -    <hbox align="center">
    
    871
    -      <description data-l10n-id="tor-advanced-settings-description" flex="1" />
    
    872
    -      <html:button
    
    873
    -        id="torPreferences-advanced-button"
    
    874
    -        class="accessory-button"
    
    875
    -        data-l10n-id="tor-advanced-settings-button"
    
    876
    -      ></html:button>
    
    877
    -    </hbox>
    
    878
    -    <hbox align="center" data-subcategory="viewlogs">
    
    879
    -      <description data-l10n-id="tor-view-log-description" flex="1" />
    
    880
    -      <html:button
    
    881
    -        id="torPreferences-buttonTorLogs"
    
    882
    -        class="accessory-button"
    
    883
    -        data-l10n-id="tor-view-log-button"
    
    884
    -      ></html:button>
    
    885
    -    </hbox>
    
    886
    -  </groupbox>
    
    887
    -</html:template>

  • browser/components/torpreferences/content/connectionPane.js deleted
    1
    -// Copyright (c) 2022, The Tor Project, Inc.
    
    2
    -// This Source Code Form is subject to the terms of the Mozilla Public
    
    3
    -// License, v. 2.0. If a copy of the MPL was not distributed with this
    
    4
    -// file, You can obtain one at http://mozilla.org/MPL/2.0/.
    
    5
    -
    
    6
    -"use strict";
    
    7
    -
    
    8
    -/* import-globals-from /browser/components/preferences/preferences.js */
    
    9
    -/* import-globals-from /browser/components/preferences/search.js */
    
    10
    -
    
    11
    -const { setTimeout, clearTimeout } = ChromeUtils.importESModule(
    
    12
    -  "resource://gre/modules/Timer.sys.mjs"
    
    13
    -);
    
    14
    -
    
    15
    -const { TorSettings, TorSettingsTopics, TorBridgeSource } =
    
    16
    -  ChromeUtils.importESModule("moz-src:///toolkit/modules/TorSettings.sys.mjs");
    
    17
    -
    
    18
    -const { TorParsers } = ChromeUtils.importESModule(
    
    19
    -  "moz-src:///toolkit/components/tor-launcher/TorParsers.sys.mjs"
    
    20
    -);
    
    21
    -const { TorProviderBuilder, TorProviderTopics } = ChromeUtils.importESModule(
    
    22
    -  "moz-src:///toolkit/components/tor-launcher/TorProviderBuilder.sys.mjs"
    
    23
    -);
    
    24
    -
    
    25
    -const { InternetStatus, TorConnect, TorConnectTopics, TorConnectStage } =
    
    26
    -  ChromeUtils.importESModule("moz-src:///toolkit/modules/TorConnect.sys.mjs");
    
    27
    -
    
    28
    -const { TorConnectParent } = ChromeUtils.importESModule(
    
    29
    -  "moz-src:///browser/components/torconnect/TorConnectParent.sys.mjs"
    
    30
    -);
    
    31
    -
    
    32
    -const { QRCode } = ChromeUtils.importESModule(
    
    33
    -  "moz-src:///toolkit/modules/QRCode.sys.mjs"
    
    34
    -);
    
    35
    -
    
    36
    -const { TorStrings } = ChromeUtils.importESModule(
    
    37
    -  "moz-src:///toolkit/modules/TorStrings.sys.mjs"
    
    38
    -);
    
    39
    -
    
    40
    -const { Lox, LoxTopics } = ChromeUtils.importESModule(
    
    41
    -  "moz-src:///toolkit/components/lox/Lox.sys.mjs"
    
    42
    -);
    
    43
    -
    
    44
    -const log = console.createInstance({
    
    45
    -  maxLogLevel: "Warn",
    
    46
    -  prefix: "connectionPane",
    
    47
    -});
    
    48
    -
    
    49
    -/*
    
    50
    - * Fake Lox module:
    
    51
    -
    
    52
    -const Lox = {
    
    53
    -  levelHistory: [0, 1],
    
    54
    -  // levelHistory: [1, 2],
    
    55
    -  // levelHistory: [2, 3],
    
    56
    -  // levelHistory: [3, 4],
    
    57
    -  // levelHistory: [0, 1, 2],
    
    58
    -  // levelHistory: [1, 2, 3],
    
    59
    -  // levelHistory: [4, 3],
    
    60
    -  // levelHistory: [4, 1],
    
    61
    -  // levelHistory: [2, 1],
    
    62
    -  //levelHistory: [2, 3, 4, 1, 2],
    
    63
    -  // Gain some invites and then loose them all. Shouldn't show any change.
    
    64
    -  // levelHistory: [0, 1, 2, 1],
    
    65
    -  // levelHistory: [1, 2, 3, 1],
    
    66
    -  getEventData() {
    
    67
    -    let prevLevel = this.levelHistory[0];
    
    68
    -    const events = [];
    
    69
    -    for (let i = 1; i < this.levelHistory.length; i++) {
    
    70
    -      const level = this.levelHistory[i];
    
    71
    -      events.push({ type: level > prevLevel ? "levelup" : "blockage", newLevel: level });
    
    72
    -      prevLevel = level;
    
    73
    -    }
    
    74
    -    return events;
    
    75
    -  },
    
    76
    -  clearEventData() {
    
    77
    -    this.levelHistory = [];
    
    78
    -  },
    
    79
    -  nextUnlock: { date: "2024-01-31T00:00:00Z", nextLevel: 1 },
    
    80
    -  //nextUnlock: { date: "2024-01-31T00:00:00Z", nextLevel: 2 },
    
    81
    -  //nextUnlock: { date: "2024-01-31T00:00:00Z", nextLevel: 3 },
    
    82
    -  //nextUnlock: { date: "2024-01-31T00:00:00Z", nextLevel: 4 },
    
    83
    -  getNextUnlock() {
    
    84
    -    return this.nextUnlock;
    
    85
    -  },
    
    86
    -  remainingInvites: 3,
    
    87
    -  // remainingInvites: 0,
    
    88
    -  getRemainingInviteCount() {
    
    89
    -    return this.remainingInvites;
    
    90
    -  },
    
    91
    -  invites: [],
    
    92
    -  // invites: ["a", "b"],
    
    93
    -  getInvites() {
    
    94
    -    return this.invites;
    
    95
    -  },
    
    96
    -};
    
    97
    -*/
    
    98
    -
    
    99
    -/**
    
    100
    - * Get the ID/fingerprint of the bridge used in the most recent Tor circuit.
    
    101
    - *
    
    102
    - * @returns {string?} - The bridge ID or null if a bridge with an id was not
    
    103
    - *   used in the last circuit.
    
    104
    - */
    
    105
    -async function getConnectedBridgeId() {
    
    106
    -  // TODO: PieroV: We could make sure TorSettings is in sync by monitoring also
    
    107
    -  // changes of settings. At that point, we could query it, instead of doing a
    
    108
    -  // query over the control port.
    
    109
    -  let bridge = null;
    
    110
    -  try {
    
    111
    -    const provider = await TorProviderBuilder.build();
    
    112
    -    bridge = provider.currentBridge;
    
    113
    -  } catch (e) {
    
    114
    -    console.warn("Could not get current bridge", e);
    
    115
    -  }
    
    116
    -  return bridge?.fingerprint ?? null;
    
    117
    -}
    
    118
    -
    
    119
    -/**
    
    120
    - * Show the bridge QR to the user.
    
    121
    - *
    
    122
    - * @param {string} bridgeString - The string to use in the QR.
    
    123
    - */
    
    124
    -function showBridgeQr(bridgeString) {
    
    125
    -  gSubDialog.open(
    
    126
    -    "chrome://browser/content/torpreferences/bridgeQrDialog.xhtml",
    
    127
    -    { features: "resizable=yes" },
    
    128
    -    bridgeString
    
    129
    -  );
    
    130
    -}
    
    131
    -
    
    132
    -// TODO: Instead of aria-live in the DOM, use the proposed ariaNotify
    
    133
    -// API if it gets accepted into firefox and works with screen readers.
    
    134
    -// See https://github.com/WICG/proposals/issues/112
    
    135
    -/**
    
    136
    - * Notification for screen reader users.
    
    137
    - */
    
    138
    -const gBridgesNotification = {
    
    139
    -  /**
    
    140
    -   * The screen reader area that shows updates.
    
    141
    -   *
    
    142
    -   * @type {Element?}
    
    143
    -   */
    
    144
    -  _updateArea: null,
    
    145
    -  /**
    
    146
    -   * The text for the screen reader update.
    
    147
    -   *
    
    148
    -   * @type {Element?}
    
    149
    -   */
    
    150
    -  _textEl: null,
    
    151
    -  /**
    
    152
    -   * A timeout for hiding the update.
    
    153
    -   *
    
    154
    -   * @type {integer?}
    
    155
    -   */
    
    156
    -  _hideUpdateTimeout: null,
    
    157
    -
    
    158
    -  /**
    
    159
    -   * Initialize the area for notifications.
    
    160
    -   */
    
    161
    -  init() {
    
    162
    -    this._updateArea = document.getElementById("tor-bridges-update-area");
    
    163
    -    this._textEl = document.getElementById("tor-bridges-update-area-text");
    
    164
    -  },
    
    165
    -
    
    166
    -  /**
    
    167
    -   * Post a new notification, replacing any existing one.
    
    168
    -   *
    
    169
    -   * @param {string} type - The notification type.
    
    170
    -   */
    
    171
    -  post(type) {
    
    172
    -    this._updateArea.hidden = false;
    
    173
    -    // First we clear the update area to reset the text to be empty.
    
    174
    -    this._textEl.removeAttribute("data-l10n-id");
    
    175
    -    this._textEl.textContent = "";
    
    176
    -    if (this._hideUpdateTimeout !== null) {
    
    177
    -      clearTimeout(this._hideUpdateTimeout);
    
    178
    -      this._hideUpdateTimeout = null;
    
    179
    -    }
    
    180
    -
    
    181
    -    let updateId;
    
    182
    -    switch (type) {
    
    183
    -      case "removed-one":
    
    184
    -        updateId = "tor-bridges-update-removed-one-bridge";
    
    185
    -        break;
    
    186
    -      case "removed-all":
    
    187
    -        updateId = "tor-bridges-update-removed-all-bridges";
    
    188
    -        break;
    
    189
    -      case "changed":
    
    190
    -      default:
    
    191
    -        // Generic message for when bridges change.
    
    192
    -        updateId = "tor-bridges-update-changed-bridges";
    
    193
    -        break;
    
    194
    -    }
    
    195
    -
    
    196
    -    // Hide the area after 5 minutes, when the update is not "recent" any
    
    197
    -    // more.
    
    198
    -    this._hideUpdateTimeout = setTimeout(() => {
    
    199
    -      this._updateArea.hidden = true;
    
    200
    -    }, 300000);
    
    201
    -
    
    202
    -    // Wait a small amount of time to actually set the textContent. Otherwise
    
    203
    -    // the screen reader (tested with Orca) may not pick up on the change in
    
    204
    -    // text.
    
    205
    -    setTimeout(() => {
    
    206
    -      document.l10n.setAttributes(this._textEl, updateId);
    
    207
    -    }, 500);
    
    208
    -  },
    
    209
    -};
    
    210
    -
    
    211
    -/**
    
    212
    - * Controls the bridge grid.
    
    213
    - */
    
    214
    -const gBridgeGrid = {
    
    215
    -  /**
    
    216
    -   * The grid element.
    
    217
    -   *
    
    218
    -   * @type {Element?}
    
    219
    -   */
    
    220
    -  _grid: null,
    
    221
    -  /**
    
    222
    -   * The template for creating new rows.
    
    223
    -   *
    
    224
    -   * @type {HTMLTemplateElement?}
    
    225
    -   */
    
    226
    -  _rowTemplate: null,
    
    227
    -
    
    228
    -  /**
    
    229
    -   * @typedef {object} BridgeGridRow
    
    230
    -   *
    
    231
    -   * @property {Element} element - The row element.
    
    232
    -   * @property {Element} optionsButton - The options button.
    
    233
    -   * @property {Element} menu - The options menupopup.
    
    234
    -   * @property {Element} statusEl - The bridge status element.
    
    235
    -   * @property {Element} statusText - The status text.
    
    236
    -   * @property {string} bridgeLine - The identifying bridge string for this row.
    
    237
    -   * @property {string?} bridgeId - The ID/fingerprint for the bridge, or null
    
    238
    -   *   if it doesn't have one.
    
    239
    -   * @property {integer} index - The index of the row in the grid.
    
    240
    -   * @property {boolean} connected - Whether we are connected to the bridge
    
    241
    -   *   (recently in use for a Tor circuit).
    
    242
    -   * @property {BridgeGridCell[]} cells - The cells that belong to the row,
    
    243
    -   *   ordered by their column.
    
    244
    -   */
    
    245
    -  /**
    
    246
    -   * @typedef {object} BridgeGridCell
    
    247
    -   *
    
    248
    -   * @property {Element} element - The cell element.
    
    249
    -   * @property {Element} focusEl - The element belonging to the cell that should
    
    250
    -   *   receive focus. Should be the cell element itself, or an interactive
    
    251
    -   *   focusable child.
    
    252
    -   * @property {integer} columnIndex - The index of the column this cell belongs
    
    253
    -   *   to.
    
    254
    -   * @property {BridgeGridRow} row - The row this cell belongs to.
    
    255
    -   */
    
    256
    -  /**
    
    257
    -   * The current rows in the grid.
    
    258
    -   *
    
    259
    -   * @type {BridgeGridRow[]}
    
    260
    -   */
    
    261
    -  _rows: [],
    
    262
    -  /**
    
    263
    -   * The cell that should be the focus target when the user moves focus into the
    
    264
    -   * grid, or null if the grid itself should be the target.
    
    265
    -   *
    
    266
    -   * @type {BridgeGridCell?}
    
    267
    -   */
    
    268
    -  _focusCell: null,
    
    269
    -
    
    270
    -  /**
    
    271
    -   * Initialize the bridge grid.
    
    272
    -   */
    
    273
    -  init() {
    
    274
    -    this._grid = document.getElementById("tor-bridges-grid-display");
    
    275
    -    // Initially, make only the grid itself part of the keyboard tab cycle.
    
    276
    -    // matches _focusCell = null.
    
    277
    -    this._grid.tabIndex = 0;
    
    278
    -
    
    279
    -    this._rowTemplate = document.getElementById(
    
    280
    -      "tor-bridges-grid-row-template"
    
    281
    -    );
    
    282
    -
    
    283
    -    this._grid.addEventListener("keydown", this);
    
    284
    -    this._grid.addEventListener("mousedown", this);
    
    285
    -    this._grid.addEventListener("focusin", this);
    
    286
    -
    
    287
    -    Services.obs.addObserver(this, TorSettingsTopics.SettingsChanged);
    
    288
    -
    
    289
    -    // NOTE: Before initializedPromise completes, this area is hidden.
    
    290
    -    TorSettings.initializedPromise.then(() => {
    
    291
    -      this._updateRows(true);
    
    292
    -    });
    
    293
    -  },
    
    294
    -
    
    295
    -  /**
    
    296
    -   * Uninitialize the bridge grid.
    
    297
    -   */
    
    298
    -  uninit() {
    
    299
    -    Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged);
    
    300
    -    this.deactivate();
    
    301
    -  },
    
    302
    -
    
    303
    -  /**
    
    304
    -   * Whether the grid is visible and responsive.
    
    305
    -   *
    
    306
    -   * @type {boolean}
    
    307
    -   */
    
    308
    -  _active: false,
    
    309
    -
    
    310
    -  /**
    
    311
    -   * Activate and show the bridge grid.
    
    312
    -   */
    
    313
    -  activate() {
    
    314
    -    if (this._active) {
    
    315
    -      return;
    
    316
    -    }
    
    317
    -
    
    318
    -    this._active = true;
    
    319
    -
    
    320
    -    Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
    
    321
    -
    
    322
    -    this._grid.hidden = false;
    
    323
    -
    
    324
    -    this._updateConnectedBridge();
    
    325
    -  },
    
    326
    -
    
    327
    -  /**
    
    328
    -   * Deactivate and hide the bridge grid.
    
    329
    -   */
    
    330
    -  deactivate() {
    
    331
    -    if (!this._active) {
    
    332
    -      return;
    
    333
    -    }
    
    334
    -
    
    335
    -    this._active = false;
    
    336
    -
    
    337
    -    this._forceCloseRowMenus();
    
    338
    -
    
    339
    -    this._grid.hidden = true;
    
    340
    -
    
    341
    -    Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
    
    342
    -  },
    
    343
    -
    
    344
    -  observe(subject, topic) {
    
    345
    -    switch (topic) {
    
    346
    -      case TorSettingsTopics.SettingsChanged: {
    
    347
    -        const { changes } = subject.wrappedJSObject;
    
    348
    -        if (
    
    349
    -          changes.includes("bridges.source") ||
    
    350
    -          changes.includes("bridges.bridge_strings")
    
    351
    -        ) {
    
    352
    -          this._updateRows();
    
    353
    -        }
    
    354
    -        break;
    
    355
    -      }
    
    356
    -      case TorProviderTopics.BridgeChanged:
    
    357
    -        this._updateConnectedBridge();
    
    358
    -        break;
    
    359
    -    }
    
    360
    -  },
    
    361
    -
    
    362
    -  handleEvent(event) {
    
    363
    -    if (event.type === "keydown") {
    
    364
    -      if (event.altKey || event.shiftKey || event.metaKey || event.ctrlKey) {
    
    365
    -        // Don't interfere with these events.
    
    366
    -        return;
    
    367
    -      }
    
    368
    -
    
    369
    -      if (this._rows.some(row => row.menu.open)) {
    
    370
    -        // Have an open menu, let the menu handle the event instead.
    
    371
    -        return;
    
    372
    -      }
    
    373
    -
    
    374
    -      let numRows = this._rows.length;
    
    375
    -      if (!numRows) {
    
    376
    -        // Nowhere for focus to go.
    
    377
    -        return;
    
    378
    -      }
    
    379
    -
    
    380
    -      let moveRow = 0;
    
    381
    -      let moveColumn = 0;
    
    382
    -      const isLTR = this._grid.matches(":dir(ltr)");
    
    383
    -      switch (event.key) {
    
    384
    -        case "ArrowDown":
    
    385
    -          moveRow = 1;
    
    386
    -          break;
    
    387
    -        case "ArrowUp":
    
    388
    -          moveRow = -1;
    
    389
    -          break;
    
    390
    -        case "ArrowRight":
    
    391
    -          moveColumn = isLTR ? 1 : -1;
    
    392
    -          break;
    
    393
    -        case "ArrowLeft":
    
    394
    -          moveColumn = isLTR ? -1 : 1;
    
    395
    -          break;
    
    396
    -        default:
    
    397
    -          return;
    
    398
    -      }
    
    399
    -
    
    400
    -      // Prevent scrolling the nearest scroll container.
    
    401
    -      event.preventDefault();
    
    402
    -
    
    403
    -      const curCell = this._focusCell;
    
    404
    -      let row = curCell ? curCell.row.index + moveRow : 0;
    
    405
    -      let column = curCell ? curCell.columnIndex + moveColumn : 0;
    
    406
    -
    
    407
    -      // Clamp in bounds.
    
    408
    -      if (row < 0) {
    
    409
    -        row = 0;
    
    410
    -      } else if (row >= numRows) {
    
    411
    -        row = numRows - 1;
    
    412
    -      }
    
    413
    -
    
    414
    -      const numCells = this._rows[row].cells.length;
    
    415
    -      if (column < 0) {
    
    416
    -        column = 0;
    
    417
    -      } else if (column >= numCells) {
    
    418
    -        column = numCells - 1;
    
    419
    -      }
    
    420
    -
    
    421
    -      const newCell = this._rows[row].cells[column];
    
    422
    -
    
    423
    -      if (newCell !== curCell) {
    
    424
    -        this._setFocus(newCell);
    
    425
    -      }
    
    426
    -    } else if (event.type === "mousedown") {
    
    427
    -      if (event.button !== 0) {
    
    428
    -        return;
    
    429
    -      }
    
    430
    -      // Move focus index to the clicked target.
    
    431
    -      // NOTE: Since the cells and the grid have "tabindex=-1", they are still
    
    432
    -      // click-focusable. Therefore, the default mousedown handler will try to
    
    433
    -      // move focus to it.
    
    434
    -      // Rather than block this default handler, we instead re-direct the focus
    
    435
    -      // to the correct cell in the "focusin" listener.
    
    436
    -      const newCell = this._getCellFromTarget(event.target);
    
    437
    -      // NOTE: If newCell is null, then we do nothing here, but instead wait for
    
    438
    -      // the focusin handler to trigger.
    
    439
    -      if (newCell && newCell !== this._focusCell) {
    
    440
    -        this._setFocus(newCell);
    
    441
    -      }
    
    442
    -    } else if (event.type === "focusin") {
    
    443
    -      const focusCell = this._getCellFromTarget(event.target);
    
    444
    -      if (focusCell !== this._focusCell) {
    
    445
    -        // Focus is not where it is expected.
    
    446
    -        // E.g. the user has clicked the edge of the grid.
    
    447
    -        // Restore focus immediately back to the cell we expect.
    
    448
    -        this._setFocus(this._focusCell);
    
    449
    -      }
    
    450
    -    }
    
    451
    -  },
    
    452
    -
    
    453
    -  /**
    
    454
    -   * Return the cell that was the target of an event.
    
    455
    -   *
    
    456
    -   * @param {Element} element - The target of an event.
    
    457
    -   *
    
    458
    -   * @returns {BridgeGridCell?} - The cell that the element belongs to, or null
    
    459
    -   *   if it doesn't belong to any cell.
    
    460
    -   */
    
    461
    -  _getCellFromTarget(element) {
    
    462
    -    for (const row of this._rows) {
    
    463
    -      for (const cell of row.cells) {
    
    464
    -        if (cell.element.contains(element)) {
    
    465
    -          return cell;
    
    466
    -        }
    
    467
    -      }
    
    468
    -    }
    
    469
    -    return null;
    
    470
    -  },
    
    471
    -
    
    472
    -  /**
    
    473
    -   * Determine whether the document's active element (focus) is within the grid
    
    474
    -   * or not.
    
    475
    -   *
    
    476
    -   * @returns {boolean} - Whether focus is within this grid or not.
    
    477
    -   */
    
    478
    -  _focusWithin() {
    
    479
    -    return this._grid.contains(document.activeElement);
    
    480
    -  },
    
    481
    -
    
    482
    -  /**
    
    483
    -   * Set the cell that should be the focus target of the grid, possibly moving
    
    484
    -   * the document's focus as well.
    
    485
    -   *
    
    486
    -   * @param {BridgeGridCell?} cell - The cell to make the focus target, or null
    
    487
    -   *   if the grid itself should be the target.
    
    488
    -   * @param {boolean} [focusWithin] - Whether focus should be moved within the
    
    489
    -   *   grid. If undefined, this will move focus if the grid currently contains
    
    490
    -   *   the document's focus.
    
    491
    -   */
    
    492
    -  _setFocus(cell, focusWithin) {
    
    493
    -    if (focusWithin === undefined) {
    
    494
    -      focusWithin = this._focusWithin();
    
    495
    -    }
    
    496
    -    const prevFocusElement = this._focusCell
    
    497
    -      ? this._focusCell.focusEl
    
    498
    -      : this._grid;
    
    499
    -    const newFocusElement = cell ? cell.focusEl : this._grid;
    
    500
    -
    
    501
    -    if (prevFocusElement !== newFocusElement) {
    
    502
    -      prevFocusElement.tabIndex = -1;
    
    503
    -      newFocusElement.tabIndex = 0;
    
    504
    -    }
    
    505
    -    // Set _focusCell now, before we potentially call "focus", which can trigger
    
    506
    -    // the "focusin" handler.
    
    507
    -    this._focusCell = cell;
    
    508
    -
    
    509
    -    if (focusWithin) {
    
    510
    -      // Focus was within the grid, so we need to actively move it to the new
    
    511
    -      // element.
    
    512
    -      newFocusElement.focus({ preventScroll: true });
    
    513
    -      // Scroll to the whole cell into view, rather than just the focus element.
    
    514
    -      (cell?.element ?? newFocusElement).scrollIntoView({
    
    515
    -        block: "nearest",
    
    516
    -        inline: "nearest",
    
    517
    -      });
    
    518
    -    }
    
    519
    -  },
    
    520
    -
    
    521
    -  /**
    
    522
    -   * Reset the grids focus to be the first row's first cell, if any.
    
    523
    -   *
    
    524
    -   * @param {boolean} [focusWithin] - Whether focus should be moved within the
    
    525
    -   *   grid. If undefined, this will move focus if the grid currently contains
    
    526
    -   *   the document's focus.
    
    527
    -   */
    
    528
    -  _resetFocus(focusWithin) {
    
    529
    -    this._setFocus(
    
    530
    -      this._rows.length ? this._rows[0].cells[0] : null,
    
    531
    -      focusWithin
    
    532
    -    );
    
    533
    -  },
    
    534
    -
    
    535
    -  /**
    
    536
    -   * The bridge ID/fingerprint of the most recently used bridge (appearing in
    
    537
    -   * the latest Tor circuit). Roughly corresponds to the bridge we are currently
    
    538
    -   * connected to.
    
    539
    -   *
    
    540
    -   * null if there are no such bridges.
    
    541
    -   *
    
    542
    -   * @type {string?}
    
    543
    -   */
    
    544
    -  _connectedBridgeId: null,
    
    545
    -  /**
    
    546
    -   * Update _connectedBridgeId.
    
    547
    -   */
    
    548
    -  async _updateConnectedBridge() {
    
    549
    -    const bridgeId = await getConnectedBridgeId();
    
    550
    -    if (bridgeId === this._connectedBridgeId) {
    
    551
    -      return;
    
    552
    -    }
    
    553
    -    this._connectedBridgeId = bridgeId;
    
    554
    -    for (const row of this._rows) {
    
    555
    -      this._updateRowStatus(row);
    
    556
    -    }
    
    557
    -  },
    
    558
    -
    
    559
    -  /**
    
    560
    -   * Update the status of a row.
    
    561
    -   *
    
    562
    -   * @param {BridgeGridRow} row - The row to update.
    
    563
    -   */
    
    564
    -  _updateRowStatus(row) {
    
    565
    -    const connected = row.bridgeId && this._connectedBridgeId === row.bridgeId;
    
    566
    -    // NOTE: row.connected is initially undefined, so won't match `connected`.
    
    567
    -    if (connected === row.connected) {
    
    568
    -      return;
    
    569
    -    }
    
    570
    -
    
    571
    -    row.connected = connected;
    
    572
    -
    
    573
    -    const noStatus = !connected;
    
    574
    -
    
    575
    -    row.element.classList.toggle("hide-status", noStatus);
    
    576
    -    row.statusEl.classList.toggle("bridge-status-none", noStatus);
    
    577
    -    row.statusEl.classList.toggle("bridge-status-connected", connected);
    
    578
    -
    
    579
    -    if (connected) {
    
    580
    -      document.l10n.setAttributes(
    
    581
    -        row.statusText,
    
    582
    -        "tor-bridges-status-connected"
    
    583
    -      );
    
    584
    -    } else {
    
    585
    -      document.l10n.setAttributes(row.statusText, "tor-bridges-status-none");
    
    586
    -    }
    
    587
    -  },
    
    588
    -
    
    589
    -  /**
    
    590
    -   * Create a new row for the grid.
    
    591
    -   *
    
    592
    -   * @param {string} bridgeLine - The bridge line for this row, which also acts
    
    593
    -   *   as its ID.
    
    594
    -   *
    
    595
    -   * @returns {BridgeGridRow} - A new row, with then "index" unset and the
    
    596
    -   *   "element" without a parent.
    
    597
    -   */
    
    598
    -  _createRow(bridgeLine) {
    
    599
    -    let details;
    
    600
    -    try {
    
    601
    -      details = TorParsers.parseBridgeLine(bridgeLine);
    
    602
    -    } catch (e) {
    
    603
    -      console.error(`Detected invalid bridge line: ${bridgeLine}`, e);
    
    604
    -    }
    
    605
    -    const row = {
    
    606
    -      element: this._rowTemplate.content.children[0].cloneNode(true),
    
    607
    -      bridgeLine,
    
    608
    -      bridgeId: details?.id ?? null,
    
    609
    -      cells: [],
    
    610
    -    };
    
    611
    -
    
    612
    -    const emojiBlock = row.element.querySelector(".tor-bridges-emojis-block");
    
    613
    -    const BridgeEmoji = customElements.get("tor-bridge-emoji");
    
    614
    -    for (const cell of BridgeEmoji.createForAddress(bridgeLine)) {
    
    615
    -      // Each emoji is its own cell, we rely on the fact that createForAddress
    
    616
    -      // always returns four elements.
    
    617
    -      cell.setAttribute("role", "gridcell");
    
    618
    -      cell.classList.add("tor-bridges-grid-cell", "tor-bridges-emoji-cell");
    
    619
    -      emojiBlock.append(cell);
    
    620
    -    }
    
    621
    -
    
    622
    -    for (const [columnIndex, element] of row.element
    
    623
    -      .querySelectorAll(".tor-bridges-grid-cell")
    
    624
    -      .entries()) {
    
    625
    -      const focusEl =
    
    626
    -        element.querySelector(".tor-bridges-grid-focus") ?? element;
    
    627
    -      // Set a negative tabIndex, this makes the element click-focusable but not
    
    628
    -      // part of the tab navigation sequence.
    
    629
    -      focusEl.tabIndex = -1;
    
    630
    -      row.cells.push({ element, focusEl, columnIndex, row });
    
    631
    -    }
    
    632
    -
    
    633
    -    const transport = details?.transport ?? "vanilla";
    
    634
    -    const typeCell = row.element.querySelector(".tor-bridges-type-cell");
    
    635
    -    if (transport === "vanilla") {
    
    636
    -      document.l10n.setAttributes(typeCell, "tor-bridges-type-prefix-generic");
    
    637
    -    } else {
    
    638
    -      document.l10n.setAttributes(typeCell, "tor-bridges-type-prefix", {
    
    639
    -        type: transport,
    
    640
    -      });
    
    641
    -    }
    
    642
    -
    
    643
    -    row.element.querySelector(".tor-bridges-address-cell-text").textContent =
    
    644
    -      bridgeLine;
    
    645
    -
    
    646
    -    row.statusEl = row.element.querySelector(
    
    647
    -      ".tor-bridges-status-cell .bridge-status-badge"
    
    648
    -    );
    
    649
    -    row.statusText = row.element.querySelector(".tor-bridges-status-cell-text");
    
    650
    -
    
    651
    -    this._initRowMenu(row);
    
    652
    -
    
    653
    -    this._updateRowStatus(row);
    
    654
    -    return row;
    
    655
    -  },
    
    656
    -
    
    657
    -  /**
    
    658
    -   * The row menu index used for generating new ids.
    
    659
    -   *
    
    660
    -   * @type {integer}
    
    661
    -   */
    
    662
    -  _rowMenuIndex: 0,
    
    663
    -  /**
    
    664
    -   * Generate a new id for the options menu.
    
    665
    -   *
    
    666
    -   * @returns {string} - The new id.
    
    667
    -   */
    
    668
    -  _generateRowMenuId() {
    
    669
    -    const id = `tor-bridges-individual-options-menu-${this._rowMenuIndex}`;
    
    670
    -    // Assume we won't run out of ids.
    
    671
    -    this._rowMenuIndex++;
    
    672
    -    return id;
    
    673
    -  },
    
    674
    -
    
    675
    -  /**
    
    676
    -   * Initialize the shared menu for a row.
    
    677
    -   *
    
    678
    -   * @param {BridgeGridRow} row - The row to initialize the menu of.
    
    679
    -   */
    
    680
    -  _initRowMenu(row) {
    
    681
    -    row.menu = row.element.querySelector(
    
    682
    -      ".tor-bridges-individual-options-menu"
    
    683
    -    );
    
    684
    -    row.optionsButton = row.element.querySelector(
    
    685
    -      ".tor-bridges-options-cell-button"
    
    686
    -    );
    
    687
    -
    
    688
    -    row.menu.id = this._generateRowMenuId();
    
    689
    -    row.optionsButton.setAttribute("aria-controls", row.menu.id);
    
    690
    -
    
    691
    -    row.optionsButton.addEventListener("click", event => {
    
    692
    -      row.menu.toggle(event);
    
    693
    -    });
    
    694
    -
    
    695
    -    row.menu.addEventListener("hidden", () => {
    
    696
    -      // Make sure the button receives focus again when the menu is hidden.
    
    697
    -      // Currently, panel-list.js only does this when the menu is opened with a
    
    698
    -      // keyboard, but this causes focus to be lost from the page if the user
    
    699
    -      // uses a mixture of keyboard and mouse.
    
    700
    -      row.optionsButton.focus();
    
    701
    -    });
    
    702
    -
    
    703
    -    const qrItem = row.menu.querySelector(
    
    704
    -      ".tor-bridges-options-qr-one-menu-item"
    
    705
    -    );
    
    706
    -    const removeItem = row.menu.querySelector(
    
    707
    -      ".tor-bridges-options-remove-one-menu-item"
    
    708
    -    );
    
    709
    -    row.menu.addEventListener("showing", () => {
    
    710
    -      const show =
    
    711
    -        this._bridgeSource === TorBridgeSource.UserProvided ||
    
    712
    -        this._bridgeSource === TorBridgeSource.BridgeDB;
    
    713
    -      qrItem.hidden = !show;
    
    714
    -      removeItem.hidden = !show;
    
    715
    -    });
    
    716
    -
    
    717
    -    qrItem.addEventListener("click", () => {
    
    718
    -      const bridgeLine = row.bridgeLine;
    
    719
    -      if (!bridgeLine) {
    
    720
    -        return;
    
    721
    -      }
    
    722
    -      showBridgeQr(bridgeLine);
    
    723
    -    });
    
    724
    -    row.menu
    
    725
    -      .querySelector(".tor-bridges-options-copy-one-menu-item")
    
    726
    -      .addEventListener("click", () => {
    
    727
    -        const clipboard = Cc[
    
    728
    -          "@mozilla.org/widget/clipboardhelper;1"
    
    729
    -        ].getService(Ci.nsIClipboardHelper);
    
    730
    -        clipboard.copyString(row.bridgeLine);
    
    731
    -      });
    
    732
    -    removeItem.addEventListener("click", () => {
    
    733
    -      const bridgeLine = row.bridgeLine;
    
    734
    -      const source = TorSettings.bridges.source;
    
    735
    -      const strings = TorSettings.bridges.bridge_strings;
    
    736
    -      const index = strings.indexOf(bridgeLine);
    
    737
    -      if (index === -1) {
    
    738
    -        return;
    
    739
    -      }
    
    740
    -      strings.splice(index, 1);
    
    741
    -
    
    742
    -      if (strings.length) {
    
    743
    -        TorSettings.changeSettings({
    
    744
    -          bridges: { source, bridge_strings: strings },
    
    745
    -        });
    
    746
    -      } else {
    
    747
    -        // Remove all bridges and disable.
    
    748
    -        TorSettings.changeSettings({
    
    749
    -          bridges: { source: TorBridgeSource.Invalid },
    
    750
    -        });
    
    751
    -      }
    
    752
    -    });
    
    753
    -  },
    
    754
    -
    
    755
    -  /**
    
    756
    -   * Force the row menu to close.
    
    757
    -   */
    
    758
    -  _forceCloseRowMenus() {
    
    759
    -    for (const row of this._rows) {
    
    760
    -      row.menu.hide(null, { force: true });
    
    761
    -    }
    
    762
    -  },
    
    763
    -
    
    764
    -  /**
    
    765
    -   * The known bridge source.
    
    766
    -   *
    
    767
    -   * Initially null to indicate that it is unset.
    
    768
    -   *
    
    769
    -   * @type {integer?}
    
    770
    -   */
    
    771
    -  _bridgeSource: null,
    
    772
    -  /**
    
    773
    -   * The bridge sources this is shown for.
    
    774
    -   *
    
    775
    -   * @type {string[]}
    
    776
    -   */
    
    777
    -  _supportedSources: [
    
    778
    -    TorBridgeSource.BridgeDB,
    
    779
    -    TorBridgeSource.UserProvided,
    
    780
    -    TorBridgeSource.Lox,
    
    781
    -  ],
    
    782
    -
    
    783
    -  /**
    
    784
    -   * Update the grid to show the latest bridge strings.
    
    785
    -   *
    
    786
    -   * @param {boolean} [initializing=false] - Whether this is being called as
    
    787
    -   *   part of initialization.
    
    788
    -   */
    
    789
    -  _updateRows(initializing = false) {
    
    790
    -    // Store whether we have focus within the grid, before removing or hiding
    
    791
    -    // DOM elements.
    
    792
    -    const focusWithin = this._focusWithin();
    
    793
    -
    
    794
    -    let lostAllBridges = false;
    
    795
    -    let newSource = false;
    
    796
    -    const bridgeSource = TorSettings.bridges.source;
    
    797
    -    if (bridgeSource !== this._bridgeSource) {
    
    798
    -      newSource = true;
    
    799
    -
    
    800
    -      this._bridgeSource = bridgeSource;
    
    801
    -
    
    802
    -      if (this._supportedSources.includes(bridgeSource)) {
    
    803
    -        this.activate();
    
    804
    -      } else {
    
    805
    -        if (this._active && bridgeSource === TorBridgeSource.Invalid) {
    
    806
    -          lostAllBridges = true;
    
    807
    -        }
    
    808
    -        this.deactivate();
    
    809
    -      }
    
    810
    -    }
    
    811
    -
    
    812
    -    const ordered = this._active
    
    813
    -      ? TorSettings.bridges.bridge_strings.map(bridgeLine => {
    
    814
    -          const row = this._rows.find(r => r.bridgeLine === bridgeLine);
    
    815
    -          if (row) {
    
    816
    -            return row;
    
    817
    -          }
    
    818
    -          return this._createRow(bridgeLine);
    
    819
    -        })
    
    820
    -      : [];
    
    821
    -
    
    822
    -    // Whether we should reset the grid's focus.
    
    823
    -    // We always reset when we have a new bridge source.
    
    824
    -    // We reset the focus if no current Cell has focus. I.e. when adding a row
    
    825
    -    // to an empty grid, we want the focus to move to the first item.
    
    826
    -    // We also reset the focus if the current Cell is in a row that will be
    
    827
    -    // removed (including if all rows are removed).
    
    828
    -    // NOTE: In principle, if a row is removed, we could move the focus to the
    
    829
    -    // next or previous row (in the same cell column). However, most likely if
    
    830
    -    // the grid has the user focus, they are removing a single row using its
    
    831
    -    // options button. In this case, returning the user to some other row's
    
    832
    -    // options button might be more disorienting since it would not be simple
    
    833
    -    // for them to know *which* bridge they have landed on.
    
    834
    -    // NOTE: We do not reset the focus in other cases because we do not want the
    
    835
    -    // user to loose their place in the grid unnecessarily.
    
    836
    -    let resetFocus =
    
    837
    -      newSource || !this._focusCell || !ordered.includes(this._focusCell.row);
    
    838
    -
    
    839
    -    // Remove rows no longer needed from the DOM.
    
    840
    -    let numRowsRemoved = 0;
    
    841
    -    let rowAddedOrMoved = false;
    
    842
    -
    
    843
    -    for (const row of this._rows) {
    
    844
    -      if (!ordered.includes(row)) {
    
    845
    -        numRowsRemoved++;
    
    846
    -        // If the row menu was open, it will also be deleted.
    
    847
    -        // NOTE: Since the row menu is part of the row, focusWithin will be true
    
    848
    -        // if the menu had focus, so focus should be re-assigned.
    
    849
    -        row.element.remove();
    
    850
    -      }
    
    851
    -    }
    
    852
    -
    
    853
    -    // Go through all the rows to set their ".index" property and to ensure they
    
    854
    -    // are in the correct position in the DOM.
    
    855
    -    // NOTE: We could use replaceChildren to get the correct DOM structure, but
    
    856
    -    // we want to avoid rebuilding the entire tree when a single row is added or
    
    857
    -    // removed.
    
    858
    -    for (const [index, row] of ordered.entries()) {
    
    859
    -      row.index = index;
    
    860
    -      const element = row.element;
    
    861
    -      // Get the expected previous element, that should already be in the DOM
    
    862
    -      // from the previous loop.
    
    863
    -      const prevEl = index ? ordered[index - 1].element : null;
    
    864
    -
    
    865
    -      if (
    
    866
    -        element.parentElement === this._grid &&
    
    867
    -        prevEl === element.previousElementSibling
    
    868
    -      ) {
    
    869
    -        // Already in the correct position in the DOM.
    
    870
    -        continue;
    
    871
    -      }
    
    872
    -
    
    873
    -      rowAddedOrMoved = true;
    
    874
    -      // NOTE: Any elements already in the DOM, but not in the correct position
    
    875
    -      // will be removed and re-added by the below command.
    
    876
    -      // NOTE: if the row has document focus, then it should remain there.
    
    877
    -      if (prevEl) {
    
    878
    -        prevEl.after(element);
    
    879
    -      } else {
    
    880
    -        this._grid.prepend(element);
    
    881
    -      }
    
    882
    -    }
    
    883
    -    this._rows = ordered;
    
    884
    -
    
    885
    -    // Restore any lost focus.
    
    886
    -    if (resetFocus) {
    
    887
    -      // If we are not active (and therefore hidden), we will not try and move
    
    888
    -      // focus (activeElement), but may still change the *focusable* element for
    
    889
    -      // when we are shown again.
    
    890
    -      this._resetFocus(this._active && focusWithin);
    
    891
    -    }
    
    892
    -    if (!this._active && focusWithin) {
    
    893
    -      // Move focus out of this element, which has been hidden.
    
    894
    -      gBridgeSettings.takeFocus();
    
    895
    -    }
    
    896
    -
    
    897
    -    // Notify the user if there was some change to the DOM.
    
    898
    -    // If we are initializing, we generate no notification since there has been
    
    899
    -    // no change in the setting.
    
    900
    -    if (!initializing) {
    
    901
    -      let notificationType;
    
    902
    -      if (lostAllBridges) {
    
    903
    -        // Just lost all bridges, and became de-active.
    
    904
    -        notificationType = "removed-all";
    
    905
    -      } else if (this._rows.length) {
    
    906
    -        // Otherwise, only generate a notification if we are still active, with
    
    907
    -        // at least one bridge.
    
    908
    -        // I.e. do not generate a message if the new source is "builtin".
    
    909
    -        if (newSource) {
    
    910
    -          // A change in source.
    
    911
    -          notificationType = "changed";
    
    912
    -        } else if (numRowsRemoved === 1 && !rowAddedOrMoved) {
    
    913
    -          // Only one bridge was removed. This is most likely in response to them
    
    914
    -          // manually removing a single bridge or using the bridge row's options
    
    915
    -          // menu.
    
    916
    -          notificationType = "removed-one";
    
    917
    -        } else if (numRowsRemoved || rowAddedOrMoved) {
    
    918
    -          // Some other change. This is most likely in response to a manual edit
    
    919
    -          // of the existing bridges.
    
    920
    -          notificationType = "changed";
    
    921
    -        }
    
    922
    -        // Else, there was no change.
    
    923
    -      }
    
    924
    -
    
    925
    -      if (notificationType) {
    
    926
    -        gBridgesNotification.post(notificationType);
    
    927
    -      }
    
    928
    -    }
    
    929
    -  },
    
    930
    -};
    
    931
    -
    
    932
    -/**
    
    933
    - * Controls the built-in bridges area.
    
    934
    - */
    
    935
    -const gBuiltinBridgesArea = {
    
    936
    -  /**
    
    937
    -   * The display area.
    
    938
    -   *
    
    939
    -   * @type {Element?}
    
    940
    -   */
    
    941
    -  _area: null,
    
    942
    -  /**
    
    943
    -   * The type name element.
    
    944
    -   *
    
    945
    -   * @type {Element?}
    
    946
    -   */
    
    947
    -  _nameEl: null,
    
    948
    -  /**
    
    949
    -   * The bridge type description element.
    
    950
    -   *
    
    951
    -   * @type {Element?}
    
    952
    -   */
    
    953
    -  _descriptionEl: null,
    
    954
    -  /**
    
    955
    -   * The connection status.
    
    956
    -   *
    
    957
    -   * @type {Element?}
    
    958
    -   */
    
    959
    -  _connectionStatusEl: null,
    
    960
    -
    
    961
    -  /**
    
    962
    -   * Initialize the built-in bridges area.
    
    963
    -   */
    
    964
    -  init() {
    
    965
    -    this._area = document.getElementById("tor-bridges-built-in-display");
    
    966
    -    this._nameEl = document.getElementById("tor-bridges-built-in-type-name");
    
    967
    -    this._descriptionEl = document.getElementById(
    
    968
    -      "tor-bridges-built-in-description"
    
    969
    -    );
    
    970
    -    this._connectionStatusEl = document.getElementById(
    
    971
    -      "tor-bridges-built-in-connected"
    
    972
    -    );
    
    973
    -
    
    974
    -    Services.obs.addObserver(this, TorSettingsTopics.SettingsChanged);
    
    975
    -
    
    976
    -    // NOTE: Before initializedPromise completes, this area is hidden.
    
    977
    -    TorSettings.initializedPromise.then(() => {
    
    978
    -      this._updateBridgeType(true);
    
    979
    -    });
    
    980
    -  },
    
    981
    -
    
    982
    -  /**
    
    983
    -   * Uninitialize the built-in bridges area.
    
    984
    -   */
    
    985
    -  uninit() {
    
    986
    -    Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged);
    
    987
    -    this.deactivate();
    
    988
    -  },
    
    989
    -
    
    990
    -  /**
    
    991
    -   * Whether the built-in area is visible and responsive.
    
    992
    -   *
    
    993
    -   * @type {boolean}
    
    994
    -   */
    
    995
    -  _active: false,
    
    996
    -
    
    997
    -  /**
    
    998
    -   * Activate and show the built-in bridge area.
    
    999
    -   */
    
    1000
    -  activate() {
    
    1001
    -    if (this._active) {
    
    1002
    -      return;
    
    1003
    -    }
    
    1004
    -    this._active = true;
    
    1005
    -
    
    1006
    -    Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
    
    1007
    -
    
    1008
    -    this._area.hidden = false;
    
    1009
    -
    
    1010
    -    this._updateBridgeIds();
    
    1011
    -    this._updateConnectedBridge();
    
    1012
    -  },
    
    1013
    -
    
    1014
    -  /**
    
    1015
    -   * Deactivate and hide built-in bridge area.
    
    1016
    -   */
    
    1017
    -  deactivate() {
    
    1018
    -    if (!this._active) {
    
    1019
    -      return;
    
    1020
    -    }
    
    1021
    -    this._active = false;
    
    1022
    -
    
    1023
    -    this._area.hidden = true;
    
    1024
    -
    
    1025
    -    Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
    
    1026
    -  },
    
    1027
    -
    
    1028
    -  observe(subject, topic) {
    
    1029
    -    switch (topic) {
    
    1030
    -      case TorSettingsTopics.SettingsChanged: {
    
    1031
    -        const { changes } = subject.wrappedJSObject;
    
    1032
    -        if (
    
    1033
    -          changes.includes("bridges.source") ||
    
    1034
    -          changes.includes("bridges.builtin_type")
    
    1035
    -        ) {
    
    1036
    -          this._updateBridgeType();
    
    1037
    -        }
    
    1038
    -        if (changes.includes("bridges.bridge_strings")) {
    
    1039
    -          this._updateBridgeIds();
    
    1040
    -        }
    
    1041
    -        break;
    
    1042
    -      }
    
    1043
    -      case TorProviderTopics.BridgeChanged:
    
    1044
    -        this._updateConnectedBridge();
    
    1045
    -        break;
    
    1046
    -    }
    
    1047
    -  },
    
    1048
    -
    
    1049
    -  /**
    
    1050
    -   * Updates the shown connected state.
    
    1051
    -   */
    
    1052
    -  _updateConnectedState() {
    
    1053
    -    this._connectionStatusEl.classList.toggle(
    
    1054
    -      "bridge-status-connected",
    
    1055
    -      this._bridgeType &&
    
    1056
    -        this._connectedBridgeId &&
    
    1057
    -        this._bridgeIds.includes(this._connectedBridgeId)
    
    1058
    -    );
    
    1059
    -  },
    
    1060
    -
    
    1061
    -  /**
    
    1062
    -   * The currently shown bridge type. Empty if deactivated, and null if
    
    1063
    -   * uninitialized.
    
    1064
    -   *
    
    1065
    -   * @type {string?}
    
    1066
    -   */
    
    1067
    -  _bridgeType: null,
    
    1068
    -  /**
    
    1069
    -   * The strings for each known bridge type.
    
    1070
    -   *
    
    1071
    -   * @type {{[key: string]: {[key: string]: string}}}
    
    1072
    -   */
    
    1073
    -  _bridgeTypeStrings: {
    
    1074
    -    obfs4: {
    
    1075
    -      name: "tor-bridges-built-in-obfs4-name",
    
    1076
    -      description: "tor-bridges-built-in-obfs4-description",
    
    1077
    -    },
    
    1078
    -    snowflake: {
    
    1079
    -      name: "tor-bridges-built-in-snowflake-name",
    
    1080
    -      description: "tor-bridges-built-in-snowflake-description",
    
    1081
    -    },
    
    1082
    -    meek: {
    
    1083
    -      name: "tor-bridges-built-in-meek-name",
    
    1084
    -      description: "tor-bridges-built-in-meek-description",
    
    1085
    -    },
    
    1086
    -  },
    
    1087
    -
    
    1088
    -  /**
    
    1089
    -   * The known bridge source.
    
    1090
    -   *
    
    1091
    -   * Initially null to indicate that it is unset.
    
    1092
    -   *
    
    1093
    -   * @type {integer?}
    
    1094
    -   */
    
    1095
    -  _bridgeSource: null,
    
    1096
    -
    
    1097
    -  /**
    
    1098
    -   * Update the shown bridge type.
    
    1099
    -   *
    
    1100
    -   * @param {boolean} [initializing=false] - Whether this is being called as
    
    1101
    -   *   part of initialization.
    
    1102
    -   */
    
    1103
    -  async _updateBridgeType(initializing = false) {
    
    1104
    -    let lostAllBridges = false;
    
    1105
    -    let newSource = false;
    
    1106
    -    const bridgeSource = TorSettings.bridges.source;
    
    1107
    -    if (bridgeSource !== this._bridgeSource) {
    
    1108
    -      newSource = true;
    
    1109
    -
    
    1110
    -      this._bridgeSource = bridgeSource;
    
    1111
    -
    
    1112
    -      if (bridgeSource === TorBridgeSource.BuiltIn) {
    
    1113
    -        this.activate();
    
    1114
    -      } else {
    
    1115
    -        if (this._active && bridgeSource === TorBridgeSource.Invalid) {
    
    1116
    -          lostAllBridges = true;
    
    1117
    -        }
    
    1118
    -        const hadFocus = this._area.contains(document.activeElement);
    
    1119
    -        this.deactivate();
    
    1120
    -        if (hadFocus) {
    
    1121
    -          gBridgeSettings.takeFocus();
    
    1122
    -        }
    
    1123
    -      }
    
    1124
    -    }
    
    1125
    -
    
    1126
    -    const bridgeType = this._active ? TorSettings.bridges.builtin_type : "";
    
    1127
    -
    
    1128
    -    let newType = false;
    
    1129
    -    if (bridgeType !== this._bridgeType) {
    
    1130
    -      newType = true;
    
    1131
    -
    
    1132
    -      this._bridgeType = bridgeType;
    
    1133
    -
    
    1134
    -      const bridgeStrings = this._bridgeTypeStrings[bridgeType];
    
    1135
    -      if (bridgeStrings) {
    
    1136
    -        document.l10n.setAttributes(this._nameEl, bridgeStrings.name);
    
    1137
    -        document.l10n.setAttributes(
    
    1138
    -          this._descriptionEl,
    
    1139
    -          bridgeStrings.description
    
    1140
    -        );
    
    1141
    -      } else {
    
    1142
    -        // Unknown type, or no type.
    
    1143
    -        this._nameEl.removeAttribute("data-l10n-id");
    
    1144
    -        this._nameEl.textContent = bridgeType;
    
    1145
    -        this._descriptionEl.removeAttribute("data-l10n-id");
    
    1146
    -        this._descriptionEl.textContent = "";
    
    1147
    -      }
    
    1148
    -
    
    1149
    -      this._updateConnectedState();
    
    1150
    -    }
    
    1151
    -
    
    1152
    -    // Notify the user if there was some change to the type.
    
    1153
    -    // If we are initializing, we generate no notification since there has been
    
    1154
    -    // no change in the setting.
    
    1155
    -    if (!initializing) {
    
    1156
    -      let notificationType;
    
    1157
    -      if (lostAllBridges) {
    
    1158
    -        // Just lost all bridges, and became de-active.
    
    1159
    -        notificationType = "removed-all";
    
    1160
    -      } else if (this._active && (newSource || newType)) {
    
    1161
    -        // Otherwise, only generate a notification if we are still active, with
    
    1162
    -        // a bridge type.
    
    1163
    -        // I.e. do not generate a message if the new source is not "builtin".
    
    1164
    -        notificationType = "changed";
    
    1165
    -      }
    
    1166
    -
    
    1167
    -      if (notificationType) {
    
    1168
    -        gBridgesNotification.post(notificationType);
    
    1169
    -      }
    
    1170
    -    }
    
    1171
    -  },
    
    1172
    -
    
    1173
    -  /**
    
    1174
    -   * The bridge IDs/fingerprints for the built-in bridges.
    
    1175
    -   *
    
    1176
    -   * @type {Array<string>}
    
    1177
    -   */
    
    1178
    -  _bridgeIds: [],
    
    1179
    -  /**
    
    1180
    -   * Update _bridgeIds
    
    1181
    -   */
    
    1182
    -  _updateBridgeIds() {
    
    1183
    -    this._bridgeIds = [];
    
    1184
    -    for (const bridgeLine of TorSettings.bridges.bridge_strings) {
    
    1185
    -      try {
    
    1186
    -        this._bridgeIds.push(TorParsers.parseBridgeLine(bridgeLine).id);
    
    1187
    -      } catch (e) {
    
    1188
    -        console.error(`Detected invalid bridge line: ${bridgeLine}`, e);
    
    1189
    -      }
    
    1190
    -    }
    
    1191
    -
    
    1192
    -    this._updateConnectedState();
    
    1193
    -  },
    
    1194
    -
    
    1195
    -  /**
    
    1196
    -   * The bridge ID/fingerprint of the most recently used bridge (appearing in
    
    1197
    -   * the latest Tor circuit). Roughly corresponds to the bridge we are currently
    
    1198
    -   * connected to.
    
    1199
    -   *
    
    1200
    -   * @type {string?}
    
    1201
    -   */
    
    1202
    -  _connectedBridgeId: null,
    
    1203
    -  /**
    
    1204
    -   * Update _connectedBridgeId.
    
    1205
    -   */
    
    1206
    -  async _updateConnectedBridge() {
    
    1207
    -    this._connectedBridgeId = await getConnectedBridgeId();
    
    1208
    -    this._updateConnectedState();
    
    1209
    -  },
    
    1210
    -};
    
    1211
    -
    
    1212
    -/**
    
    1213
    - * Controls the bridge pass area.
    
    1214
    - */
    
    1215
    -const gLoxStatus = {
    
    1216
    -  /**
    
    1217
    -   * The status area.
    
    1218
    -   *
    
    1219
    -   * @type {Element?}
    
    1220
    -   */
    
    1221
    -  _area: null,
    
    1222
    -  /**
    
    1223
    -   * The area for showing the next unlock and invites.
    
    1224
    -   *
    
    1225
    -   * @type {Element?}
    
    1226
    -   */
    
    1227
    -  _detailsArea: null,
    
    1228
    -  /**
    
    1229
    -   * The list items showing the next unlocks.
    
    1230
    -   *
    
    1231
    -   * @type {?{[key: string]: Element}}
    
    1232
    -   */
    
    1233
    -  _nextUnlockItems: null,
    
    1234
    -  /**
    
    1235
    -   * The day counter headings for the next unlock.
    
    1236
    -   *
    
    1237
    -   * One heading is shown during a search, the other is shown otherwise.
    
    1238
    -   *
    
    1239
    -   * @type {?Element[]}
    
    1240
    -   */
    
    1241
    -  _nextUnlockCounterEls: null,
    
    1242
    -  /**
    
    1243
    -   * Shows the number of remaining invites.
    
    1244
    -   *
    
    1245
    -   * @type {Element?}
    
    1246
    -   */
    
    1247
    -  _remainingInvitesEl: null,
    
    1248
    -  /**
    
    1249
    -   * The button to show the invites.
    
    1250
    -   *
    
    1251
    -   * @type {Element?}
    
    1252
    -   */
    
    1253
    -  _invitesButton: null,
    
    1254
    -  /**
    
    1255
    -   * The alert for new unlocks.
    
    1256
    -   *
    
    1257
    -   * @type {Element?}
    
    1258
    -   */
    
    1259
    -  _unlockAlert: null,
    
    1260
    -  /**
    
    1261
    -   * The list items showing the unlocks.
    
    1262
    -   *
    
    1263
    -   * @type {?{[key: string]: Element}}
    
    1264
    -   */
    
    1265
    -  _unlockItems: null,
    
    1266
    -  /**
    
    1267
    -   * The alert title.
    
    1268
    -   *
    
    1269
    -   * @type {Element?}
    
    1270
    -   */
    
    1271
    -  _unlockAlertTitle: null,
    
    1272
    -  /**
    
    1273
    -   * The alert invites item.
    
    1274
    -   *
    
    1275
    -   * @type {Element?}
    
    1276
    -   */
    
    1277
    -  _unlockAlertInvitesItem: null,
    
    1278
    -  /**
    
    1279
    -   * Button for the user to dismiss the alert.
    
    1280
    -   *
    
    1281
    -   * @type {Element?}
    
    1282
    -   */
    
    1283
    -  _unlockAlertButton: null,
    
    1284
    -
    
    1285
    -  /**
    
    1286
    -   * Initialize the bridge pass area.
    
    1287
    -   */
    
    1288
    -  init() {
    
    1289
    -    if (!Lox.enabled) {
    
    1290
    -      // Area should remain inactive and hidden.
    
    1291
    -      return;
    
    1292
    -    }
    
    1293
    -
    
    1294
    -    this._area = document.getElementById("tor-bridges-lox-status");
    
    1295
    -    this._detailsArea = document.getElementById("tor-bridges-lox-details");
    
    1296
    -    this._nextUnlockItems = {
    
    1297
    -      gainBridges: document.getElementById(
    
    1298
    -        "tor-bridges-lox-next-unlock-gain-bridges"
    
    1299
    -      ),
    
    1300
    -      firstInvites: document.getElementById(
    
    1301
    -        "tor-bridges-lox-next-unlock-first-invites"
    
    1302
    -      ),
    
    1303
    -      moreInvites: document.getElementById(
    
    1304
    -        "tor-bridges-lox-next-unlock-more-invites"
    
    1305
    -      ),
    
    1306
    -    };
    
    1307
    -    this._nextUnlockCounterEls = Array.from(
    
    1308
    -      document.querySelectorAll(".tor-bridges-lox-next-unlock-counter")
    
    1309
    -    );
    
    1310
    -    this._remainingInvitesEl = document.getElementById(
    
    1311
    -      "tor-bridges-lox-remaining-invites"
    
    1312
    -    );
    
    1313
    -    this._invitesButton = document.getElementById(
    
    1314
    -      "tor-bridges-lox-show-invites-button"
    
    1315
    -    );
    
    1316
    -    this._unlockAlert = document.getElementById("tor-bridges-lox-unlock-alert");
    
    1317
    -    this._unlockItems = {
    
    1318
    -      gainBridges: document.getElementById(
    
    1319
    -        "tor-bridges-lox-unlock-alert-gain-bridges"
    
    1320
    -      ),
    
    1321
    -      newBridges: document.getElementById(
    
    1322
    -        "tor-bridges-lox-unlock-alert-new-bridges"
    
    1323
    -      ),
    
    1324
    -      invites: document.getElementById("tor-bridges-lox-unlock-alert-invites"),
    
    1325
    -    };
    
    1326
    -    this._unlockAlertTitle = document.getElementById(
    
    1327
    -      "tor-bridge-unlock-alert-title"
    
    1328
    -    );
    
    1329
    -    this._unlockAlertInviteItem = document.getElementById(
    
    1330
    -      "tor-bridges-lox-unlock-alert-invites"
    
    1331
    -    );
    
    1332
    -    this._unlockAlertButton = document.getElementById(
    
    1333
    -      "tor-bridges-lox-unlock-alert-button"
    
    1334
    -    );
    
    1335
    -
    
    1336
    -    this._invitesButton.addEventListener("click", () => {
    
    1337
    -      gSubDialog.open(
    
    1338
    -        "chrome://browser/content/torpreferences/loxInviteDialog.xhtml",
    
    1339
    -        { features: "resizable=yes" }
    
    1340
    -      );
    
    1341
    -    });
    
    1342
    -    this._unlockAlertButton.addEventListener("click", () => {
    
    1343
    -      Lox.clearEventData(this._loxId);
    
    1344
    -    });
    
    1345
    -
    
    1346
    -    Services.obs.addObserver(this, TorSettingsTopics.SettingsChanged);
    
    1347
    -    Services.obs.addObserver(this, LoxTopics.UpdateActiveLoxId);
    
    1348
    -    Services.obs.addObserver(this, LoxTopics.UpdateEvents);
    
    1349
    -    Services.obs.addObserver(this, LoxTopics.UpdateNextUnlock);
    
    1350
    -    Services.obs.addObserver(this, LoxTopics.UpdateRemainingInvites);
    
    1351
    -    Services.obs.addObserver(this, LoxTopics.NewInvite);
    
    1352
    -
    
    1353
    -    // NOTE: Before initializedPromise completes, this area is hidden.
    
    1354
    -    TorSettings.initializedPromise.then(() => {
    
    1355
    -      this._updateLoxId();
    
    1356
    -    });
    
    1357
    -  },
    
    1358
    -
    
    1359
    -  /**
    
    1360
    -   * Uninitialize the built-in bridges area.
    
    1361
    -   */
    
    1362
    -  uninit() {
    
    1363
    -    if (!Lox.enabled) {
    
    1364
    -      return;
    
    1365
    -    }
    
    1366
    -
    
    1367
    -    Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged);
    
    1368
    -    Services.obs.removeObserver(this, LoxTopics.UpdateActiveLoxId);
    
    1369
    -    Services.obs.removeObserver(this, LoxTopics.UpdateEvents);
    
    1370
    -    Services.obs.removeObserver(this, LoxTopics.UpdateNextUnlock);
    
    1371
    -    Services.obs.removeObserver(this, LoxTopics.UpdateRemainingInvites);
    
    1372
    -    Services.obs.removeObserver(this, LoxTopics.NewInvite);
    
    1373
    -  },
    
    1374
    -
    
    1375
    -  observe(subject, topic) {
    
    1376
    -    switch (topic) {
    
    1377
    -      case TorSettingsTopics.SettingsChanged: {
    
    1378
    -        const { changes } = subject.wrappedJSObject;
    
    1379
    -        if (changes.includes("bridges.source")) {
    
    1380
    -          this._updateLoxId();
    
    1381
    -        }
    
    1382
    -        // NOTE: We do not call _updateLoxId when "bridges.lox_id" is in the
    
    1383
    -        // changes. Instead we wait until LoxTopics.UpdateActiveLoxId to ensure
    
    1384
    -        // that the Lox module has responded to the change in ID strictly
    
    1385
    -        // *before* we do. In particular, we want to make sure the invites and
    
    1386
    -        // event data has been cleared.
    
    1387
    -        break;
    
    1388
    -      }
    
    1389
    -      case LoxTopics.UpdateActiveLoxId:
    
    1390
    -        this._updateLoxId();
    
    1391
    -        break;
    
    1392
    -      case LoxTopics.UpdateNextUnlock:
    
    1393
    -        this._updateNextUnlock();
    
    1394
    -        break;
    
    1395
    -      case LoxTopics.UpdateEvents:
    
    1396
    -        this._updatePendingEvents();
    
    1397
    -        break;
    
    1398
    -      case LoxTopics.UpdateRemainingInvites:
    
    1399
    -        this._updateRemainingInvites();
    
    1400
    -        break;
    
    1401
    -      case LoxTopics.NewInvite:
    
    1402
    -        this._updateHaveExistingInvites();
    
    1403
    -        break;
    
    1404
    -    }
    
    1405
    -  },
    
    1406
    -
    
    1407
    -  /**
    
    1408
    -   * The Lox id currently shown. Empty if deactivated, and null if
    
    1409
    -   * uninitialized.
    
    1410
    -   *
    
    1411
    -   * @type {string?}
    
    1412
    -   */
    
    1413
    -  _loxId: null,
    
    1414
    -
    
    1415
    -  /**
    
    1416
    -   * Update the shown bridge pass.
    
    1417
    -   */
    
    1418
    -  async _updateLoxId() {
    
    1419
    -    let loxId =
    
    1420
    -      TorSettings.bridges.source === TorBridgeSource.Lox ? Lox.activeLoxId : "";
    
    1421
    -    if (loxId === this._loxId) {
    
    1422
    -      return;
    
    1423
    -    }
    
    1424
    -    this._loxId = loxId;
    
    1425
    -    this._area.hidden = !loxId;
    
    1426
    -    // We unset _nextUnlock to ensure the areas no longer use the old value for
    
    1427
    -    // the new loxId.
    
    1428
    -    this._updateNextUnlock(true);
    
    1429
    -    this._updateRemainingInvites();
    
    1430
    -    this._updateHaveExistingInvites();
    
    1431
    -    this._updatePendingEvents();
    
    1432
    -  },
    
    1433
    -
    
    1434
    -  /**
    
    1435
    -   * The remaining invites shown, or null if uninitialized or no loxId.
    
    1436
    -   *
    
    1437
    -   * @type {integer?}
    
    1438
    -   */
    
    1439
    -  _remainingInvites: null,
    
    1440
    -  /**
    
    1441
    -   * Update the shown value.
    
    1442
    -   */
    
    1443
    -  _updateRemainingInvites() {
    
    1444
    -    const numInvites = this._loxId
    
    1445
    -      ? Lox.getRemainingInviteCount(this._loxId)
    
    1446
    -      : null;
    
    1447
    -    if (numInvites === this._remainingInvites) {
    
    1448
    -      return;
    
    1449
    -    }
    
    1450
    -    this._remainingInvites = numInvites;
    
    1451
    -    this._updateUnlockArea();
    
    1452
    -    this._updateInvitesArea();
    
    1453
    -  },
    
    1454
    -  /**
    
    1455
    -   * Whether we have existing invites, or null if uninitialized or no loxId.
    
    1456
    -   *
    
    1457
    -   * @type {boolean?}
    
    1458
    -   */
    
    1459
    -  _haveExistingInvites: null,
    
    1460
    -  /**
    
    1461
    -   * Update the shown value.
    
    1462
    -   */
    
    1463
    -  _updateHaveExistingInvites() {
    
    1464
    -    const haveInvites = this._loxId ? !!Lox.getInvites().length : null;
    
    1465
    -    if (haveInvites === this._haveExistingInvites) {
    
    1466
    -      return;
    
    1467
    -    }
    
    1468
    -    this._haveExistingInvites = haveInvites;
    
    1469
    -    this._updateInvitesArea();
    
    1470
    -  },
    
    1471
    -  /**
    
    1472
    -   * Details about the next unlock, or null if uninitialized or no loxId.
    
    1473
    -   *
    
    1474
    -   * @type {UnlockData?}
    
    1475
    -   */
    
    1476
    -  _nextUnlock: null,
    
    1477
    -  /**
    
    1478
    -   * Tracker id to ensure that the results from later calls to _updateNextUnlock
    
    1479
    -   * take priority over earlier calls.
    
    1480
    -   *
    
    1481
    -   * @type {integer}
    
    1482
    -   */
    
    1483
    -  _nextUnlockCallId: 0,
    
    1484
    -  /**
    
    1485
    -   * Update the shown value asynchronously.
    
    1486
    -   *
    
    1487
    -   * @param {boolean} [unset=false] - Whether to set the _nextUnlock value to
    
    1488
    -   *   null before waiting for the new value. I.e. ensure that the current value
    
    1489
    -   *   will not be used.
    
    1490
    -   */
    
    1491
    -  async _updateNextUnlock(unset = false) {
    
    1492
    -    // NOTE: We do not expect the integer to exceed the maximum integer.
    
    1493
    -    this._nextUnlockCallId++;
    
    1494
    -    const callId = this._nextUnlockCallId;
    
    1495
    -    if (unset) {
    
    1496
    -      this._nextUnlock = null;
    
    1497
    -    }
    
    1498
    -    const nextUnlock = this._loxId
    
    1499
    -      ? await Lox.getNextUnlock(this._loxId)
    
    1500
    -      : null;
    
    1501
    -    if (callId !== this._nextUnlockCallId) {
    
    1502
    -      // Replaced by another update.
    
    1503
    -      // E.g. if the _loxId changed. Or if getNextUnlock triggered
    
    1504
    -      // LoxTopics.UpdateNextUnlock.
    
    1505
    -      return;
    
    1506
    -    }
    
    1507
    -    // Should be safe to trigger the update, even when the value hasn't changed.
    
    1508
    -    this._nextUnlock = nextUnlock;
    
    1509
    -    this._updateUnlockArea();
    
    1510
    -  },
    
    1511
    -  /**
    
    1512
    -   * The list of events the user has not yet cleared, or null if uninitialized
    
    1513
    -   * or no loxId.
    
    1514
    -   *
    
    1515
    -   * @type {EventData[]?}
    
    1516
    -   */
    
    1517
    -  _pendingEvents: null,
    
    1518
    -  /**
    
    1519
    -   * Update the shown value.
    
    1520
    -   */
    
    1521
    -  _updatePendingEvents() {
    
    1522
    -    // Should be safe to trigger the update, even when the value hasn't changed.
    
    1523
    -    this._pendingEvents = this._loxId ? Lox.getEventData(this._loxId) : null;
    
    1524
    -    this._updateUnlockArea();
    
    1525
    -  },
    
    1526
    -
    
    1527
    -  /**
    
    1528
    -   * Update the display of the current or next unlock.
    
    1529
    -   */
    
    1530
    -  _updateUnlockArea() {
    
    1531
    -    if (
    
    1532
    -      !this._loxId ||
    
    1533
    -      this._pendingEvents === null ||
    
    1534
    -      this._remainingInvites === null ||
    
    1535
    -      this._nextUnlock === null
    
    1536
    -    ) {
    
    1537
    -      // Uninitialized or no Lox source.
    
    1538
    -      // NOTE: This area may already be hidden by the change in Lox source,
    
    1539
    -      // but we clean up for the next non-empty id.
    
    1540
    -      this._unlockAlert.hidden = true;
    
    1541
    -      this._detailsArea.hidden = true;
    
    1542
    -      return;
    
    1543
    -    }
    
    1544
    -
    
    1545
    -    // Grab focus state before changing visibility.
    
    1546
    -    const alertHadFocus = this._unlockAlert.contains(document.activeElement);
    
    1547
    -    const detailsHadFocus = this._detailsArea.contains(document.activeElement);
    
    1548
    -
    
    1549
    -    const pendingEvents = this._pendingEvents;
    
    1550
    -    const showAlert = !!pendingEvents.length;
    
    1551
    -    this._unlockAlert.hidden = !showAlert;
    
    1552
    -    this._detailsArea.hidden = showAlert;
    
    1553
    -
    
    1554
    -    if (showAlert) {
    
    1555
    -      // At level 0 and level 1, we do not have any invites.
    
    1556
    -      // If the user starts and ends on level 0 or 1, then overall they would
    
    1557
    -      // have had no change in their invites. So we do not want to show their
    
    1558
    -      // latest updates.
    
    1559
    -      // NOTE: If the user starts at level > 1 and ends with level 1 (levelling
    
    1560
    -      // down to level 0 should not be possible), then we *do* want to show the
    
    1561
    -      // user that they now have "0" invites.
    
    1562
    -      // NOTE: pendingEvents are time-ordered, with the most recent event
    
    1563
    -      // *last*.
    
    1564
    -      const firstEvent = pendingEvents[0];
    
    1565
    -      // NOTE: We cannot get a blockage event when the user starts at level 1 or
    
    1566
    -      // 0.
    
    1567
    -      const startingAtLowLevel =
    
    1568
    -        firstEvent.type === "levelup" && firstEvent.newLevel <= 2;
    
    1569
    -      const lastEvent = pendingEvents[pendingEvents.length - 1];
    
    1570
    -      const endingAtLowLevel = lastEvent.newLevel <= 1;
    
    1571
    -
    
    1572
    -      const showInvites = !(startingAtLowLevel && endingAtLowLevel);
    
    1573
    -
    
    1574
    -      let blockage = false;
    
    1575
    -      let levelUp = false;
    
    1576
    -      let bridgeGain = false;
    
    1577
    -      // Go through events, in the order that they occurred.
    
    1578
    -      for (const loxEvent of pendingEvents) {
    
    1579
    -        if (loxEvent.type === "levelup") {
    
    1580
    -          levelUp = true;
    
    1581
    -          if (loxEvent.newLevel === 1) {
    
    1582
    -            // Gain 2 bridges from level 0 to 1.
    
    1583
    -            bridgeGain = true;
    
    1584
    -          }
    
    1585
    -        } else {
    
    1586
    -          blockage = true;
    
    1587
    -        }
    
    1588
    -      }
    
    1589
    -
    
    1590
    -      let alertTitleId;
    
    1591
    -      if (levelUp && !blockage) {
    
    1592
    -        alertTitleId = "tor-bridges-lox-upgrade";
    
    1593
    -      } else {
    
    1594
    -        // Show as blocked bridges replaced.
    
    1595
    -        // Even if we have a mixture of level ups as well.
    
    1596
    -        alertTitleId = "tor-bridges-lox-blocked";
    
    1597
    -      }
    
    1598
    -      document.l10n.setAttributes(this._unlockAlertTitle, alertTitleId);
    
    1599
    -      document.l10n.setAttributes(
    
    1600
    -        this._unlockAlertInviteItem,
    
    1601
    -        "tor-bridges-lox-new-invites",
    
    1602
    -        { numInvites: this._remainingInvites }
    
    1603
    -      );
    
    1604
    -      this._unlockAlert.classList.toggle(
    
    1605
    -        "lox-unlock-upgrade",
    
    1606
    -        levelUp && !blockage
    
    1607
    -      );
    
    1608
    -      this._unlockItems.gainBridges.hidden = !bridgeGain;
    
    1609
    -      this._unlockItems.newBridges.hidden = !blockage;
    
    1610
    -      this._unlockItems.invites.hidden = !showInvites;
    
    1611
    -    } else {
    
    1612
    -      // Show next unlock.
    
    1613
    -      // Number of days until the next unlock, rounded up.
    
    1614
    -      const numDays = Math.max(
    
    1615
    -        1,
    
    1616
    -        Math.ceil(
    
    1617
    -          (new Date(this._nextUnlock.date).getTime() - Date.now()) /
    
    1618
    -            (24 * 60 * 60 * 1000)
    
    1619
    -        )
    
    1620
    -      );
    
    1621
    -      for (const counterEl of this._nextUnlockCounterEls) {
    
    1622
    -        document.l10n.setAttributes(
    
    1623
    -          counterEl,
    
    1624
    -          "tor-bridges-lox-days-until-unlock",
    
    1625
    -          { numDays }
    
    1626
    -        );
    
    1627
    -      }
    
    1628
    -
    
    1629
    -      // Gain 2 bridges from level 0 to 1. After that gain invites.
    
    1630
    -      this._nextUnlockItems.gainBridges.hidden =
    
    1631
    -        this._nextUnlock.nextLevel !== 1;
    
    1632
    -      this._nextUnlockItems.firstInvites.hidden =
    
    1633
    -        this._nextUnlock.nextLevel !== 2;
    
    1634
    -      this._nextUnlockItems.moreInvites.hidden =
    
    1635
    -        this._nextUnlock.nextLevel <= 2;
    
    1636
    -    }
    
    1637
    -
    
    1638
    -    if (alertHadFocus && !showAlert) {
    
    1639
    -      // Alert has become hidden, move focus back up to the now revealed details
    
    1640
    -      // area.
    
    1641
    -      // NOTE: We have two headings: one shown during a search and one shown
    
    1642
    -      // otherwise. We focus the heading that is currently visible.
    
    1643
    -      // See tor-browser#43320.
    
    1644
    -      // TODO: It might be better if we could use the # named anchor to
    
    1645
    -      // re-orient the screen reader position instead of using tabIndex=-1, but
    
    1646
    -      // about:preferences currently uses the anchor for showing categories
    
    1647
    -      // only. See bugzilla bug 1799153.
    
    1648
    -      if (
    
    1649
    -        this._nextUnlockCounterEls[0].checkVisibility({
    
    1650
    -          visibilityProperty: true,
    
    1651
    -        })
    
    1652
    -      ) {
    
    1653
    -        this._nextUnlockCounterEls[0].focus();
    
    1654
    -      } else {
    
    1655
    -        this._nextUnlockCounterEls[1].focus();
    
    1656
    -      }
    
    1657
    -    } else if (detailsHadFocus && showAlert) {
    
    1658
    -      this._unlockAlertButton.focus();
    
    1659
    -    }
    
    1660
    -  },
    
    1661
    -
    
    1662
    -  /**
    
    1663
    -   * Update the invites area.
    
    1664
    -   */
    
    1665
    -  _updateInvitesArea() {
    
    1666
    -    let hasInvites;
    
    1667
    -    if (
    
    1668
    -      !this._loxId ||
    
    1669
    -      this._remainingInvites === null ||
    
    1670
    -      this._haveExistingInvites === null
    
    1671
    -    ) {
    
    1672
    -      // Not initialized yet.
    
    1673
    -      hasInvites = false;
    
    1674
    -    } else {
    
    1675
    -      hasInvites = this._haveExistingInvites || !!this._remainingInvites;
    
    1676
    -    }
    
    1677
    -
    
    1678
    -    if (
    
    1679
    -      !hasInvites &&
    
    1680
    -      (this._remainingInvitesEl.contains(document.activeElement) ||
    
    1681
    -        this._invitesButton.contains(document.activeElement))
    
    1682
    -    ) {
    
    1683
    -      // About to loose focus.
    
    1684
    -      // Unexpected for the lox level to loose all invites.
    
    1685
    -      // Move to the top of the details area, which should be visible if we
    
    1686
    -      // just had focus.
    
    1687
    -      this._nextUnlockCounterEl.focus();
    
    1688
    -    }
    
    1689
    -    // Hide the invite elements if we have no historic invites or a way of
    
    1690
    -    // creating new ones.
    
    1691
    -    this._remainingInvitesEl.hidden = !hasInvites;
    
    1692
    -    this._invitesButton.hidden = !hasInvites;
    
    1693
    -
    
    1694
    -    if (hasInvites) {
    
    1695
    -      document.l10n.setAttributes(
    
    1696
    -        this._remainingInvitesEl,
    
    1697
    -        "tor-bridges-lox-remaining-invites",
    
    1698
    -        { numInvites: this._remainingInvites }
    
    1699
    -      );
    
    1700
    -    }
    
    1701
    -  },
    
    1702
    -};
    
    1703
    -
    
    1704
    -/**
    
    1705
    - * Controls the bridge settings.
    
    1706
    - */
    
    1707
    -const gBridgeSettings = {
    
    1708
    -  /**
    
    1709
    -   * The preferences <groupbox> for bridges
    
    1710
    -   *
    
    1711
    -   * @type {Element?}
    
    1712
    -   */
    
    1713
    -  _groupEl: null,
    
    1714
    -  /**
    
    1715
    -   * The button for controlling whether bridges are enabled.
    
    1716
    -   *
    
    1717
    -   * @type {Element?}
    
    1718
    -   */
    
    1719
    -  _toggleButton: null,
    
    1720
    -  /**
    
    1721
    -   * The area for showing current bridges.
    
    1722
    -   *
    
    1723
    -   * @type {Element?}
    
    1724
    -   */
    
    1725
    -  _bridgesEl: null,
    
    1726
    -  /**
    
    1727
    -   * The area for sharing bridge addresses.
    
    1728
    -   *
    
    1729
    -   * @type {Element?}
    
    1730
    -   */
    
    1731
    -  _shareEl: null,
    
    1732
    -  /**
    
    1733
    -   * The two headings for the bridge settings.
    
    1734
    -   *
    
    1735
    -   * One heading is shown during a search, the other is shown otherwise.
    
    1736
    -   *
    
    1737
    -   * @type {?Element[]}
    
    1738
    -   */
    
    1739
    -  _bridgesSettingsHeadings: null,
    
    1740
    -  /**
    
    1741
    -   * The two headings for the current bridges, at the start of the area.
    
    1742
    -   *
    
    1743
    -   * One heading is shown during a search, the other is shown otherwise.
    
    1744
    -   *
    
    1745
    -   * @type {Element?}
    
    1746
    -   */
    
    1747
    -  _currentBridgesHeadings: null,
    
    1748
    -  /**
    
    1749
    -   * The area for showing no bridges.
    
    1750
    -   *
    
    1751
    -   * @type {Element?}
    
    1752
    -   */
    
    1753
    -  _noBridgesEl: null,
    
    1754
    -  /**
    
    1755
    -   * The heading elements for changing bridges.
    
    1756
    -   *
    
    1757
    -   * One heading is shown during a search, the other is shown otherwise.
    
    1758
    -   *
    
    1759
    -   * @type {?Element[]}
    
    1760
    -   */
    
    1761
    -  _changeHeadingEls: null,
    
    1762
    -  /**
    
    1763
    -   * The button for user to provide a bridge address or share code.
    
    1764
    -   *
    
    1765
    -   * @type {Element?}
    
    1766
    -   */
    
    1767
    -  _userProvideButton: null,
    
    1768
    -  /**
    
    1769
    -   * A map from the bridge source to its corresponding label.
    
    1770
    -   *
    
    1771
    -   * @type {?Map<number, Element>}
    
    1772
    -   */
    
    1773
    -  _sourceLabels: null,
    
    1774
    -
    
    1775
    -  /**
    
    1776
    -   * Initialize the bridge settings.
    
    1777
    -   */
    
    1778
    -  init() {
    
    1779
    -    gBridgesNotification.init();
    
    1780
    -
    
    1781
    -    this._bridgesSettingsHeadings = Array.from(
    
    1782
    -      document.querySelectorAll(".tor-bridges-subcategory-heading")
    
    1783
    -    );
    
    1784
    -    this._currentBridgesHeadings = Array.from(
    
    1785
    -      document.querySelectorAll(".tor-bridges-current-heading")
    
    1786
    -    );
    
    1787
    -    this._bridgesEl = document.getElementById("tor-bridges-current");
    
    1788
    -    this._noBridgesEl = document.getElementById("tor-bridges-none");
    
    1789
    -    this._groupEl = document.getElementById("torPreferences-bridges-group");
    
    1790
    -
    
    1791
    -    this._sourceLabels = new Map([
    
    1792
    -      [
    
    1793
    -        TorBridgeSource.BuiltIn,
    
    1794
    -        document.getElementById("tor-bridges-built-in-label"),
    
    1795
    -      ],
    
    1796
    -      [
    
    1797
    -        TorBridgeSource.UserProvided,
    
    1798
    -        document.getElementById("tor-bridges-user-label"),
    
    1799
    -      ],
    
    1800
    -      [
    
    1801
    -        TorBridgeSource.BridgeDB,
    
    1802
    -        document.getElementById("tor-bridges-requested-label"),
    
    1803
    -      ],
    
    1804
    -      [TorBridgeSource.Lox, document.getElementById("tor-bridges-lox-label")],
    
    1805
    -    ]);
    
    1806
    -    this._shareEl = document.getElementById("tor-bridges-share");
    
    1807
    -
    
    1808
    -    this._toggleButton = document.getElementById("tor-bridges-enabled-toggle");
    
    1809
    -    // Initially disabled whilst TorSettings may not be initialized.
    
    1810
    -    this._toggleButton.disabled = true;
    
    1811
    -
    
    1812
    -    this._toggleButton.addEventListener("toggle", () => {
    
    1813
    -      if (!this._haveBridges) {
    
    1814
    -        return;
    
    1815
    -      }
    
    1816
    -      TorSettings.changeSettings({
    
    1817
    -        bridges: { enabled: this._toggleButton.pressed },
    
    1818
    -      });
    
    1819
    -    });
    
    1820
    -
    
    1821
    -    this._changeHeadingEls = Array.from(
    
    1822
    -      document.querySelectorAll(".tor-bridges-change-heading")
    
    1823
    -    );
    
    1824
    -    this._userProvideButton = document.getElementById(
    
    1825
    -      "tor-bridges-open-user-provide-dialog-button"
    
    1826
    -    );
    
    1827
    -
    
    1828
    -    document.l10n.setAttributes(
    
    1829
    -      document.getElementById("tor-bridges-user-provide-description"),
    
    1830
    -      // TODO: Set a different string if we have Lox enabled.
    
    1831
    -      "tor-bridges-add-addresses-description"
    
    1832
    -    );
    
    1833
    -
    
    1834
    -    // TODO: Change to GetLoxBridges if Lox enabled, and the account is set up.
    
    1835
    -    const telegramUserName = "GetBridgesBot";
    
    1836
    -    const telegramInstruction = document.getElementById(
    
    1837
    -      "tor-bridges-provider-instruction-telegram"
    
    1838
    -    );
    
    1839
    -    telegramInstruction.querySelector("a").href =
    
    1840
    -      `https://t.me/${telegramUserName}`;
    
    1841
    -    document.l10n.setAttributes(
    
    1842
    -      telegramInstruction,
    
    1843
    -      "tor-bridges-provider-telegram-instruction",
    
    1844
    -      { telegramUserName }
    
    1845
    -    );
    
    1846
    -
    
    1847
    -    document
    
    1848
    -      .getElementById("tor-bridges-open-built-in-dialog-button")
    
    1849
    -      .addEventListener("click", () => {
    
    1850
    -        this._openBuiltinDialog();
    
    1851
    -      });
    
    1852
    -    this._userProvideButton.addEventListener("click", () => {
    
    1853
    -      this._openUserProvideDialog(this._haveBridges ? "replace" : "add");
    
    1854
    -    });
    
    1855
    -    document
    
    1856
    -      .getElementById("tor-bridges-open-request-dialog-button")
    
    1857
    -      .addEventListener("click", () => {
    
    1858
    -        this._openRequestDialog();
    
    1859
    -      });
    
    1860
    -
    
    1861
    -    Services.obs.addObserver(this, TorSettingsTopics.SettingsChanged);
    
    1862
    -
    
    1863
    -    gBridgeGrid.init();
    
    1864
    -    gBuiltinBridgesArea.init();
    
    1865
    -    gLoxStatus.init();
    
    1866
    -
    
    1867
    -    this._initBridgesMenu();
    
    1868
    -    this._initShareArea();
    
    1869
    -
    
    1870
    -    // NOTE: Before initializedPromise completes, the current bridges sections
    
    1871
    -    // should be hidden.
    
    1872
    -    // And gBridgeGrid and gBuiltinBridgesArea are not active.
    
    1873
    -    TorSettings.initializedPromise.then(() => {
    
    1874
    -      this._updateEnabled();
    
    1875
    -      this._updateBridgeStrings();
    
    1876
    -      this._updateSource();
    
    1877
    -    });
    
    1878
    -  },
    
    1879
    -
    
    1880
    -  /**
    
    1881
    -   * Un-initialize the bridge settings.
    
    1882
    -   */
    
    1883
    -  uninit() {
    
    1884
    -    gBridgeGrid.uninit();
    
    1885
    -    gBuiltinBridgesArea.uninit();
    
    1886
    -    gLoxStatus.uninit();
    
    1887
    -
    
    1888
    -    Services.obs.removeObserver(this, TorSettingsTopics.SettingsChanged);
    
    1889
    -  },
    
    1890
    -
    
    1891
    -  observe(subject, topic) {
    
    1892
    -    switch (topic) {
    
    1893
    -      case TorSettingsTopics.SettingsChanged: {
    
    1894
    -        const { changes } = subject.wrappedJSObject;
    
    1895
    -        if (changes.includes("bridges.enabled")) {
    
    1896
    -          this._updateEnabled();
    
    1897
    -        }
    
    1898
    -        if (changes.includes("bridges.source")) {
    
    1899
    -          this._updateSource();
    
    1900
    -        }
    
    1901
    -        if (changes.includes("bridges.bridge_strings")) {
    
    1902
    -          this._updateBridgeStrings();
    
    1903
    -        }
    
    1904
    -        break;
    
    1905
    -      }
    
    1906
    -    }
    
    1907
    -  },
    
    1908
    -
    
    1909
    -  /**
    
    1910
    -   * Update whether the bridges should be shown as enabled.
    
    1911
    -   */
    
    1912
    -  _updateEnabled() {
    
    1913
    -    // Changing the pressed property on moz-toggle should not trigger its
    
    1914
    -    // "toggle" event.
    
    1915
    -    this._toggleButton.pressed = TorSettings.bridges.enabled;
    
    1916
    -  },
    
    1917
    -
    
    1918
    -  /**
    
    1919
    -   * The shown bridge source.
    
    1920
    -   *
    
    1921
    -   * Initially null to indicate that it is unset for the first call to
    
    1922
    -   * _updateSource.
    
    1923
    -   *
    
    1924
    -   * @type {integer?}
    
    1925
    -   */
    
    1926
    -  _bridgeSource: null,
    
    1927
    -  /**
    
    1928
    -   * Whether the user is encouraged to share their bridge addresses.
    
    1929
    -   *
    
    1930
    -   * @type {boolean}
    
    1931
    -   */
    
    1932
    -  _canShare: false,
    
    1933
    -
    
    1934
    -  /**
    
    1935
    -   * Update _bridgeSource.
    
    1936
    -   */
    
    1937
    -  _updateSource() {
    
    1938
    -    // NOTE: This should only ever be called after TorSettings is already
    
    1939
    -    // initialized.
    
    1940
    -    const bridgeSource = TorSettings.bridges.source;
    
    1941
    -    if (bridgeSource === this._bridgeSource) {
    
    1942
    -      // Avoid re-activating an area if the source has not changed.
    
    1943
    -      return;
    
    1944
    -    }
    
    1945
    -
    
    1946
    -    this._bridgeSource = bridgeSource;
    
    1947
    -
    
    1948
    -    // Before hiding elements, we determine whether our region contained the
    
    1949
    -    // user focus.
    
    1950
    -    const hadFocus =
    
    1951
    -      this._bridgesEl.contains(document.activeElement) ||
    
    1952
    -      this._noBridgesEl.contains(document.activeElement);
    
    1953
    -
    
    1954
    -    for (const [source, labelEl] of this._sourceLabels.entries()) {
    
    1955
    -      labelEl.hidden = source !== bridgeSource;
    
    1956
    -    }
    
    1957
    -
    
    1958
    -    this._canShare =
    
    1959
    -      bridgeSource === TorBridgeSource.UserProvided ||
    
    1960
    -      bridgeSource === TorBridgeSource.BridgeDB;
    
    1961
    -
    
    1962
    -    this._shareEl.hidden = !this._canShare;
    
    1963
    -
    
    1964
    -    // Force the menu to close whenever the source changes.
    
    1965
    -    // NOTE: If the menu had focus then hadFocus will be true, and focus will be
    
    1966
    -    // re-assigned.
    
    1967
    -    this._forceCloseBridgesMenu();
    
    1968
    -
    
    1969
    -    // Update whether we have bridges.
    
    1970
    -    this._updateHaveBridges();
    
    1971
    -
    
    1972
    -    if (hadFocus) {
    
    1973
    -      // Always reset the focus to the start of the area whenever the source
    
    1974
    -      // changes.
    
    1975
    -      // NOTE: gBuiltinBridges._updateBridgeType and gBridgeGrid._updateRows
    
    1976
    -      // may have already called takeFocus in response to them being
    
    1977
    -      // de-activated. The re-call should be safe.
    
    1978
    -      this.takeFocus();
    
    1979
    -    }
    
    1980
    -  },
    
    1981
    -
    
    1982
    -  /**
    
    1983
    -   * Whether we have bridges or not, or null if it is unknown.
    
    1984
    -   *
    
    1985
    -   * @type {boolean?}
    
    1986
    -   */
    
    1987
    -  _haveBridges: null,
    
    1988
    -
    
    1989
    -  /**
    
    1990
    -   * Update the _haveBridges value.
    
    1991
    -   */
    
    1992
    -  _updateHaveBridges() {
    
    1993
    -    // NOTE: We use the TorSettings.bridges.source value, rather than
    
    1994
    -    // this._bridgeSource because _updateHaveBridges can be called just before
    
    1995
    -    // _updateSource (via takeFocus).
    
    1996
    -    const haveBridges = TorSettings.bridges.source !== TorBridgeSource.Invalid;
    
    1997
    -
    
    1998
    -    if (haveBridges === this._haveBridges) {
    
    1999
    -      return;
    
    2000
    -    }
    
    2001
    -
    
    2002
    -    this._haveBridges = haveBridges;
    
    2003
    -
    
    2004
    -    this._toggleButton.disabled = !haveBridges;
    
    2005
    -    // Add classes to show or hide the "no bridges" and "Your bridges" sections.
    
    2006
    -    // NOTE: Before haveBridges is set, neither class is added, so both sections
    
    2007
    -    // and hidden.
    
    2008
    -    this._groupEl.classList.add("bridges-initialized");
    
    2009
    -    this._bridgesEl.hidden = !haveBridges;
    
    2010
    -    this._noBridgesEl.hidden = haveBridges;
    
    2011
    -
    
    2012
    -    for (const headingEl of this._changeHeadingEls) {
    
    2013
    -      document.l10n.setAttributes(
    
    2014
    -        headingEl,
    
    2015
    -        haveBridges
    
    2016
    -          ? "tor-bridges-replace-bridges-heading"
    
    2017
    -          : "tor-bridges-add-bridges-heading"
    
    2018
    -      );
    
    2019
    -    }
    
    2020
    -    document.l10n.setAttributes(
    
    2021
    -      this._userProvideButton,
    
    2022
    -      haveBridges ? "tor-bridges-replace-button" : "tor-bridges-add-new-button"
    
    2023
    -    );
    
    2024
    -  },
    
    2025
    -
    
    2026
    -  /**
    
    2027
    -   * Force the focus to move to the bridge area.
    
    2028
    -   */
    
    2029
    -  takeFocus() {
    
    2030
    -    if (this._haveBridges === null) {
    
    2031
    -      // The bridges area has not been initialized yet, which means that
    
    2032
    -      // TorSettings may not be initialized.
    
    2033
    -      // Unexpected to receive a call before then, so just return early.
    
    2034
    -      return;
    
    2035
    -    }
    
    2036
    -
    
    2037
    -    // Make sure we have the latest value for _haveBridges.
    
    2038
    -    // We also ensure that the _currentBridgesHeadings element is visible before
    
    2039
    -    // we focus it.
    
    2040
    -    this._updateHaveBridges();
    
    2041
    -
    
    2042
    -    // Move focus to the start of the relevant section, which is a heading.
    
    2043
    -    // They have tabindex="-1" so should be focusable, even though they are not
    
    2044
    -    // part of the usual tab navigation.
    
    2045
    -    // NOTE: We have two headings: one shown during a search and one shown
    
    2046
    -    // otherwise. We focus the heading that is currently visible.
    
    2047
    -    // See tor-browser#43320.
    
    2048
    -    // TODO: It might be better if we could use the # named anchor to
    
    2049
    -    // re-orient the screen reader position instead of using tabIndex=-1, but
    
    2050
    -    // about:preferences currently uses the anchor for showing categories
    
    2051
    -    // only. See bugzilla bug 1799153.
    
    2052
    -    const focusHeadings = this._haveBridges
    
    2053
    -      ? this._currentBridgesHeadings // The heading above the new bridges.
    
    2054
    -      : this._bridgesSettingsHeadings; // The top of the bridge settings.
    
    2055
    -    if (focusHeadings[0].checkVisibility({ visibilityProperty: true })) {
    
    2056
    -      focusHeadings[0].focus();
    
    2057
    -    } else {
    
    2058
    -      focusHeadings[1].focus();
    
    2059
    -    }
    
    2060
    -  },
    
    2061
    -
    
    2062
    -  /**
    
    2063
    -   * The bridge strings in a copy-able form.
    
    2064
    -   *
    
    2065
    -   * @type {string}
    
    2066
    -   */
    
    2067
    -  _bridgeStrings: "",
    
    2068
    -  /**
    
    2069
    -   * Whether the bridge strings should be shown as a QR code.
    
    2070
    -   *
    
    2071
    -   * @type {boolean}
    
    2072
    -   */
    
    2073
    -  _canQRBridges: false,
    
    2074
    -
    
    2075
    -  /**
    
    2076
    -   * Update the stored bridge strings.
    
    2077
    -   */
    
    2078
    -  _updateBridgeStrings() {
    
    2079
    -    const bridges = TorSettings.bridges.bridge_strings;
    
    2080
    -
    
    2081
    -    this._bridgeStrings = bridges.join("\n");
    
    2082
    -    // TODO: Determine what logic we want.
    
    2083
    -    this._canQRBridges = bridges.length <= 3;
    
    2084
    -
    
    2085
    -    this._qrButton.disabled = !this._canQRBridges;
    
    2086
    -  },
    
    2087
    -
    
    2088
    -  /**
    
    2089
    -   * Copy all the bridge addresses to the clipboard.
    
    2090
    -   */
    
    2091
    -  _copyBridges() {
    
    2092
    -    const clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
    
    2093
    -      Ci.nsIClipboardHelper
    
    2094
    -    );
    
    2095
    -    clipboard.copyString(this._bridgeStrings);
    
    2096
    -  },
    
    2097
    -
    
    2098
    -  /**
    
    2099
    -   * Open the QR code dialog encoding all the bridge addresses.
    
    2100
    -   */
    
    2101
    -  _openQR() {
    
    2102
    -    if (!this._canQRBridges) {
    
    2103
    -      return;
    
    2104
    -    }
    
    2105
    -    showBridgeQr(this._bridgeStrings);
    
    2106
    -  },
    
    2107
    -
    
    2108
    -  /**
    
    2109
    -   * The QR button for copying all QR codes.
    
    2110
    -   *
    
    2111
    -   * @type {Element?}
    
    2112
    -   */
    
    2113
    -  _qrButton: null,
    
    2114
    -
    
    2115
    -  _initShareArea() {
    
    2116
    -    document
    
    2117
    -      .getElementById("tor-bridges-copy-addresses-button")
    
    2118
    -      .addEventListener("click", () => {
    
    2119
    -        this._copyBridges();
    
    2120
    -      });
    
    2121
    -
    
    2122
    -    this._qrButton = document.getElementById("tor-bridges-qr-addresses-button");
    
    2123
    -    this._qrButton.addEventListener("click", () => {
    
    2124
    -      this._openQR();
    
    2125
    -    });
    
    2126
    -  },
    
    2127
    -
    
    2128
    -  /**
    
    2129
    -   * The menu for all bridges.
    
    2130
    -   *
    
    2131
    -   * @type {Element?}
    
    2132
    -   */
    
    2133
    -  _bridgesMenu: null,
    
    2134
    -
    
    2135
    -  /**
    
    2136
    -   * Initialize the menu for all bridges.
    
    2137
    -   */
    
    2138
    -  _initBridgesMenu() {
    
    2139
    -    this._bridgesMenu = document.getElementById("tor-bridges-all-options-menu");
    
    2140
    -
    
    2141
    -    // NOTE: We generally assume that once the bridge menu is opened the
    
    2142
    -    // this._bridgeStrings value will not change.
    
    2143
    -    const qrItem = document.getElementById(
    
    2144
    -      "tor-bridges-options-qr-all-menu-item"
    
    2145
    -    );
    
    2146
    -    qrItem.addEventListener("click", () => {
    
    2147
    -      this._openQR();
    
    2148
    -    });
    
    2149
    -
    
    2150
    -    const copyItem = document.getElementById(
    
    2151
    -      "tor-bridges-options-copy-all-menu-item"
    
    2152
    -    );
    
    2153
    -    copyItem.addEventListener("click", () => {
    
    2154
    -      this._copyBridges();
    
    2155
    -    });
    
    2156
    -
    
    2157
    -    const editItem = document.getElementById(
    
    2158
    -      "tor-bridges-options-edit-all-menu-item"
    
    2159
    -    );
    
    2160
    -    editItem.addEventListener("click", () => {
    
    2161
    -      this._openUserProvideDialog("edit");
    
    2162
    -    });
    
    2163
    -
    
    2164
    -    // TODO: Do we want a different item for built-in bridges, rather than
    
    2165
    -    // "Remove all bridges"?
    
    2166
    -    document
    
    2167
    -      .getElementById("tor-bridges-options-remove-all-menu-item")
    
    2168
    -      .addEventListener("click", async () => {
    
    2169
    -        // TODO: Should we only have a warning when not built-in?
    
    2170
    -        const parentWindow =
    
    2171
    -          Services.wm.getMostRecentWindow("navigator:browser");
    
    2172
    -        const flags =
    
    2173
    -          Services.prompt.BUTTON_POS_0 *
    
    2174
    -            Services.prompt.BUTTON_TITLE_IS_STRING +
    
    2175
    -          Services.prompt.BUTTON_POS_0_DEFAULT +
    
    2176
    -          Services.prompt.BUTTON_DEFAULT_IS_DESTRUCTIVE +
    
    2177
    -          Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_CANCEL;
    
    2178
    -
    
    2179
    -        const [titleString, bodyString, removeString] =
    
    2180
    -          await document.l10n.formatValues([
    
    2181
    -            { id: "remove-all-bridges-warning-title" },
    
    2182
    -            { id: "remove-all-bridges-warning-description" },
    
    2183
    -            { id: "remove-all-bridges-warning-remove-button" },
    
    2184
    -          ]);
    
    2185
    -
    
    2186
    -        // TODO: Update the text, and remove old strings.
    
    2187
    -        const buttonIndex = Services.prompt.confirmEx(
    
    2188
    -          parentWindow,
    
    2189
    -          titleString,
    
    2190
    -          bodyString,
    
    2191
    -          flags,
    
    2192
    -          removeString,
    
    2193
    -          null,
    
    2194
    -          null,
    
    2195
    -          null,
    
    2196
    -          {}
    
    2197
    -        );
    
    2198
    -
    
    2199
    -        if (buttonIndex !== 0) {
    
    2200
    -          return;
    
    2201
    -        }
    
    2202
    -
    
    2203
    -        TorSettings.changeSettings({
    
    2204
    -          // This should always have the side effect of disabling bridges as
    
    2205
    -          // well.
    
    2206
    -          bridges: { source: TorBridgeSource.Invalid },
    
    2207
    -        });
    
    2208
    -      });
    
    2209
    -
    
    2210
    -    this._bridgesMenu.addEventListener("showing", () => {
    
    2211
    -      qrItem.hidden = !this._canShare || !this._canQRBridges;
    
    2212
    -      editItem.hidden = this._bridgeSource !== TorBridgeSource.UserProvided;
    
    2213
    -    });
    
    2214
    -
    
    2215
    -    const bridgesMenuButton = document.getElementById(
    
    2216
    -      "tor-bridges-all-options-button"
    
    2217
    -    );
    
    2218
    -    bridgesMenuButton.addEventListener("click", event => {
    
    2219
    -      this._bridgesMenu.toggle(event, bridgesMenuButton);
    
    2220
    -    });
    
    2221
    -
    
    2222
    -    this._bridgesMenu.addEventListener("hidden", () => {
    
    2223
    -      // Make sure the button receives focus again when the menu is hidden.
    
    2224
    -      // Currently, panel-list.js only does this when the menu is opened with a
    
    2225
    -      // keyboard, but this causes focus to be lost from the page if the user
    
    2226
    -      // uses a mixture of keyboard and mouse.
    
    2227
    -      bridgesMenuButton.focus();
    
    2228
    -    });
    
    2229
    -  },
    
    2230
    -
    
    2231
    -  /**
    
    2232
    -   * Force the bridges menu to close.
    
    2233
    -   */
    
    2234
    -  _forceCloseBridgesMenu() {
    
    2235
    -    this._bridgesMenu.hide(null, { force: true });
    
    2236
    -  },
    
    2237
    -
    
    2238
    -  /**
    
    2239
    -   * Open a bridge dialog that will change the users bridges.
    
    2240
    -   *
    
    2241
    -   * @param {string} url - The url of the dialog to open.
    
    2242
    -   * @param {object?} inputData - The input data to send to the dialog window.
    
    2243
    -   * @param {Function} onAccept - The method to call if the bridge dialog was
    
    2244
    -   *   accepted by the user. This will be passed a "result" object containing
    
    2245
    -   *   data set by the dialog. This should return a promise that resolves once
    
    2246
    -   *   the bridge settings have been set, or null if the settings have not
    
    2247
    -   *   been applied.
    
    2248
    -   */
    
    2249
    -  _openDialog(url, inputData, onAccept) {
    
    2250
    -    const result = { accepted: false, connect: false };
    
    2251
    -    let savedSettings = null;
    
    2252
    -    gSubDialog.open(
    
    2253
    -      url,
    
    2254
    -      {
    
    2255
    -        features: "resizable=yes",
    
    2256
    -        closingCallback: () => {
    
    2257
    -          if (!result.accepted) {
    
    2258
    -            return;
    
    2259
    -          }
    
    2260
    -          savedSettings = onAccept(result);
    
    2261
    -          if (!savedSettings) {
    
    2262
    -            // No change in settings.
    
    2263
    -            return;
    
    2264
    -          }
    
    2265
    -          if (!result.connect) {
    
    2266
    -            // Do not open about:torconnect.
    
    2267
    -            return;
    
    2268
    -          }
    
    2269
    -
    
    2270
    -          // Wait until the settings are applied before bootstrapping.
    
    2271
    -          // NOTE: Saving the settings should also cancel any existing bootstrap
    
    2272
    -          // attempt first. See tor-browser#41921.
    
    2273
    -          savedSettings.then(() => {
    
    2274
    -            // The bridge dialog button is "connect" when Tor is not
    
    2275
    -            // bootstrapped, so do the connect.
    
    2276
    -
    
    2277
    -            // Start Bootstrapping, which should use the configured bridges.
    
    2278
    -            // NOTE: We do this regardless of any previous TorConnect Error.
    
    2279
    -            TorConnectParent.open({ beginBootstrapping: "hard" });
    
    2280
    -          });
    
    2281
    -        },
    
    2282
    -        // closedCallback should be called after gSubDialog has already
    
    2283
    -        // re-assigned focus back to the document.
    
    2284
    -        closedCallback: () => {
    
    2285
    -          if (!savedSettings) {
    
    2286
    -            return;
    
    2287
    -          }
    
    2288
    -          // Wait until the settings have changed, so that the UI could
    
    2289
    -          // respond, then move focus.
    
    2290
    -          savedSettings.then(() => gBridgeSettings.takeFocus());
    
    2291
    -        },
    
    2292
    -      },
    
    2293
    -      result,
    
    2294
    -      inputData
    
    2295
    -    );
    
    2296
    -  },
    
    2297
    -
    
    2298
    -  /**
    
    2299
    -   * Open the built-in bridge dialog.
    
    2300
    -   */
    
    2301
    -  _openBuiltinDialog() {
    
    2302
    -    this._openDialog(
    
    2303
    -      "chrome://browser/content/torpreferences/builtinBridgeDialog.xhtml",
    
    2304
    -      null,
    
    2305
    -      result => {
    
    2306
    -        if (!result.type) {
    
    2307
    -          return null;
    
    2308
    -        }
    
    2309
    -        return TorSettings.changeSettings({
    
    2310
    -          bridges: {
    
    2311
    -            enabled: true,
    
    2312
    -            source: TorBridgeSource.BuiltIn,
    
    2313
    -            builtin_type: result.type,
    
    2314
    -          },
    
    2315
    -        });
    
    2316
    -      }
    
    2317
    -    );
    
    2318
    -  },
    
    2319
    -
    
    2320
    -  /*
    
    2321
    -   * Open the request bridge dialog.
    
    2322
    -   */
    
    2323
    -  _openRequestDialog() {
    
    2324
    -    this._openDialog(
    
    2325
    -      "chrome://browser/content/torpreferences/requestBridgeDialog.xhtml",
    
    2326
    -      null,
    
    2327
    -      result => {
    
    2328
    -        if (!result.bridges?.length) {
    
    2329
    -          return null;
    
    2330
    -        }
    
    2331
    -        return TorSettings.changeSettings({
    
    2332
    -          bridges: {
    
    2333
    -            enabled: true,
    
    2334
    -            source: TorBridgeSource.BridgeDB,
    
    2335
    -            bridge_strings: result.bridges,
    
    2336
    -          },
    
    2337
    -        });
    
    2338
    -      }
    
    2339
    -    );
    
    2340
    -  },
    
    2341
    -
    
    2342
    -  /**
    
    2343
    -   * Open the user provide dialog.
    
    2344
    -   *
    
    2345
    -   * @param {string} mode - The mode to open the dialog in: "add", "replace" or
    
    2346
    -   *   "edit".
    
    2347
    -   */
    
    2348
    -  _openUserProvideDialog(mode) {
    
    2349
    -    this._openDialog(
    
    2350
    -      "chrome://browser/content/torpreferences/provideBridgeDialog.xhtml",
    
    2351
    -      { mode },
    
    2352
    -      result => {
    
    2353
    -        const loxId = result.loxId;
    
    2354
    -        if (!loxId && !result.addresses?.length) {
    
    2355
    -          return null;
    
    2356
    -        }
    
    2357
    -        const bridges = { enabled: true };
    
    2358
    -        if (loxId) {
    
    2359
    -          bridges.source = TorBridgeSource.Lox;
    
    2360
    -          bridges.lox_id = loxId;
    
    2361
    -        } else {
    
    2362
    -          bridges.source = TorBridgeSource.UserProvided;
    
    2363
    -          bridges.bridge_strings = result.addresses;
    
    2364
    -        }
    
    2365
    -        return TorSettings.changeSettings({ bridges });
    
    2366
    -      }
    
    2367
    -    );
    
    2368
    -  },
    
    2369
    -};
    
    2370
    -
    
    2371
    -/**
    
    2372
    - * Area to show the internet and tor network connection status.
    
    2373
    - */
    
    2374
    -const gNetworkStatus = {
    
    2375
    -  /**
    
    2376
    -   * Initialize the area.
    
    2377
    -   */
    
    2378
    -  init() {
    
    2379
    -    this._internetAreaEl = document.getElementById(
    
    2380
    -      "network-status-internet-area"
    
    2381
    -    );
    
    2382
    -    this._internetResultEl = this._internetAreaEl.querySelector(
    
    2383
    -      ".network-status-result"
    
    2384
    -    );
    
    2385
    -
    
    2386
    -    this._torAreaEl = document.getElementById("network-status-tor-area");
    
    2387
    -    this._torResultEl = this._torAreaEl.querySelector(".network-status-result");
    
    2388
    -    this._torConnectButton = document.getElementById(
    
    2389
    -      "network-status-tor-connect-button"
    
    2390
    -    );
    
    2391
    -    this._torConnectButton.addEventListener("click", () => {
    
    2392
    -      TorConnectParent.open({ beginBootstrapping: "soft" });
    
    2393
    -    });
    
    2394
    -
    
    2395
    -    this._updateInternetStatus();
    
    2396
    -    this._updateTorConnectionStatus();
    
    2397
    -
    
    2398
    -    Services.obs.addObserver(this, TorConnectTopics.StageChange);
    
    2399
    -    Services.obs.addObserver(this, TorConnectTopics.InternetStatusChange);
    
    2400
    -  },
    
    2401
    -
    
    2402
    -  /**
    
    2403
    -   * Un-initialize the area.
    
    2404
    -   */
    
    2405
    -  uninit() {
    
    2406
    -    Services.obs.removeObserver(this, TorConnectTopics.StageChange);
    
    2407
    -    Services.obs.removeObserver(this, TorConnectTopics.InternetStatusChange);
    
    2408
    -  },
    
    2409
    -
    
    2410
    -  observe(subject, topic) {
    
    2411
    -    switch (topic) {
    
    2412
    -      // triggered when tor connect state changes and we may
    
    2413
    -      // need to update the messagebox
    
    2414
    -      case TorConnectTopics.StageChange:
    
    2415
    -        this._updateTorConnectionStatus();
    
    2416
    -        break;
    
    2417
    -      case TorConnectTopics.InternetStatusChange:
    
    2418
    -        this._updateInternetStatus();
    
    2419
    -        break;
    
    2420
    -    }
    
    2421
    -  },
    
    2422
    -
    
    2423
    -  /**
    
    2424
    -   * Update the shown internet status.
    
    2425
    -   */
    
    2426
    -  _updateInternetStatus() {
    
    2427
    -    let l10nId;
    
    2428
    -    let isOffline = false;
    
    2429
    -    switch (TorConnect.internetStatus) {
    
    2430
    -      case InternetStatus.Offline:
    
    2431
    -        l10nId = "tor-connection-internet-status-offline";
    
    2432
    -        isOffline = true;
    
    2433
    -        break;
    
    2434
    -      case InternetStatus.Online:
    
    2435
    -        l10nId = "tor-connection-internet-status-online";
    
    2436
    -        break;
    
    2437
    -      default:
    
    2438
    -        l10nId = "tor-connection-internet-status-unknown";
    
    2439
    -        break;
    
    2440
    -    }
    
    2441
    -    this._internetResultEl.setAttribute("data-l10n-id", l10nId);
    
    2442
    -    this._internetAreaEl.classList.toggle("status-offline", isOffline);
    
    2443
    -  },
    
    2444
    -
    
    2445
    -  /**
    
    2446
    -   * Update the shown Tor connection status.
    
    2447
    -   */
    
    2448
    -  _updateTorConnectionStatus() {
    
    2449
    -    const buttonHadFocus = this._torConnectButton.contains(
    
    2450
    -      document.activeElement
    
    2451
    -    );
    
    2452
    -    const isBootstrapped =
    
    2453
    -      TorConnect.stageName === TorConnectStage.Bootstrapped;
    
    2454
    -    const isBlocked = !isBootstrapped && TorConnect.potentiallyBlocked;
    
    2455
    -    let l10nId;
    
    2456
    -    if (isBootstrapped) {
    
    2457
    -      l10nId = "tor-connection-network-status-connected";
    
    2458
    -    } else if (isBlocked) {
    
    2459
    -      l10nId = "tor-connection-network-status-blocked";
    
    2460
    -    } else {
    
    2461
    -      l10nId = "tor-connection-network-status-not-connected";
    
    2462
    -    }
    
    2463
    -
    
    2464
    -    document.l10n.setAttributes(this._torResultEl, l10nId);
    
    2465
    -    this._torAreaEl.classList.toggle("status-connected", isBootstrapped);
    
    2466
    -    this._torAreaEl.classList.toggle("status-blocked", isBlocked);
    
    2467
    -    if (isBootstrapped && buttonHadFocus) {
    
    2468
    -      // Button has become hidden and will loose focus. Most likely this has
    
    2469
    -      // happened because the user clicked the button to open about:torconnect.
    
    2470
    -      // Since this is near the top of the page, we move focus to the search
    
    2471
    -      // input (for when the user returns).
    
    2472
    -      gSearchResultsPane.searchInput.focus();
    
    2473
    -    }
    
    2474
    -  },
    
    2475
    -};
    
    2476
    -
    
    2477
    -/*
    
    2478
    -  Connection Pane
    
    2479
    -
    
    2480
    -  Code for populating the XUL in about:preferences#connection, handling input events, interfacing with tor-launcher
    
    2481
    -*/
    
    2482
    -const gConnectionPane = (function () {
    
    2483
    -  /* CSS selectors for all of the Tor Network DOM elements we need to access */
    
    2484
    -  const selectors = {
    
    2485
    -    bridges: {
    
    2486
    -      locationGroup: "#torPreferences-bridges-locationGroup",
    
    2487
    -      locationLabel: "#torPreferences-bridges-locationLabel",
    
    2488
    -      location: "#torPreferences-bridges-location",
    
    2489
    -      locationEntries: "#torPreferences-bridges-locationEntries",
    
    2490
    -      chooseForMe: "#torPreferences-bridges-buttonChooseBridgeForMe",
    
    2491
    -    },
    
    2492
    -  }; /* selectors */
    
    2493
    -
    
    2494
    -  const retval = {
    
    2495
    -    // cached frequently accessed DOM elements
    
    2496
    -    _enableQuickstartToggle: null,
    
    2497
    -
    
    2498
    -    // populate xul with strings and cache the relevant elements
    
    2499
    -    _populateXUL() {
    
    2500
    -      // Quickstart
    
    2501
    -      this._enableQuickstartToggle = document.getElementById(
    
    2502
    -        "tor-connection-quickstart-toggle"
    
    2503
    -      );
    
    2504
    -      this._enableQuickstartToggle.addEventListener("toggle", () => {
    
    2505
    -        TorConnect.quickstart = this._enableQuickstartToggle.pressed;
    
    2506
    -      });
    
    2507
    -      this._enableQuickstartToggle.pressed = TorConnect.quickstart;
    
    2508
    -      Services.obs.addObserver(this, TorConnectTopics.QuickstartChange);
    
    2509
    -
    
    2510
    -      // Location
    
    2511
    -      {
    
    2512
    -        const prefpane = document.getElementById("mainPrefPane");
    
    2513
    -
    
    2514
    -        const locationGroup = prefpane.querySelector(
    
    2515
    -          selectors.bridges.locationGroup
    
    2516
    -        );
    
    2517
    -        prefpane.querySelector(selectors.bridges.locationLabel).textContent =
    
    2518
    -          TorStrings.settings.bridgeLocation;
    
    2519
    -        const location = prefpane.querySelector(selectors.bridges.location);
    
    2520
    -        const locationEntries = prefpane.querySelector(
    
    2521
    -          selectors.bridges.locationEntries
    
    2522
    -        );
    
    2523
    -        const chooseForMe = prefpane.querySelector(
    
    2524
    -          selectors.bridges.chooseForMe
    
    2525
    -        );
    
    2526
    -        chooseForMe.setAttribute(
    
    2527
    -          "label",
    
    2528
    -          TorStrings.settings.bridgeChooseForMe
    
    2529
    -        );
    
    2530
    -        chooseForMe.addEventListener("command", () => {
    
    2531
    -          if (!location.value) {
    
    2532
    -            return;
    
    2533
    -          }
    
    2534
    -          TorConnectParent.open({
    
    2535
    -            beginBootstrapping: "hard",
    
    2536
    -            regionCode: location.value,
    
    2537
    -          });
    
    2538
    -        });
    
    2539
    -        const createItem = (value, label, disabled) => {
    
    2540
    -          const item = document.createXULElement("menuitem");
    
    2541
    -          item.setAttribute("value", value);
    
    2542
    -          item.setAttribute("label", label);
    
    2543
    -          if (disabled) {
    
    2544
    -            item.setAttribute("disabled", "true");
    
    2545
    -          }
    
    2546
    -          return item;
    
    2547
    -        };
    
    2548
    -
    
    2549
    -        // TODO: Re-fetch when intl:app-locales-changed is fired, if we keep
    
    2550
    -        // this after tor-browser#42477.
    
    2551
    -        const regionNames = TorConnect.getRegionNames();
    
    2552
    -        const addLocations = codes => {
    
    2553
    -          const items = [];
    
    2554
    -          for (const code of codes) {
    
    2555
    -            items.push(createItem(code, regionNames[code] || code));
    
    2556
    -          }
    
    2557
    -          items.sort((left, right) => left.label.localeCompare(right.label));
    
    2558
    -          locationEntries.append(...items);
    
    2559
    -        };
    
    2560
    -        // Add automatic before waiting for getFrequentRegions.
    
    2561
    -        locationEntries.append(
    
    2562
    -          createItem("automatic", TorStrings.settings.bridgeLocationAutomatic)
    
    2563
    -        );
    
    2564
    -        location.value = "automatic";
    
    2565
    -        TorConnect.getFrequentRegions().then(frequentCodes => {
    
    2566
    -          locationEntries.append(
    
    2567
    -            createItem("", TorStrings.settings.bridgeLocationFrequent, true)
    
    2568
    -          );
    
    2569
    -          addLocations(frequentCodes);
    
    2570
    -          locationEntries.append(
    
    2571
    -            createItem("", TorStrings.settings.bridgeLocationOther, true)
    
    2572
    -          );
    
    2573
    -          addLocations(Object.keys(regionNames));
    
    2574
    -        });
    
    2575
    -        this._showAutoconfiguration = () => {
    
    2576
    -          locationGroup.hidden =
    
    2577
    -            !TorConnect.canBeginAutoBootstrap || !TorConnect.potentiallyBlocked;
    
    2578
    -        };
    
    2579
    -        this._showAutoconfiguration();
    
    2580
    -      }
    
    2581
    -
    
    2582
    -      // Advanced setup
    
    2583
    -      document
    
    2584
    -        .getElementById("torPreferences-advanced-button")
    
    2585
    -        .addEventListener("click", () => {
    
    2586
    -          this.onAdvancedSettings();
    
    2587
    -        });
    
    2588
    -
    
    2589
    -      // Tor logs
    
    2590
    -      document
    
    2591
    -        .getElementById("torPreferences-buttonTorLogs")
    
    2592
    -        .addEventListener("click", () => {
    
    2593
    -          this.onViewTorLogs();
    
    2594
    -        });
    
    2595
    -
    
    2596
    -      Services.obs.addObserver(this, TorConnectTopics.StageChange);
    
    2597
    -    },
    
    2598
    -
    
    2599
    -    init() {
    
    2600
    -      gBridgeSettings.init();
    
    2601
    -      gNetworkStatus.init();
    
    2602
    -
    
    2603
    -      this._populateXUL();
    
    2604
    -
    
    2605
    -      const onUnload = () => {
    
    2606
    -        window.removeEventListener("unload", onUnload);
    
    2607
    -        gConnectionPane.uninit();
    
    2608
    -      };
    
    2609
    -      window.addEventListener("unload", onUnload);
    
    2610
    -    },
    
    2611
    -
    
    2612
    -    uninit() {
    
    2613
    -      gBridgeSettings.uninit();
    
    2614
    -      gNetworkStatus.uninit();
    
    2615
    -
    
    2616
    -      // unregister our observer topics
    
    2617
    -      Services.obs.removeObserver(this, TorConnectTopics.QuickstartChange);
    
    2618
    -      Services.obs.removeObserver(this, TorConnectTopics.StageChange);
    
    2619
    -    },
    
    2620
    -
    
    2621
    -    // whether the page should be present in about:preferences
    
    2622
    -    get enabled() {
    
    2623
    -      return TorConnect.enabled;
    
    2624
    -    },
    
    2625
    -
    
    2626
    -    //
    
    2627
    -    // Callbacks
    
    2628
    -    //
    
    2629
    -
    
    2630
    -    observe(subject, topic) {
    
    2631
    -      switch (topic) {
    
    2632
    -        case TorConnectTopics.QuickstartChange: {
    
    2633
    -          this._enableQuickstartToggle.pressed = TorConnect.quickstart;
    
    2634
    -          break;
    
    2635
    -        }
    
    2636
    -        // triggered when tor connect state changes and we may
    
    2637
    -        // need to update the messagebox
    
    2638
    -        case TorConnectTopics.StageChange: {
    
    2639
    -          this._showAutoconfiguration();
    
    2640
    -          break;
    
    2641
    -        }
    
    2642
    -      }
    
    2643
    -    },
    
    2644
    -
    
    2645
    -    async onAdvancedSettings() {
    
    2646
    -      // Ensure TorSettings is complete before loading the dialog, which reads
    
    2647
    -      // from TorSettings.
    
    2648
    -      await TorSettings.initializedPromise;
    
    2649
    -      gSubDialog.open(
    
    2650
    -        "chrome://browser/content/torpreferences/connectionSettingsDialog.xhtml",
    
    2651
    -        { features: "resizable=yes" }
    
    2652
    -      );
    
    2653
    -    },
    
    2654
    -
    
    2655
    -    onViewTorLogs() {
    
    2656
    -      gSubDialog.open(
    
    2657
    -        "chrome://browser/content/torpreferences/torLogDialog.xhtml",
    
    2658
    -        { features: "resizable=yes" }
    
    2659
    -      );
    
    2660
    -    },
    
    2661
    -  };
    
    2662
    -  return retval;
    
    2663
    -})(); /* gConnectionPane */

  • browser/components/torpreferences/content/torPreferences.css
    ... ... @@ -28,108 +28,14 @@ button.spoof-button-disabled {
    28 28
       }
    
    29 29
     }
    
    30 30
     
    
    31
    -.tor-toggle {
    
    32
    -  margin-block: var(--space-large);
    
    33
    -  width: max-content;
    
    34
    -}
    
    35
    -
    
    36
    -/* Status */
    
    37
    -
    
    38
    -#network-status-internet-area {
    
    39
    -  margin-block: var(--space-large);
    
    40
    -}
    
    41
    -
    
    42
    -#network-status-tor-area {
    
    43
    -  margin-block: 0 var(--space-xxlarge);
    
    44
    -}
    
    45
    -
    
    46
    -.network-status-area {
    
    47
    -  display: flex;
    
    48
    -  align-items: center;
    
    49
    -  white-space: nowrap;
    
    50
    -}
    
    51
    -
    
    52
    -.network-status-area > * {
    
    53
    -  flex: 0 0 auto;
    
    54
    -}
    
    55
    -
    
    56
    -.network-status-icon {
    
    57
    -  width: var(--icon-size);
    
    58
    -  height: var(--icon-size);
    
    59
    -  margin-inline-end: var(--space-small);
    
    60
    -  -moz-context-properties: fill, stroke;
    
    61
    -  fill: var(--icon-color);
    
    62
    -  stroke: var(--icon-color);
    
    63
    -}
    
    64
    -
    
    65
    -#network-status-internet-area .network-status-icon {
    
    66
    -  content: url("chrome://browser/content/torconnect/network.svg");
    
    67
    -}
    
    68
    -
    
    69
    -#network-status-internet-area.status-offline .network-status-icon {
    
    70
    -  content: url("chrome://browser/content/torconnect/network-broken.svg");
    
    71
    -}
    
    72
    -
    
    73
    -#network-status-tor-area .network-status-icon {
    
    74
    -  content: url("chrome://browser/content/torconnect/tor-connect.svg");
    
    75
    -}
    
    76
    -
    
    77
    -#network-status-tor-area:not(.status-connected) .network-status-icon {
    
    78
    -  content: url("chrome://browser/content/torconnect/tor-connect-broken.svg");
    
    79
    -}
    
    80
    -
    
    81
    -#network-status-tor-area.status-blocked .network-status-icon {
    
    82
    -  /* Same as .tor-connect-status-potentially-blocked. */
    
    83
    -  stroke: var(--icon-color-critical);
    
    84
    -}
    
    85
    -
    
    86
    -.network-status-label {
    
    87
    -  font-weight: var(--font-weight-bold);
    
    88
    -  margin-inline-end: var(--space-medium);
    
    89
    -}
    
    90
    -
    
    91
    -.network-status-result {
    
    92
    -  margin-inline-end: var(--space-medium);
    
    93
    -}
    
    94
    -
    
    95
    -#network-status-tor-area.status-connected #network-status-tor-connect-button {
    
    96
    -  /* Hide button when already connected. */
    
    97
    -  display: none;
    
    98
    -}
    
    99
    -
    
    100 31
     /* Bridge settings */
    
    101 32
     
    
    102
    -.tor-medium-heading {
    
    103
    -  /* Same font size as mozilla preferences h2. */
    
    104
    -  font-size: var(--font-size-large);
    
    105
    -  font-weight: var(--font-weight-bold);
    
    106
    -  margin: 0;
    
    107
    -}
    
    108
    -
    
    109 33
     .tor-small-heading {
    
    110 34
       font-size: inherit;
    
    111 35
       font-weight: var(--font-weight-bold);
    
    112 36
       margin: 0;
    
    113 37
     }
    
    114 38
     
    
    115
    -/* Hide the tor-search-heading elements when the group's search header is
    
    116
    - * hidden. These only appear in search results.
    
    117
    - * See tor-browser#43320.
    
    118
    - * NOTE: `.search-header[hidden] ~ :is(* .tor-search-heading)` will not match
    
    119
    - * (possibly because the `~` selector is unsure how to integrate with the
    
    120
    - * non-compound `* .tor-search-heading` selector). So we need to duplicate the
    
    121
    - * `.search-header[hidden]` rule. */
    
    122
    -#torPreferences-bridges-group :is(.search-header[hidden] ~ * .tor-search-heading, .search-header[hidden] ~ .tor-search-heading) {
    
    123
    -  display: none;
    
    124
    -}
    
    125
    -
    
    126
    -/* Hide the tor-non-search-heading elements when the group's search header is
    
    127
    - * not hidden. These only appear outside of search results.
    
    128
    - * See tor-browser#43320. */
    
    129
    -#torPreferences-bridges-group :is(.search-header:not([hidden]) ~ * .tor-non-search-heading, .search-header:not([hidden]) ~ .tor-non-search-heading) {
    
    130
    -  display: none;
    
    131
    -}
    
    132
    -
    
    133 39
     .tor-focusable-heading {
    
    134 40
       /* Do not occupy more horizontal space than necessary. */
    
    135 41
       width: fit-content;
    
    ... ... @@ -188,25 +94,6 @@ button.spoof-button-disabled {
    188 94
       display: none;
    
    189 95
     }
    
    190 96
     
    
    191
    -#tor-bridges-update-area {
    
    192
    -  /* Still accessible to screen reader, but not visual. */
    
    193
    -  position: absolute;
    
    194
    -  clip-path: inset(50%);
    
    195
    -}
    
    196
    -
    
    197
    -#torPreferences-bridges-group:not(.bridges-initialized) {
    
    198
    -  /* Hide bridge settings whilst not initialized. */
    
    199
    -  display: none;
    
    200
    -}
    
    201
    -
    
    202
    -#tor-bridges-none,
    
    203
    -#tor-bridges-current {
    
    204
    -  @media not -moz-pref("browser.settings-redesign.enabled") {
    
    205
    -    margin-inline: 0;
    
    206
    -    margin-block: var(--space-xxlarge);
    
    207
    -  }
    
    208
    -}
    
    209
    -
    
    210 97
     #tor-bridges-none:not([hidden]) {
    
    211 98
       display: grid;
    
    212 99
       justify-items: center;
    
    ... ... @@ -217,11 +104,6 @@ button.spoof-button-disabled {
    217 104
       border-radius: var(--border-radius-small);
    
    218 105
       color: var(--text-color-deemphasized);
    
    219 106
       border: 2px dashed var(--border-color-deemphasized);
    
    220
    -
    
    221
    -  @media not -moz-pref("browser.settings-redesign.enabled") {
    
    222
    -    padding-block: 64px;
    
    223
    -    gap: var(--space-large);
    
    224
    -  }
    
    225 107
     }
    
    226 108
     
    
    227 109
     #tor-bridges-none-icon {
    
    ... ... @@ -232,33 +114,10 @@ button.spoof-button-disabled {
    232 114
       fill: currentColor;
    
    233 115
     }
    
    234 116
     
    
    235
    -.tor-bridges-box,
    
    236 117
     .tor-bridges-details-box {
    
    237 118
       padding: var(--space-large);
    
    238 119
       border-radius: var(--border-radius-medium);
    
    239 120
       border: var(--border-width) solid var(--border-color);
    
    240
    -
    
    241
    -  @media not -moz-pref("browser.settings-redesign.enabled") {
    
    242
    -    border-radius: var(--border-radius-small);
    
    243
    -    background: var(--background-color-box-info);
    
    244
    -  }
    
    245
    -}
    
    246
    -
    
    247
    -@media not forced-colors {
    
    248
    -  .tor-bridges-box {
    
    249
    -    border-color: transparent;
    
    250
    -  }
    
    251
    -}
    
    252
    -
    
    253
    -#tor-bridges-current-header-bar {
    
    254
    -  display: grid;
    
    255
    -  min-width: max-content;
    
    256
    -  grid-template: "heading source button" min-content / max-content 1fr max-content;
    
    257
    -  align-items: center;
    
    258
    -  border-block-end: var(--border-width) solid var(--border-color);
    
    259
    -  padding-block-end: var(--space-large);
    
    260
    -  margin-block-end: var(--space-large);
    
    261
    -  white-space: nowrap;
    
    262 121
     }
    
    263 122
     
    
    264 123
     tor-bridges-display {
    
    ... ... @@ -685,63 +544,6 @@ tor-bridges-display {
    685 544
       align-self: center;
    
    686 545
     }
    
    687 546
     
    
    688
    -.tor-bridges-provider-heading {
    
    689
    -  margin-block: var(--space-xxlarge) var(--space-small);
    
    690
    -}
    
    691
    -
    
    692
    -#tor-bridges-provider-area {
    
    693
    -  display: grid;
    
    694
    -  grid-template-columns: 1fr 1fr;
    
    695
    -  gap: var(--space-large);
    
    696
    -  align-items: start;
    
    697
    -  margin-block-start: var(--space-xlarge);
    
    698
    -}
    
    699
    -
    
    700
    -#tor-bridges-provider-list {
    
    701
    -  display: grid;
    
    702
    -  grid-template-columns: max-content max-content;
    
    703
    -  gap: var(--space-xlarge) var(--space-medium);
    
    704
    -  margin-block: var(--space-large);
    
    705
    -  padding: 0;
    
    706
    -}
    
    707
    -
    
    708
    -.tor-bridges-provider-item {
    
    709
    -  grid-column: 1 / -1;
    
    710
    -  display: grid;
    
    711
    -  grid-template-columns: subgrid;
    
    712
    -  align-items: center;
    
    713
    -  justify-items: start;
    
    714
    -  gap: var(--space-small) var(--space-medium);
    
    715
    -}
    
    716
    -
    
    717
    -.tor-bridges-provider-icon {
    
    718
    -  width: var(--icon-size);
    
    719
    -  height: var(--icon-size);
    
    720
    -  -moz-context-properties: fill;
    
    721
    -  fill: var(--icon-color);
    
    722
    -}
    
    723
    -
    
    724
    -#tor-bridges-provider-icon-telegram {
    
    725
    -  content: url("chrome://browser/content/torpreferences/telegram-logo.svg");
    
    726
    -}
    
    727
    -
    
    728
    -#tor-bridges-provider-icon-web {
    
    729
    -  content: url("chrome://browser/content/torconnect/network.svg");
    
    730
    -}
    
    731
    -
    
    732
    -#tor-bridges-provider-icon-email {
    
    733
    -  content: url("chrome://browser/content/torpreferences/mail.svg");
    
    734
    -}
    
    735
    -
    
    736
    -.tor-bridges-provider-name {
    
    737
    -  font-weight: var(--font-weight-bold);
    
    738
    -  font-size: var(--font-size-small);
    
    739
    -}
    
    740
    -
    
    741
    -.tor-bridges-provider-instruction {
    
    742
    -  grid-column: 2 / 3;
    
    743
    -}
    
    744
    -
    
    745 547
     #torBridgesRequestBanner {
    
    746 548
       display: flex;
    
    747 549
       flex-direction: column;
    
    ... ... @@ -760,37 +562,6 @@ tor-bridges-display {
    760 562
       }
    
    761 563
     }
    
    762 564
     
    
    763
    -#tor-bridges-request-box {
    
    764
    -  /* Take up the full height in the container. */
    
    765
    -  align-self: stretch;
    
    766
    -  display: flex;
    
    767
    -  flex-direction: column;
    
    768
    -  align-items: center;
    
    769
    -  text-align: center;
    
    770
    -  justify-content: center;
    
    771
    -}
    
    772
    -
    
    773
    -#tor-bridges-request-box > * {
    
    774
    -  flex: 0 0 auto;
    
    775
    -}
    
    776
    -
    
    777
    -#tor-bridges-request-description {
    
    778
    -  margin-block: var(--space-medium) var(--space-large);
    
    779
    -}
    
    780
    -
    
    781
    -#tor-bridges-open-request-dialog-button {
    
    782
    -  margin: 0;
    
    783
    -}
    
    784
    -
    
    785
    -#torPreferences-bridges-location {
    
    786
    -  width: 280px;
    
    787
    -}
    
    788
    -
    
    789
    -#torPreferences-bridges-location menuitem[disabled="true"] {
    
    790
    -  color: var(--button-text-color, inherit);
    
    791
    -  font-weight: var(--font-weight-bold);
    
    792
    -}
    
    793
    -
    
    794 565
     /* Request bridges */
    
    795 566
     
    
    796 567
     image#torPreferences-requestBridge-captchaImage {
    
    ... ... @@ -973,11 +744,6 @@ image#torPreferences-requestBridge-errorIcon {
    973 744
       fill: var(--icon-color-warning);
    
    974 745
     }
    
    975 746
     
    
    976
    -groupbox#torPreferences-bridges-group textarea {
    
    977
    -  white-space: pre;
    
    978
    -  overflow: auto;
    
    979
    -}
    
    980
    -
    
    981 747
     /* Provide bridge dialog */
    
    982 748
     
    
    983 749
     #user-provide-bridge-dialog:not(.show-entry-page) #user-provide-bridge-entry-page {
    

  • browser/components/torpreferences/jar.mn
    ... ... @@ -30,7 +30,6 @@ browser.jar:
    30 30
         content/browser/torpreferences/widgets/tor-connection-assist-banner.mjs (widgets/tor-connection-assist-banner.mjs)
    
    31 31
         content/browser/torpreferences/widgets/tor-connection-status.mjs (widgets/tor-connection-status.mjs)
    
    32 32
         content/browser/torpreferences/widgets/tor-connection-status.css (widgets/tor-connection-status.css)
    
    33
    -    content/browser/torpreferences/connectionPane.js                 (content/connectionPane.js)
    
    34 33
         content/browser/torpreferences/torPreferences.css                (content/torPreferences.css)
    
    35 34
         content/browser/torpreferences/bridgemoji/BridgeEmoji.js         (content/bridgemoji/BridgeEmoji.js)
    
    36 35
         content/browser/torpreferences/bridgemoji/bridge-emojis.json     (content/bridgemoji/bridge-emojis.json)
    

  • toolkit/locales/en-US/toolkit/global/tor-browser.ftl
    ... ... @@ -101,11 +101,6 @@ tor-connection-settings-pane =
    101 101
     # "Connection" refers to the Tor Browser's connection to the Tor network.
    
    102 102
     tor-connection-settings-nav-button = Connection
    
    103 103
         .title = Connection
    
    104
    -# -brand-short-name refers to 'Tor Browser', localized.
    
    105
    -tor-connection-overview = { -brand-short-name } routes your traffic over the Tor network, run by thousands of volunteers around the world.
    
    106
    -tor-connection-browser-learn-more-link = Learn more
    
    107
    -tor-connection-automatic-heading = Connect automatically
    
    108
    -tor-connection-automatic-description = Automatically connect to the Tor network at launch using your current connection settings.
    
    109 104
     tor-connection-quickstart-checkbox =
    
    110 105
         .label = Always connect automatically
    
    111 106
     
    
    ... ... @@ -138,10 +133,6 @@ tor-connection-network-status-not-connected = Not connected
    138 133
     # Shown when the user's Tor connection may be blocked.
    
    139 134
     # Uses sentence case in English (US).
    
    140 135
     tor-connection-network-status-blocked = Potentially blocked
    
    141
    -# Button shown when we are not yet connected to the Tor network.
    
    142
    -# It will open a page to start connecting to the Tor network.
    
    143
    -# Uses sentence case in English (US).
    
    144
    -tor-connection-network-status-connect-button = Connect
    
    145 136
     # Here "Connect" refers to connecting to the Tor network.
    
    146 137
     # Uses sentence case in English (US).
    
    147 138
     tor-connection-status-connect-button =
    
    ... ... @@ -149,9 +140,6 @@ tor-connection-status-connect-button =
    149 140
     
    
    150 141
     ## Tor Bridges Settings.
    
    151 142
     
    
    152
    -tor-bridges-heading = Bridges
    
    153
    -tor-bridges-overview = Bridges help you securely access the Tor network in places where Tor is blocked. Depending on where you are, one bridge may work better than another.
    
    154
    -tor-bridges-learn-more-link = Learn more
    
    155 143
     tor-bridges-group =
    
    156 144
         .label = Bridges
    
    157 145
         .description = Bridges help you securely access the Tor network in places where Tor is blocked. Depending on where you are, one bridge may work better than another.
    
    ... ... @@ -232,10 +220,6 @@ tor-bridges-menu-item-copy-address = Copy bridge address
    232 220
     tor-bridges-menu-item-remove-bridge = Remove bridge
    
    233 221
         .accesskey = R
    
    234 222
     
    
    235
    -# Text shown just before a description of the most recent change to the list of user's bridges. Some white space will separate this text from the change description.
    
    236
    -# This text is not visible, but is instead used for screen reader users.
    
    237
    -# E.g. in English this could be "Recent update: One of your Tor bridges has been removed."
    
    238
    -tor-bridges-update-area-intro = Recent update:
    
    239 223
     # Update text for screen reader users when only one of their bridges has been removed.
    
    240 224
     tor-bridges-update-removed-one-bridge = One of your Tor bridges has been removed.
    
    241 225
     # Update text for screen reader users when all of their bridges have been removed.
    
    ... ... @@ -313,74 +297,33 @@ tor-bridges-lox-new-invites =
    313 297
     # Button for the user to acknowledge a change in their "bridge pass".
    
    314 298
     tor-bridges-lox-got-it-button = Got it
    
    315 299
     
    
    316
    -
    
    317
    -# Shown as a heading when the user has no current bridges.
    
    318
    -tor-bridges-add-bridges-heading = Add bridges
    
    319
    -# Shown as a heading when the user has existing bridges that can be replaced.
    
    320
    -tor-bridges-replace-bridges-heading = Replace your bridges
    
    321 300
     # Shown as a heading when the user has no current bridges.
    
    322 301
     tor-bridges-add-bridges-group =
    
    323 302
         .label = Add bridges
    
    324 303
     # Shown as a heading when the user has existing bridges that can be replaced.
    
    325 304
     tor-bridges-replace-bridges-group =
    
    326 305
         .label = Replace your bridges
    
    327
    -
    
    328
    -# -brand-short-name refers to 'Tor Browser', localized.
    
    329
    -tor-bridges-select-built-in-description = Choose from one of { -brand-short-name }’s built-in bridges
    
    330 306
     # -brand-short-name refers to 'Tor Browser', localized.
    
    331 307
     tor-bridges-choose-built-in-button =
    
    332 308
         .label = Choose from one of { -brand-short-name }’s built-in bridges
    
    333
    -tor-bridges-select-built-in-button = Select a built-in bridge…
    
    334
    -
    
    335
    -tor-bridges-add-addresses-description = Enter bridge addresses you already know
    
    336 309
     tor-bridges-enter-bridges-button =
    
    337 310
         .label = Enter bridge addresses you already know
    
    338
    -# Shown when the user has no current bridges.
    
    339
    -# Opens a dialog where the user can provide a new bridge address or share code.
    
    340
    -tor-bridges-add-new-button = Add new bridges…
    
    341
    -# Shown when the user has existing bridges.
    
    342
    -# Opens a dialog where the user can provide a new bridge address or share code to replace their current bridges.
    
    343
    -tor-bridges-replace-button = Replace bridges…
    
    344
    -
    
    345
    -tor-bridges-find-more-heading = Find more bridges
    
    346
    -# "Tor Project" is the organisation name.
    
    347
    -tor-bridges-find-more-description = Since many bridge addresses aren’t public, you may need to request some from the Tor Project.
    
    311
    +
    
    348 312
     # "Tor Project" is the organisation name.
    
    349 313
     tor-bridges-find-more-group =
    
    350 314
         .label = Find more bridges
    
    351 315
         .description = Since many bridge addresses aren’t public, you may need to request some from the Tor Project.
    
    352
    -
    
    353
    -# "Telegram" is the common brand name of the Telegram Messenger application
    
    354
    -tor-bridges-provider-telegram-name = Telegram
    
    355
    -# Here "Message" is a verb, short for "Send a message to". This is an instruction to send a message to the given Telegram Messenger user to receive a new bridge.
    
    356
    -# $telegramUserName (String) - The Telegram Messenger user name that should receive messages. Should be wrapped in '<a data-l10n-name="user">' and '</a>'.
    
    357
    -# E.g. in English, "Message GetBridgesBot".
    
    358
    -tor-bridges-provider-telegram-instruction = Message <a data-l10n-name="user">{ $telegramUserName }</a>
    
    359 316
     # "Telegram" is the common brand name of the Telegram Messenger application.
    
    360 317
     # Here "Message" is a verb, short for "Send a message to". This is an instruction to send a message to the given Telegram Messenger user to receive a new bridge.
    
    361 318
     # $telegramUserName (String) - The Telegram Messenger user name that should receive messages.
    
    362 319
     tor-bridges-source-telegram-link =
    
    363 320
         .label = Telegram
    
    364 321
         .description = Message { $telegramUserName }
    
    365
    -
    
    366
    -# "Web" is the proper noun for the "World Wide Web".
    
    367
    -tor-bridges-provider-web-name = Web
    
    368
    -# Instructions to visit the given website.
    
    369
    -# $url (String) - The URL for Tor Project bridges. Should be wrapped in '<a data-l10n-name"url">' and '</a>'.
    
    370
    -tor-bridges-provider-web-instruction = Visit <a data-l10n-name="url">{ $url }</a>
    
    371
    -
    
    372 322
     # "Web" is the proper noun for the "World Wide Web".
    
    373 323
     # $url (String) - The URL for Tor Project bridges.
    
    374 324
     tor-bridges-source-web-link =
    
    375 325
         .label = Web
    
    376 326
         .description = Visit { $url }
    
    377
    -
    
    378
    -# "Gmail" is the Google brand name. "Riseup" refers to the Riseup organisation at riseup.net.
    
    379
    -tor-bridges-provider-email-name = Gmail or Riseup
    
    380
    -# Here "Email" is a verb, short for "Send an email to". This is an instruction to send an email to the given address to receive a new bridge.
    
    381
    -# $address (String) - The email address that should receive the email.
    
    382
    -# E.g. in English, "Email bridges@xxxxxxxxxxxxxx".
    
    383
    -tor-bridges-provider-email-instruction = Email { $address }
    
    384 327
     # "Gmail" is the Google brand name. "Riseup" refers to the Riseup organisation at riseup.net.
    
    385 328
     # Here "Email" is a verb, short for "Send an email to". This is an instruction to send an email to the given address to receive a new bridge.
    
    386 329
     # $address (String) - The email address that should receive the email.
    
    ... ... @@ -514,16 +457,6 @@ tor-advanced-configure-button =
    514 457
         .label = Configure how { -brand-short-name } connects to the internet
    
    515 458
     tor-view-log-button2 =
    
    516 459
         .label = View the Tor log
    
    517
    -tor-advanced-settings-heading = Advanced
    
    518
    -tor-advanced-settings-description = Configure how { -brand-short-name } connects to the internet.
    
    519
    -# Button that opens the advanced connection settings dialog.
    
    520
    -# Uses sentence case in English (US).
    
    521
    -tor-advanced-settings-button = Settings…
    
    522
    -# "log" is a noun, referring to the recorded text output of the Tor process.
    
    523
    -tor-view-log-description = View the Tor log.
    
    524
    -# "log" is a noun, referring to the recorded text output of the Tor process.
    
    525
    -# Uses sentence case in English (US).
    
    526
    -tor-view-log-button = View log…
    
    527 460
     
    
    528 461
     ## Tor log dialog.
    
    529 462
     
    

  • toolkit/modules/TorStrings.sys.mjs
    ... ... @@ -70,13 +70,7 @@ const Loader = {
    70 70
           // Message box
    
    71 71
           torPreferencesDescription:
    
    72 72
             "Tor Browser routes your traffic over the Tor network, run by thousands of volunteers around the world.",
    
    73
    -      // Quickstart
    
    74 73
           quickstartCheckbox: "Always connect automatically",
    
    75
    -      bridgeLocation: "Your location",
    
    76
    -      bridgeLocationAutomatic: "Automatic",
    
    77
    -      bridgeLocationFrequent: "Frequently selected locations",
    
    78
    -      bridgeLocationOther: "Other locations",
    
    79
    -      bridgeChooseForMe: "Choose a Bridge For Me…",
    
    80 74
         };
    
    81 75
     
    
    82 76
         const tsb = new TorPropertyStringBundle(
    

  • toolkit/torbutton/chrome/locale/en-US/settings.properties
    ... ... @@ -9,11 +9,3 @@
    9 9
     settings.torPreferencesDescription=Tor Browser routes your traffic over the Tor network, run by thousands of volunteers around the world.
    
    10 10
     
    
    11 11
     settings.quickstartCheckbox=Always connect automatically
    12
    -
    
    13
    -# Might be removed in tor-browser#42477
    
    14
    -
    
    15
    -settings.bridgeLocation=Your location
    
    16
    -settings.bridgeLocationAutomatic=Automatic
    
    17
    -settings.bridgeLocationFrequent=Frequently selected locations
    
    18
    -settings.bridgeLocationOther=Other locations
    
    19
    -settings.bridgeChooseForMe=Choose a Bridge For Me…

  • _______________________________________________
    tor-commits mailing list -- tor-commits@xxxxxxxxxxxxxxxxxxxx
    To unsubscribe send an email to tor-commits-leave@xxxxxxxxxxxxxxxxxxxx