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

Commits:

3 changed files:

Changes:

  • browser/components/torpreferences/content/connectionPane.js
    ... ... @@ -346,7 +346,7 @@ const gBridgeGrid = {
    346 346
     
    
    347 347
         Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
    
    348 348
     
    
    349
    -    this._grid.classList.add("grid-active");
    
    349
    +    this._grid.hidden = false;
    
    350 350
     
    
    351 351
         this._updateConnectedBridge();
    
    352 352
       },
    
    ... ... @@ -363,7 +363,7 @@ const gBridgeGrid = {
    363 363
     
    
    364 364
         this._forceCloseRowMenus();
    
    365 365
     
    
    366
    -    this._grid.classList.remove("grid-active");
    
    366
    +    this._grid.hidden = true;
    
    367 367
     
    
    368 368
         Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
    
    369 369
       },
    
    ... ... @@ -1023,7 +1023,7 @@ const gBuiltinBridgesArea = {
    1023 1023
     
    
    1024 1024
         Services.obs.addObserver(this, TorProviderTopics.BridgeChanged);
    
    1025 1025
     
    
    1026
    -    this._area.classList.add("built-in-active");
    
    1026
    +    this._area.hidden = false;
    
    1027 1027
     
    
    1028 1028
         this._updateBridgeIds();
    
    1029 1029
         this._updateConnectedBridge();
    
    ... ... @@ -1038,7 +1038,7 @@ const gBuiltinBridgesArea = {
    1038 1038
         }
    
    1039 1039
         this._active = false;
    
    1040 1040
     
    
    1041
    -    this._area.classList.remove("built-in-active");
    
    1041
    +    this._area.hidden = true;
    
    1042 1042
     
    
    1043 1043
         Services.obs.removeObserver(this, TorProviderTopics.BridgeChanged);
    
    1044 1044
       },
    
    ... ... @@ -1243,11 +1243,19 @@ const gLoxStatus = {
    1243 1243
        */
    
    1244 1244
       _detailsArea: null,
    
    1245 1245
       /**
    
    1246
    -   * The day counter for the next unlock.
    
    1246
    +   * The list items showing the next unlocks.
    
    1247 1247
        *
    
    1248
    -   * @type {Element?}
    
    1248
    +   * @type {?Object<string, Element>}
    
    1249
    +   */
    
    1250
    +  _nextUnlockItems: null,
    
    1251
    +  /**
    
    1252
    +   * The day counter headings for the next unlock.
    
    1253
    +   *
    
    1254
    +   * One heading is shown during a search, the other is shown otherwise.
    
    1255
    +   *
    
    1256
    +   * @type {?Element[]}
    
    1249 1257
        */
    
    1250
    -  _nextUnlockCounterEl: null,
    
    1258
    +  _nextUnlockCounterEls: null,
    
    1251 1259
       /**
    
    1252 1260
        * Shows the number of remaining invites.
    
    1253 1261
        *
    
    ... ... @@ -1266,6 +1274,12 @@ const gLoxStatus = {
    1266 1274
        * @type {Element?}
    
    1267 1275
        */
    
    1268 1276
       _unlockAlert: null,
    
    1277
    +  /**
    
    1278
    +   * The list items showing the unlocks.
    
    1279
    +   *
    
    1280
    +   * @type {?Object<string, Element>}
    
    1281
    +   */
    
    1282
    +  _unlockItems: null,
    
    1269 1283
       /**
    
    1270 1284
        * The alert title.
    
    1271 1285
        *
    
    ... ... @@ -1296,8 +1310,19 @@ const gLoxStatus = {
    1296 1310
     
    
    1297 1311
         this._area = document.getElementById("tor-bridges-lox-status");
    
    1298 1312
         this._detailsArea = document.getElementById("tor-bridges-lox-details");
    
    1299
    -    this._nextUnlockCounterEl = document.getElementById(
    
    1300
    -      "tor-bridges-lox-next-unlock-counter"
    
    1313
    +    this._nextUnlockItems = {
    
    1314
    +      gainBridges: document.getElementById(
    
    1315
    +        "tor-bridges-lox-next-unlock-gain-bridges"
    
    1316
    +      ),
    
    1317
    +      firstInvites: document.getElementById(
    
    1318
    +        "tor-bridges-lox-next-unlock-first-invites"
    
    1319
    +      ),
    
    1320
    +      moreInvites: document.getElementById(
    
    1321
    +        "tor-bridges-lox-next-unlock-more-invites"
    
    1322
    +      ),
    
    1323
    +    };
    
    1324
    +    this._nextUnlockCounterEls = Array.from(
    
    1325
    +      document.querySelectorAll(".tor-bridges-lox-next-unlock-counter")
    
    1301 1326
         );
    
    1302 1327
         this._remainingInvitesEl = document.getElementById(
    
    1303 1328
           "tor-bridges-lox-remaining-invites"
    
    ... ... @@ -1306,6 +1331,15 @@ const gLoxStatus = {
    1306 1331
           "tor-bridges-lox-show-invites-button"
    
    1307 1332
         );
    
    1308 1333
         this._unlockAlert = document.getElementById("tor-bridges-lox-unlock-alert");
    
    1334
    +    this._unlockItems = {
    
    1335
    +      gainBridges: document.getElementById(
    
    1336
    +        "tor-bridges-lox-unlock-alert-gain-bridges"
    
    1337
    +      ),
    
    1338
    +      newBridges: document.getElementById(
    
    1339
    +        "tor-bridges-lox-unlock-alert-new-bridges"
    
    1340
    +      ),
    
    1341
    +      invites: document.getElementById("tor-bridges-lox-unlock-alert-invites"),
    
    1342
    +    };
    
    1309 1343
         this._unlockAlertTitle = document.getElementById(
    
    1310 1344
           "tor-bridge-unlock-alert-title"
    
    1311 1345
         );
    
    ... ... @@ -1404,6 +1438,7 @@ const gLoxStatus = {
    1404 1438
           return;
    
    1405 1439
         }
    
    1406 1440
         this._loxId = loxId;
    
    1441
    +    this._area.hidden = !loxId;
    
    1407 1442
         // We unset _nextUnlock to ensure the areas no longer use the old value for
    
    1408 1443
         // the new loxId.
    
    1409 1444
         this._updateNextUnlock(true);
    
    ... ... @@ -1518,8 +1553,8 @@ const gLoxStatus = {
    1518 1553
           // Uninitialized or no Lox source.
    
    1519 1554
           // NOTE: This area may already be hidden by the change in Lox source,
    
    1520 1555
           // but we clean up for the next non-empty id.
    
    1521
    -      this._area.classList.remove("show-unlock-alert");
    
    1522
    -      this._area.classList.remove("show-next-unlock");
    
    1556
    +      this._unlockAlert.hidden = true;
    
    1557
    +      this._detailsArea.hidden = true;
    
    1523 1558
           return;
    
    1524 1559
         }
    
    1525 1560
     
    
    ... ... @@ -1529,8 +1564,8 @@ const gLoxStatus = {
    1529 1564
     
    
    1530 1565
         const pendingEvents = this._pendingEvents;
    
    1531 1566
         const showAlert = !!pendingEvents.length;
    
    1532
    -    this._area.classList.toggle("show-unlock-alert", showAlert);
    
    1533
    -    this._area.classList.toggle("show-next-unlock", !showAlert);
    
    1567
    +    this._unlockAlert.hidden = !showAlert;
    
    1568
    +    this._detailsArea.hidden = showAlert;
    
    1534 1569
     
    
    1535 1570
         if (showAlert) {
    
    1536 1571
           // At level 0 and level 1, we do not have any invites.
    
    ... ... @@ -1567,6 +1602,7 @@ const gLoxStatus = {
    1567 1602
               blockage = true;
    
    1568 1603
             }
    
    1569 1604
           }
    
    1605
    +
    
    1570 1606
           let alertTitleId;
    
    1571 1607
           if (levelUp && !blockage) {
    
    1572 1608
             alertTitleId = "tor-bridges-lox-upgrade";
    
    ... ... @@ -1585,9 +1621,9 @@ const gLoxStatus = {
    1585 1621
             "lox-unlock-upgrade",
    
    1586 1622
             levelUp && !blockage
    
    1587 1623
           );
    
    1588
    -      this._unlockAlert.classList.toggle("lox-unlock-new-bridges", blockage);
    
    1589
    -      this._unlockAlert.classList.toggle("lox-unlock-gain-bridges", bridgeGain);
    
    1590
    -      this._unlockAlert.classList.toggle("lox-unlock-invites", showInvites);
    
    1624
    +      this._unlockItems.gainBridges.hidden = !bridgeGain;
    
    1625
    +      this._unlockItems.newBridges.hidden = !blockage;
    
    1626
    +      this._unlockItems.invites.hidden = !showInvites;
    
    1591 1627
         } else {
    
    1592 1628
           // Show next unlock.
    
    1593 1629
           // Number of days until the next unlock, rounded up.
    
    ... ... @@ -1598,29 +1634,42 @@ const gLoxStatus = {
    1598 1634
                 (24 * 60 * 60 * 1000)
    
    1599 1635
             )
    
    1600 1636
           );
    
    1601
    -      document.l10n.setAttributes(
    
    1602
    -        this._nextUnlockCounterEl,
    
    1603
    -        "tor-bridges-lox-days-until-unlock",
    
    1604
    -        { numDays }
    
    1605
    -      );
    
    1637
    +      for (const counterEl of this._nextUnlockCounterEls) {
    
    1638
    +        document.l10n.setAttributes(
    
    1639
    +          counterEl,
    
    1640
    +          "tor-bridges-lox-days-until-unlock",
    
    1641
    +          { numDays }
    
    1642
    +        );
    
    1643
    +      }
    
    1606 1644
     
    
    1607 1645
           // Gain 2 bridges from level 0 to 1. After that gain invites.
    
    1608
    -      const bridgeGain = this._nextUnlock.nextLevel === 1;
    
    1609
    -      const firstInvites = this._nextUnlock.nextLevel === 2;
    
    1610
    -      const moreInvites = this._nextUnlock.nextLevel > 2;
    
    1611
    -
    
    1612
    -      this._detailsArea.classList.toggle("lox-next-gain-bridges", bridgeGain);
    
    1613
    -      this._detailsArea.classList.toggle(
    
    1614
    -        "lox-next-first-invites",
    
    1615
    -        firstInvites
    
    1616
    -      );
    
    1617
    -      this._detailsArea.classList.toggle("lox-next-more-invites", moreInvites);
    
    1646
    +      this._nextUnlockItems.gainBridges.hidden =
    
    1647
    +        this._nextUnlock.nextLevel !== 1;
    
    1648
    +      this._nextUnlockItems.firstInvites.hidden =
    
    1649
    +        this._nextUnlock.nextLevel !== 2;
    
    1650
    +      this._nextUnlockItems.moreInvites.hidden =
    
    1651
    +        this._nextUnlock.nextLevel <= 2;
    
    1618 1652
         }
    
    1619 1653
     
    
    1620 1654
         if (alertHadFocus && !showAlert) {
    
    1621 1655
           // Alert has become hidden, move focus back up to the now revealed details
    
    1622 1656
           // area.
    
    1623
    -      this._nextUnlockCounterEl.focus();
    
    1657
    +      // NOTE: We have two headings: one shown during a search and one shown
    
    1658
    +      // otherwise. We focus the heading that is currently visible.
    
    1659
    +      // See tor-browser#43320.
    
    1660
    +      // TODO: It might be better if we could use the # named anchor to
    
    1661
    +      // re-orient the screen reader position instead of using tabIndex=-1, but
    
    1662
    +      // about:preferences currently uses the anchor for showing categories
    
    1663
    +      // only. See bugzilla bug 1799153.
    
    1664
    +      if (
    
    1665
    +        this._nextUnlockCounterEls[0].checkVisibility({
    
    1666
    +          visibilityProperty: true,
    
    1667
    +        })
    
    1668
    +      ) {
    
    1669
    +        this._nextUnlockCounterEls[0].focus();
    
    1670
    +      } else {
    
    1671
    +        this._nextUnlockCounterEls[1].focus();
    
    1672
    +      }
    
    1624 1673
         } else if (detailsHadFocus && showAlert) {
    
    1625 1674
           this._unlockAlertButton.focus();
    
    1626 1675
         }
    
    ... ... @@ -1642,21 +1691,21 @@ const gLoxStatus = {
    1642 1691
           hasInvites = this._haveExistingInvites || !!this._remainingInvites;
    
    1643 1692
         }
    
    1644 1693
     
    
    1645
    -    if (!hasInvites) {
    
    1646
    -      if (
    
    1647
    -        this._remainingInvitesEl.contains(document.activeElement) ||
    
    1648
    -        this._invitesButton.contains(document.activeElement)
    
    1649
    -      ) {
    
    1650
    -        // About to loose focus.
    
    1651
    -        // Unexpected for the lox level to loose all invites.
    
    1652
    -        // Move to the top of the details area, which should be visible if we
    
    1653
    -        // just had focus.
    
    1654
    -        this._nextUnlockCounterEl.focus();
    
    1655
    -      }
    
    1694
    +    if (
    
    1695
    +      !hasInvites &&
    
    1696
    +      (this._remainingInvitesEl.contains(document.activeElement) ||
    
    1697
    +        this._invitesButton.contains(document.activeElement))
    
    1698
    +    ) {
    
    1699
    +      // About to loose focus.
    
    1700
    +      // Unexpected for the lox level to loose all invites.
    
    1701
    +      // Move to the top of the details area, which should be visible if we
    
    1702
    +      // just had focus.
    
    1703
    +      this._nextUnlockCounterEl.focus();
    
    1656 1704
         }
    
    1657 1705
         // Hide the invite elements if we have no historic invites or a way of
    
    1658 1706
         // creating new ones.
    
    1659
    -    this._detailsArea.classList.toggle("lox-has-invites", hasInvites);
    
    1707
    +    this._remainingInvitesEl.hidden = !hasInvites;
    
    1708
    +    this._invitesButton.hidden = !hasInvites;
    
    1660 1709
     
    
    1661 1710
         if (hasInvites) {
    
    1662 1711
           document.l10n.setAttributes(
    
    ... ... @@ -1691,17 +1740,27 @@ const gBridgeSettings = {
    1691 1740
        */
    
    1692 1741
       _bridgesEl: null,
    
    1693 1742
       /**
    
    1694
    -   * The heading for the bridge settings.
    
    1743
    +   * The area for sharing bridge addresses.
    
    1695 1744
        *
    
    1696 1745
        * @type {Element?}
    
    1697 1746
        */
    
    1698
    -  _bridgesSettingsHeading: null,
    
    1747
    +  _shareEl: null,
    
    1748
    +  /**
    
    1749
    +   * The two headings for the bridge settings.
    
    1750
    +   *
    
    1751
    +   * One heading is shown during a search, the other is shown otherwise.
    
    1752
    +   *
    
    1753
    +   * @type {?Element[]}
    
    1754
    +   */
    
    1755
    +  _bridgesSettingsHeadings: null,
    
    1699 1756
       /**
    
    1700
    -   * The current bridges heading, at the start of the area.
    
    1757
    +   * The two headings for the current bridges, at the start of the area.
    
    1758
    +   *
    
    1759
    +   * One heading is shown during a search, the other is shown otherwise.
    
    1701 1760
        *
    
    1702 1761
        * @type {Element?}
    
    1703 1762
        */
    
    1704
    -  _currentBridgesHeading: null,
    
    1763
    +  _currentBridgesHeadings: null,
    
    1705 1764
       /**
    
    1706 1765
        * The area for showing no bridges.
    
    1707 1766
        *
    
    ... ... @@ -1709,17 +1768,25 @@ const gBridgeSettings = {
    1709 1768
        */
    
    1710 1769
       _noBridgesEl: null,
    
    1711 1770
       /**
    
    1712
    -   * The heading element for changing bridges.
    
    1771
    +   * The heading elements for changing bridges.
    
    1713 1772
        *
    
    1714
    -   * @type {Element?}
    
    1773
    +   * One heading is shown during a search, the other is shown otherwise.
    
    1774
    +   *
    
    1775
    +   * @type {?Element[]}
    
    1715 1776
        */
    
    1716
    -  _changeHeadingEl: null,
    
    1777
    +  _changeHeadingEls: null,
    
    1717 1778
       /**
    
    1718 1779
        * The button for user to provide a bridge address or share code.
    
    1719 1780
        *
    
    1720 1781
        * @type {Element?}
    
    1721 1782
        */
    
    1722 1783
       _userProvideButton: null,
    
    1784
    +  /**
    
    1785
    +   * A map from the bridge source to its corresponding label.
    
    1786
    +   *
    
    1787
    +   * @type {?Map<number, Element>}
    
    1788
    +   */
    
    1789
    +  _sourceLabels: null,
    
    1723 1790
     
    
    1724 1791
       /**
    
    1725 1792
        * Initialize the bridge settings.
    
    ... ... @@ -1727,15 +1794,33 @@ const gBridgeSettings = {
    1727 1794
       init() {
    
    1728 1795
         gBridgesNotification.init();
    
    1729 1796
     
    
    1730
    -    this._bridgesSettingsHeading = document.getElementById(
    
    1731
    -      "torPreferences-bridges-header"
    
    1797
    +    this._bridgesSettingsHeadings = Array.from(
    
    1798
    +      document.querySelectorAll(".tor-bridges-subcategory-heading")
    
    1732 1799
         );
    
    1733
    -    this._currentBridgesHeading = document.getElementById(
    
    1734
    -      "tor-bridges-current-heading"
    
    1800
    +    this._currentBridgesHeadings = Array.from(
    
    1801
    +      document.querySelectorAll(".tor-bridges-current-heading")
    
    1735 1802
         );
    
    1736 1803
         this._bridgesEl = document.getElementById("tor-bridges-current");
    
    1737 1804
         this._noBridgesEl = document.getElementById("tor-bridges-none");
    
    1738 1805
         this._groupEl = document.getElementById("torPreferences-bridges-group");
    
    1806
    +
    
    1807
    +    this._sourceLabels = new Map([
    
    1808
    +      [
    
    1809
    +        TorBridgeSource.BuiltIn,
    
    1810
    +        document.getElementById("tor-bridges-built-in-label"),
    
    1811
    +      ],
    
    1812
    +      [
    
    1813
    +        TorBridgeSource.UserProvided,
    
    1814
    +        document.getElementById("tor-bridges-user-label"),
    
    1815
    +      ],
    
    1816
    +      [
    
    1817
    +        TorBridgeSource.BridgeDB,
    
    1818
    +        document.getElementById("tor-bridges-requested-label"),
    
    1819
    +      ],
    
    1820
    +      [TorBridgeSource.Lox, document.getElementById("tor-bridges-lox-label")],
    
    1821
    +    ]);
    
    1822
    +    this._shareEl = document.getElementById("tor-bridges-share");
    
    1823
    +
    
    1739 1824
         this._toggleButton = document.getElementById("tor-bridges-enabled-toggle");
    
    1740 1825
         // Initially disabled whilst TorSettings may not be initialized.
    
    1741 1826
         this._toggleButton.disabled = true;
    
    ... ... @@ -1749,8 +1834,8 @@ const gBridgeSettings = {
    1749 1834
           });
    
    1750 1835
         });
    
    1751 1836
     
    
    1752
    -    this._changeHeadingEl = document.getElementById(
    
    1753
    -      "tor-bridges-change-heading"
    
    1837
    +    this._changeHeadingEls = Array.from(
    
    1838
    +      document.querySelectorAll(".tor-bridges-change-heading")
    
    1754 1839
         );
    
    1755 1840
         this._userProvideButton = document.getElementById(
    
    1756 1841
           "tor-bridges-open-user-provide-dialog-button"
    
    ... ... @@ -1855,6 +1940,12 @@ const gBridgeSettings = {
    1855 1940
        * @type {integer?}
    
    1856 1941
        */
    
    1857 1942
       _bridgeSource: null,
    
    1943
    +  /**
    
    1944
    +   * Whether the user is encouraged to share their bridge addresses.
    
    1945
    +   *
    
    1946
    +   * @type {boolean}
    
    1947
    +   */
    
    1948
    +  _canShare: false,
    
    1858 1949
     
    
    1859 1950
       /**
    
    1860 1951
        * Update _bridgeSource.
    
    ... ... @@ -1876,22 +1967,15 @@ const gBridgeSettings = {
    1876 1967
           this._bridgesEl.contains(document.activeElement) ||
    
    1877 1968
           this._noBridgesEl.contains(document.activeElement);
    
    1878 1969
     
    
    1879
    -    this._bridgesEl.classList.toggle(
    
    1880
    -      "source-built-in",
    
    1881
    -      bridgeSource === TorBridgeSource.BuiltIn
    
    1882
    -    );
    
    1883
    -    this._bridgesEl.classList.toggle(
    
    1884
    -      "source-user",
    
    1885
    -      bridgeSource === TorBridgeSource.UserProvided
    
    1886
    -    );
    
    1887
    -    this._bridgesEl.classList.toggle(
    
    1888
    -      "source-requested",
    
    1889
    -      bridgeSource === TorBridgeSource.BridgeDB
    
    1890
    -    );
    
    1891
    -    this._bridgesEl.classList.toggle(
    
    1892
    -      "source-lox",
    
    1893
    -      bridgeSource === TorBridgeSource.Lox
    
    1894
    -    );
    
    1970
    +    for (const [source, labelEl] of this._sourceLabels.entries()) {
    
    1971
    +      labelEl.hidden = source !== bridgeSource;
    
    1972
    +    }
    
    1973
    +
    
    1974
    +    this._canShare =
    
    1975
    +      bridgeSource === TorBridgeSource.UserProvided ||
    
    1976
    +      bridgeSource === TorBridgeSource.BridgeDB;
    
    1977
    +
    
    1978
    +    this._shareEl.hidden = !this._canShare;
    
    1895 1979
     
    
    1896 1980
         // Force the menu to close whenever the source changes.
    
    1897 1981
         // NOTE: If the menu had focus then hadFocus will be true, and focus will be
    
    ... ... @@ -1937,15 +2021,18 @@ const gBridgeSettings = {
    1937 2021
         // Add classes to show or hide the "no bridges" and "Your bridges" sections.
    
    1938 2022
         // NOTE: Before haveBridges is set, neither class is added, so both sections
    
    1939 2023
         // and hidden.
    
    1940
    -    this._groupEl.classList.toggle("no-bridges", !haveBridges);
    
    1941
    -    this._groupEl.classList.toggle("have-bridges", haveBridges);
    
    2024
    +    this._groupEl.classList.add("bridges-initialized");
    
    2025
    +    this._bridgesEl.hidden = !haveBridges;
    
    2026
    +    this._noBridgesEl.hidden = haveBridges;
    
    1942 2027
     
    
    1943
    -    document.l10n.setAttributes(
    
    1944
    -      this._changeHeadingEl,
    
    1945
    -      haveBridges
    
    1946
    -        ? "tor-bridges-replace-bridges-heading"
    
    1947
    -        : "tor-bridges-add-bridges-heading"
    
    1948
    -    );
    
    2028
    +    for (const headingEl of this._changeHeadingEls) {
    
    2029
    +      document.l10n.setAttributes(
    
    2030
    +        headingEl,
    
    2031
    +        haveBridges
    
    2032
    +          ? "tor-bridges-replace-bridges-heading"
    
    2033
    +          : "tor-bridges-add-bridges-heading"
    
    2034
    +      );
    
    2035
    +    }
    
    1949 2036
         document.l10n.setAttributes(
    
    1950 2037
           this._userProvideButton,
    
    1951 2038
           haveBridges ? "tor-bridges-replace-button" : "tor-bridges-add-new-button"
    
    ... ... @@ -1964,17 +2051,27 @@ const gBridgeSettings = {
    1964 2051
         }
    
    1965 2052
     
    
    1966 2053
         // Make sure we have the latest value for _haveBridges.
    
    1967
    -    // We also ensure that the _currentBridgesHeading element is visible before
    
    2054
    +    // We also ensure that the _currentBridgesHeadings element is visible before
    
    1968 2055
         // we focus it.
    
    1969 2056
         this._updateHaveBridges();
    
    1970
    -    if (this._haveBridges) {
    
    1971
    -      // Move focus to the start of the area, which is the heading.
    
    1972
    -      // It has tabindex="-1" so should be focusable, even though it is not part
    
    1973
    -      // of the usual tab navigation.
    
    1974
    -      this._currentBridgesHeading.focus();
    
    2057
    +
    
    2058
    +    // Move focus to the start of the relevant section, which is a heading.
    
    2059
    +    // They have tabindex="-1" so should be focusable, even though they are not
    
    2060
    +    // part of the usual tab navigation.
    
    2061
    +    // NOTE: We have two headings: one shown during a search and one shown
    
    2062
    +    // otherwise. We focus the heading that is currently visible.
    
    2063
    +    // See tor-browser#43320.
    
    2064
    +    // TODO: It might be better if we could use the # named anchor to
    
    2065
    +    // re-orient the screen reader position instead of using tabIndex=-1, but
    
    2066
    +    // about:preferences currently uses the anchor for showing categories
    
    2067
    +    // only. See bugzilla bug 1799153.
    
    2068
    +    const focusHeadings = this._haveBridges
    
    2069
    +      ? this._currentBridgesHeadings // The heading above the new bridges.
    
    2070
    +      : this._bridgesSettingsHeadings; // The top of the bridge settings.
    
    2071
    +    if (focusHeadings[0].checkVisibility({ visibilityProperty: true })) {
    
    2072
    +      focusHeadings[0].focus();
    
    1975 2073
         } else {
    
    1976
    -      // Move focus to the top of the bridge settings.
    
    1977
    -      this._bridgesSettingsHeading.focus();
    
    2074
    +      focusHeadings[1].focus();
    
    1978 2075
         }
    
    1979 2076
       },
    
    1980 2077
     
    
    ... ... @@ -2126,10 +2223,7 @@ const gBridgeSettings = {
    2126 2223
           });
    
    2127 2224
     
    
    2128 2225
         this._bridgesMenu.addEventListener("showing", () => {
    
    2129
    -      const canShare =
    
    2130
    -        this._bridgeSource === TorBridgeSource.UserProvided ||
    
    2131
    -        this._bridgeSource === TorBridgeSource.BridgeDB;
    
    2132
    -      qrItem.hidden = !canShare || !this._canQRBridges;
    
    2226
    +      qrItem.hidden = !this._canShare || !this._canQRBridges;
    
    2133 2227
           editItem.hidden = this._bridgeSource !== TorBridgeSource.UserProvided;
    
    2134 2228
         });
    
    2135 2229
     
    

  • browser/components/torpreferences/content/connectionPane.xhtml
    ... ... @@ -97,8 +97,8 @@
    97 97
       <!-- Bridges -->
    
    98 98
       <hbox class="subcategory" data-category="paneConnection" hidden="true">
    
    99 99
         <html:h1
    
    100
    -      id="torPreferences-bridges-header"
    
    101
    -      class="tor-focusable-heading"
    
    100
    +      id="tor-bridges-subcategory-heading-non-search"
    
    101
    +      class="tor-bridges-subcategory-heading tor-focusable-heading"
    
    102 102
           tabindex="-1"
    
    103 103
           data-l10n-id="tor-bridges-heading"
    
    104 104
         ></html:h1>
    
    ... ... @@ -107,7 +107,22 @@
    107 107
         id="torPreferences-bridges-group"
    
    108 108
         data-category="paneConnection"
    
    109 109
         hidden="true"
    
    110
    +    aria-labelledby="tor-bridges-subcategory-heading-non-search"
    
    110 111
       >
    
    112
    +    <!-- Add a search-header that only appears in search results as a substitute
    
    113
    +       - for the hidden h1 element. See tor-browser#43320.
    
    114
    +       - NOTE: Usually the first xul:label will act as the accessible name for
    
    115
    +       - a xul:groubbox element *if* it is not hidden. Since the search-header
    
    116
    +       - is sometimes hidden we need an explicit aria-labelledby anyway.
    
    117
    +       - However, we keep the wrapper xul:label for styling consistency with the
    
    118
    +       - other settings. -->
    
    119
    +    <label class="search-header" hidden="true">
    
    120
    +      <html:h2
    
    121
    +        class="tor-bridges-subcategory-heading tor-focusable-heading"
    
    122
    +        tabindex="-1"
    
    123
    +        data-l10n-id="tor-bridges-heading"
    
    124
    +      ></html:h2>
    
    125
    +    </label>
    
    111 126
         <description class="description-deemphasized" flex="1">
    
    112 127
           <html:span data-l10n-id="tor-bridges-overview"></html:span>
    
    113 128
           <label
    
    ... ... @@ -164,8 +179,13 @@
    164 179
            - See https://github.com/WICG/proposals/issues/112
    
    165 180
            -->
    
    166 181
         <!-- NOTE: This area is hidden by default, and is only shown temporarily
    
    167
    -       - when a notification is added. -->
    
    168
    -    <html:div id="tor-bridges-update-area" hidden="hidden">
    
    182
    +       - when a notification is added. It should never match with search
    
    183
    +       - queries. -->
    
    184
    +    <html:div
    
    185
    +      id="tor-bridges-update-area"
    
    186
    +      hidden="hidden"
    
    187
    +      data-hidden-from-search="true"
    
    188
    +    >
    
    169 189
           <!-- NOTE: This first span's text content will *not* be read out as part
    
    170 190
              - of the notification because it does not have an aria-live
    
    171 191
              - attribute. Instead it is just here to give context to the following
    
    ... ... @@ -182,18 +202,33 @@
    182 202
             aria-live="polite"
    
    183 203
           ></html:span>
    
    184 204
         </html:div>
    
    185
    -    <html:div id="tor-bridges-none">
    
    205
    +    <html:div id="tor-bridges-none" hidden="hidden">
    
    186 206
           <html:img id="tor-bridges-none-icon" alt="" />
    
    187 207
           <html:div data-l10n-id="tor-bridges-none-added"></html:div>
    
    188 208
         </html:div>
    
    189
    -    <html:div id="tor-bridges-current">
    
    209
    +    <html:div id="tor-bridges-current" hidden="hidden">
    
    190 210
           <html:div id="tor-bridges-current-header-bar">
    
    191 211
             <html:h2
    
    192
    -          id="tor-bridges-current-heading"
    
    193
    -          class="tor-focusable-heading"
    
    212
    +          id="tor-bridges-current-heading-non-search"
    
    213
    +          class="tor-bridges-current-heading tor-focusable-heading tor-small-heading tor-non-search-heading"
    
    194 214
               tabindex="-1"
    
    195 215
               data-l10n-id="tor-bridges-your-bridges"
    
    196 216
             ></html:h2>
    
    217
    +        <!-- Add a duplicate search heading.
    
    218
    +           - In a search result the heading h1.tor-bridges-subcategory-heading
    
    219
    +           - will be hidden, and the h2.tor-bridges-subcategory-heading
    
    220
    +           - will be visible.
    
    221
    +           - As such, all headings below h2.tor-bridges-subcategory-heading also
    
    222
    +           - need to shift one lower in heading level to preseve the correct
    
    223
    +           - hierarchy of - heading levels.
    
    224
    +           - In this case we hide the <h2> heading and show the duplicate <h3>
    
    225
    +           - heading instead.
    
    226
    +           - See tor-browser#43320. -->
    
    227
    +        <html:h3
    
    228
    +          class="tor-bridges-current-heading tor-focusable-heading tor-small-heading tor-search-heading"
    
    229
    +          tabindex="-1"
    
    230
    +          data-l10n-id="tor-bridges-your-bridges"
    
    231
    +        ></html:h3>
    
    197 232
             <html:span
    
    198 233
               id="tor-bridges-user-label"
    
    199 234
               class="tor-bridges-source-label"
    
    ... ... @@ -221,7 +256,10 @@
    221 256
               aria-controls="tor-bridges-all-options-menu"
    
    222 257
               data-l10n-id="tor-bridges-options-button"
    
    223 258
             ></html:button>
    
    224
    -        <html:panel-list id="tor-bridges-all-options-menu">
    
    259
    +        <html:panel-list
    
    260
    +          id="tor-bridges-all-options-menu"
    
    261
    +          data-hidden-from-search="true"
    
    262
    +        >
    
    225 263
               <html:panel-item
    
    226 264
                 id="tor-bridges-options-qr-all-menu-item"
    
    227 265
                 data-l10n-attrs="accesskey"
    
    ... ... @@ -244,7 +282,7 @@
    244 282
               ></html:panel-item>
    
    245 283
             </html:panel-list>
    
    246 284
           </html:div>
    
    247
    -      <html:div id="tor-bridges-built-in-display">
    
    285
    +      <html:div id="tor-bridges-built-in-display" hidden="hidden">
    
    248 286
             <html:div id="tor-bridges-built-in-type-name"></html:div>
    
    249 287
             <html:div
    
    250 288
               id="tor-bridges-built-in-connected"
    
    ... ... @@ -261,7 +299,8 @@
    261 299
             id="tor-bridges-grid-display"
    
    262 300
             class="tor-bridges-grid"
    
    263 301
             role="grid"
    
    264
    -        aria-labelledby="tor-bridges-current-heading"
    
    302
    +        aria-labelledby="tor-bridges-current-heading-non-search"
    
    303
    +        hidden="hidden"
    
    265 304
           ></html:div>
    
    266 305
           <html:template id="tor-bridges-grid-row-template">
    
    267 306
             <html:div class="tor-bridges-grid-row" role="row">
    
    ... ... @@ -297,7 +336,10 @@
    297 336
                     aria-expanded="false"
    
    298 337
                     data-l10n-id="tor-bridges-individual-bridge-options-button"
    
    299 338
                   ></html:button>
    
    300
    -              <html:panel-list class="tor-bridges-individual-options-menu">
    
    339
    +              <html:panel-list
    
    340
    +                class="tor-bridges-individual-options-menu"
    
    341
    +                data-hidden-from-search="true"
    
    342
    +              >
    
    301 343
                     <html:panel-item
    
    302 344
                       class="tor-bridges-options-qr-one-menu-item"
    
    303 345
                       data-l10n-attrs="accesskey"
    
    ... ... @@ -318,11 +360,20 @@
    318 360
               </html:span>
    
    319 361
             </html:div>
    
    320 362
           </html:template>
    
    321
    -      <html:div id="tor-bridges-share" class="tor-bridges-details-box">
    
    363
    +      <html:div
    
    364
    +        id="tor-bridges-share"
    
    365
    +        class="tor-bridges-details-box"
    
    366
    +        hidden="hidden"
    
    367
    +      >
    
    322 368
             <html:h3
    
    323
    -          id="tor-bridges-share-heading"
    
    369
    +          class="tor-bridges-share-heading tor-small-heading tor-non-search-heading"
    
    324 370
               data-l10n-id="tor-bridges-share-heading"
    
    325 371
             ></html:h3>
    
    372
    +        <!-- Add a duplicate search heading. See tor-browser#43320. -->
    
    373
    +        <html:h4
    
    374
    +          class="tor-bridges-share-heading tor-small-heading tor-search-heading"
    
    375
    +          data-l10n-id="tor-bridges-share-heading"
    
    376
    +        ></html:h4>
    
    326 377
             <html:span
    
    327 378
               id="tor-bridges-share-description"
    
    328 379
               data-l10n-id="tor-bridges-share-description"
    
    ... ... @@ -336,68 +387,84 @@
    336 387
               data-l10n-id="tor-bridges-qr-addresses-button"
    
    337 388
             ></html:button>
    
    338 389
           </html:div>
    
    339
    -      <html:div id="tor-bridges-lox-status">
    
    390
    +      <html:div id="tor-bridges-lox-status" hidden="hidden">
    
    340 391
             <html:div data-l10n-id="tor-bridges-lox-description"></html:div>
    
    341 392
             <html:div
    
    342 393
               id="tor-bridges-lox-details"
    
    343 394
               class="tor-bridges-details-box tor-bridges-lox-box"
    
    395
    +          hidden="hidden"
    
    344 396
             >
    
    345 397
               <html:img alt="" class="tor-bridges-lox-image-inner" />
    
    346 398
               <html:img alt="" class="tor-bridges-lox-image-outer" />
    
    347 399
               <html:h3
    
    348
    -            id="tor-bridges-lox-next-unlock-counter"
    
    349
    -            class="tor-bridges-lox-intro tor-focusable-heading"
    
    400
    +            class="tor-bridges-lox-next-unlock-counter tor-small-heading tor-bridges-lox-intro tor-focusable-heading tor-non-search-heading"
    
    350 401
                 tabindex="-1"
    
    351 402
               ></html:h3>
    
    403
    +          <!-- Add a duplicate search heading. See tor-browser#43320. -->
    
    404
    +          <html:h4
    
    405
    +            class="tor-bridges-lox-next-unlock-counter tor-small-heading tor-bridges-lox-intro tor-focusable-heading tor-search-heading"
    
    406
    +            tabindex="-1"
    
    407
    +          ></html:h4>
    
    352 408
               <html:ul class="tor-bridges-lox-list">
    
    353 409
                 <html:li
    
    354 410
                   id="tor-bridges-lox-next-unlock-gain-bridges"
    
    355 411
                   class="tor-bridges-lox-list-item tor-bridges-lox-list-item-bridge"
    
    356 412
                   data-l10n-id="tor-bridges-lox-unlock-two-bridges"
    
    413
    +              hidden="hidden"
    
    357 414
                 ></html:li>
    
    358 415
                 <html:li
    
    359 416
                   id="tor-bridges-lox-next-unlock-first-invites"
    
    360 417
                   class="tor-bridges-lox-list-item tor-bridges-lox-list-item-invite"
    
    361 418
                   data-l10n-id="tor-bridges-lox-unlock-first-invites"
    
    419
    +              hidden="hidden"
    
    362 420
                 ></html:li>
    
    363 421
                 <html:li
    
    364 422
                   id="tor-bridges-lox-next-unlock-more-invites"
    
    365 423
                   class="tor-bridges-lox-list-item tor-bridges-lox-list-item-invite"
    
    366 424
                   data-l10n-id="tor-bridges-lox-unlock-more-invites"
    
    425
    +              hidden="hidden"
    
    367 426
                 ></html:li>
    
    368 427
               </html:ul>
    
    369
    -          <html:div id="tor-bridges-lox-remaining-invites"></html:div>
    
    428
    +          <html:div
    
    429
    +            id="tor-bridges-lox-remaining-invites"
    
    430
    +            hidden="hidden"
    
    431
    +          ></html:div>
    
    370 432
               <html:button
    
    371 433
                 id="tor-bridges-lox-show-invites-button"
    
    372 434
                 class="tor-bridges-lox-button"
    
    373 435
                 data-l10n-id="tor-bridges-lox-show-invites-button"
    
    436
    +            hidden="hidden"
    
    374 437
               ></html:button>
    
    375 438
             </html:div>
    
    376 439
             <html:div
    
    377 440
               id="tor-bridges-lox-unlock-alert"
    
    378 441
               role="alert"
    
    379 442
               class="tor-bridges-details-box tor-bridges-lox-box"
    
    443
    +          hidden="hidden"
    
    380 444
             >
    
    381 445
               <html:img alt="" class="tor-bridges-lox-image-inner" />
    
    382 446
               <html:img alt="" class="tor-bridges-lox-image-outer" />
    
    383 447
               <html:div
    
    384 448
                 id="tor-bridge-unlock-alert-title"
    
    385
    -            class="tor-bridges-lox-intro"
    
    449
    +            class="tor-small-heading tor-bridges-lox-intro"
    
    386 450
               ></html:div>
    
    387 451
               <html:ul class="tor-bridges-lox-list">
    
    388 452
                 <html:li
    
    389 453
                   id="tor-bridges-lox-unlock-alert-gain-bridges"
    
    390 454
                   class="tor-bridges-lox-list-item tor-bridges-lox-list-item-bridge"
    
    391 455
                   data-l10n-id="tor-bridges-lox-gained-two-bridges"
    
    456
    +              hidden="hidden"
    
    392 457
                 ></html:li>
    
    393 458
                 <html:li
    
    394 459
                   id="tor-bridges-lox-unlock-alert-new-bridges"
    
    395 460
                   class="tor-bridges-lox-list-item tor-bridges-lox-list-item-bridge"
    
    396 461
                   data-l10n-id="tor-bridges-lox-new-bridges"
    
    462
    +              hidden="hidden"
    
    397 463
                 ></html:li>
    
    398 464
                 <html:li
    
    399 465
                   id="tor-bridges-lox-unlock-alert-invites"
    
    400 466
                   class="tor-bridges-lox-list-item tor-bridges-lox-list-item-invite"
    
    467
    +              hidden="hidden"
    
    401 468
                 ></html:li>
    
    402 469
               </html:ul>
    
    403 470
               <html:button
    
    ... ... @@ -408,7 +475,14 @@
    408 475
             </html:div>
    
    409 476
           </html:div>
    
    410 477
         </html:div>
    
    411
    -    <html:h2 id="tor-bridges-change-heading"></html:h2>
    
    478
    +    <html:h2
    
    479
    +      class="tor-bridges-change-heading tor-medium-heading tor-non-search-heading"
    
    480
    +    ></html:h2>
    
    481
    +    <!-- Add a duplicate search heading. See tor-browser#43320.
    
    482
    +       - This has the same content, but a smaller font. -->
    
    483
    +    <html:h3
    
    484
    +      class="tor-bridges-change-heading tor-small-heading tor-search-heading"
    
    485
    +    ></html:h3>
    
    412 486
         <hbox align="center">
    
    413 487
           <description
    
    414 488
             flex="1"
    
    ... ... @@ -428,9 +502,15 @@
    428 502
           ></html:button>
    
    429 503
         </hbox>
    
    430 504
         <html:h3
    
    431
    -      id="tor-bridges-provider-heading"
    
    505
    +      class="tor-bridges-provider-heading tor-medium-heading tor-non-search-heading"
    
    432 506
           data-l10n-id="tor-bridges-find-more-heading"
    
    433 507
         ></html:h3>
    
    508
    +    <!-- Add a duplicate search heading. See tor-browser#43320.
    
    509
    +       - This has the same content, but a smaller font. -->
    
    510
    +    <html:h4
    
    511
    +      class="tor-bridges-provider-heading tor-small-heading tor-search-heading"
    
    512
    +      data-l10n-id="tor-bridges-find-more-heading"
    
    513
    +    ></html:h4>
    
    434 514
         <description
    
    435 515
           data-l10n-id="tor-bridges-find-more-description"
    
    436 516
           class="description-deemphasized"
    
    ... ... @@ -511,15 +591,29 @@
    511 591
     
    
    512 592
       <!-- Advanced -->
    
    513 593
       <hbox class="subcategory" data-category="paneConnection" hidden="true">
    
    514
    -    <html:h1 data-l10n-id="tor-advanced-settings-heading"></html:h1>
    
    594
    +    <html:h1
    
    595
    +      id="tor-advanced-subcategory-heading-non-search"
    
    596
    +      data-l10n-id="tor-advanced-settings-heading"
    
    597
    +    ></html:h1>
    
    515 598
       </hbox>
    
    516 599
       <groupbox
    
    517 600
         id="torPreferences-advanced-group"
    
    518 601
         data-category="paneConnection"
    
    519 602
         hidden="true"
    
    603
    +    aria-labelledby="tor-advanced-subcategory-heading-non-search"
    
    520 604
       >
    
    605
    +    <!-- Add a search-header that only appears in search results as a substitute
    
    606
    +       - for the hidden h1 element. See tor-browser#43320.
    
    607
    +       - NOTE: Usually the first xul:label will act as the accessible name for
    
    608
    +       - a xul:groubbox element *if* it is not hidden. Since the search-header
    
    609
    +       - is sometimes hidden we need an explicit aria-labelledby anyway.
    
    610
    +       - However, we keep the wrapper xul:label for styling consistency with the
    
    611
    +       - other settings. -->
    
    612
    +    <label class="search-header" hidden="true">
    
    613
    +      <html:h2 data-l10n-id="tor-advanced-settings-heading"></html:h2>
    
    614
    +    </label>
    
    521 615
         <hbox align="center">
    
    522
    -      <label data-l10n-id="tor-advanced-settings-description" flex="1" />
    
    616
    +      <description data-l10n-id="tor-advanced-settings-description" flex="1" />
    
    523 617
           <html:button
    
    524 618
             id="torPreferences-advanced-button"
    
    525 619
             class="accessory-button"
    
    ... ... @@ -527,7 +621,7 @@
    527 621
           ></html:button>
    
    528 622
         </hbox>
    
    529 623
         <hbox align="center" data-subcategory="viewlogs">
    
    530
    -      <label data-l10n-id="tor-view-log-description" flex="1" />
    
    624
    +      <description data-l10n-id="tor-view-log-description" flex="1" />
    
    531 625
           <html:button
    
    532 626
             id="torPreferences-buttonTorLogs"
    
    533 627
             class="accessory-button"
    

  • browser/components/torpreferences/content/torPreferences.css
    ... ... @@ -118,6 +118,48 @@ button.spoof-button-disabled {
    118 118
     
    
    119 119
     /* Bridge settings */
    
    120 120
     
    
    121
    +.tor-medium-heading {
    
    122
    +  /* Same font size as mozilla preferences h2. */
    
    123
    +  font-size: var(--font-size-large);
    
    124
    +  font-weight: var(--font-weight-bold);
    
    125
    +  margin: 0;
    
    126
    +}
    
    127
    +
    
    128
    +.tor-small-heading {
    
    129
    +  font-size: inherit;
    
    130
    +  font-weight: var(--font-weight-bold);
    
    131
    +  margin: 0;
    
    132
    +}
    
    133
    +
    
    134
    +/* Hide the tor-search-heading elements when the group's search header is
    
    135
    + * hidden. These only appear in search results.
    
    136
    + * See tor-browser#43320.
    
    137
    + * NOTE: `.search-header[hidden] ~ :is(* .tor-search-heading)` will not match
    
    138
    + * (possibly because the `~` selector is unsure how to integrate with the
    
    139
    + * non-compound `* .tor-search-heading` selector). So we need to duplicate the
    
    140
    + * `.search-header[hidden]` rule. */
    
    141
    +#torPreferences-bridges-group :is(
    
    142
    +  .search-header[hidden] ~ * .tor-search-heading,
    
    143
    +  .search-header[hidden] ~ .tor-search-heading
    
    144
    +) {
    
    145
    +  display: none;
    
    146
    +}
    
    147
    +
    
    148
    +/* Hide the tor-non-search-heading elements when the group's search header is
    
    149
    + * not hidden. These only appear outside of search results.
    
    150
    + * See tor-browser#43320. */
    
    151
    +#torPreferences-bridges-group :is(
    
    152
    +  .search-header:not([hidden]) ~ * .tor-non-search-heading,
    
    153
    +  .search-header:not([hidden]) ~ .tor-non-search-heading
    
    154
    +) {
    
    155
    +  display: none;
    
    156
    +}
    
    157
    +
    
    158
    +.tor-focusable-heading {
    
    159
    +  /* Do not occupy more horizontal space than necessary. */
    
    160
    +  width: fit-content;
    
    161
    +}
    
    162
    +
    
    121 163
     .tor-focusable-heading:focus-visible {
    
    122 164
       outline-offset: var(--in-content-focus-outline-offset);
    
    123 165
     }
    
    ... ... @@ -182,53 +224,18 @@ button.spoof-button-disabled {
    182 224
       clip-path: inset(50%);
    
    183 225
     }
    
    184 226
     
    
    185
    -#torPreferences-bridges-group:not(.have-bridges, .no-bridges) {
    
    227
    +#torPreferences-bridges-group:not(.bridges-initialized) {
    
    186 228
       /* Hide bridge settings whilst not initialized. */
    
    187 229
       display: none;
    
    188 230
     }
    
    189 231
     
    
    190
    -#torPreferences-bridges-group:not(.have-bridges) #tor-bridges-current {
    
    191
    -  display: none;
    
    192
    -}
    
    193
    -
    
    194
    -#torPreferences-bridges-group:not(.no-bridges) #tor-bridges-none {
    
    195
    -  display: none;
    
    196
    -}
    
    197
    -
    
    198
    -#tor-bridges-current:not(.source-built-in) #tor-bridges-built-in-label {
    
    199
    -  display: none;
    
    200
    -}
    
    201
    -
    
    202
    -#tor-bridges-current:not(.source-user) #tor-bridges-user-label {
    
    203
    -  display: none;
    
    204
    -}
    
    205
    -
    
    206
    -#tor-bridges-current:not(.source-requested) #tor-bridges-requested-label {
    
    207
    -  display: none;
    
    208
    -}
    
    209
    -
    
    210
    -#tor-bridges-current:not(.source-lox) #tor-bridges-lox-label {
    
    211
    -  display: none;
    
    212
    -}
    
    213
    -
    
    214
    -#tor-bridges-current:not(
    
    215
    -  .source-user,
    
    216
    -  .source-requested
    
    217
    -) #tor-bridges-share {
    
    218
    -  display: none;
    
    219
    -}
    
    220
    -
    
    221
    -#tor-bridges-current:not(.source-lox) #tor-bridges-lox-status {
    
    222
    -  display: none;
    
    223
    -}
    
    224
    -
    
    225 232
     #tor-bridges-none,
    
    226 233
     #tor-bridges-current {
    
    227 234
       margin-inline: 0;
    
    228 235
       margin-block: 32px;
    
    229 236
     }
    
    230 237
     
    
    231
    -#tor-bridges-none {
    
    238
    +#tor-bridges-none:not([hidden]) {
    
    232 239
       display: grid;
    
    233 240
       justify-items: center;
    
    234 241
       text-align: center;
    
    ... ... @@ -265,9 +272,7 @@ button.spoof-button-disabled {
    265 272
       white-space: nowrap;
    
    266 273
     }
    
    267 274
     
    
    268
    -#tor-bridges-current-heading {
    
    269
    -  margin: 0;
    
    270
    -  font-size: inherit;
    
    275
    +.tor-bridges-current-heading {
    
    271 276
       grid-area: heading;
    
    272 277
     }
    
    273 278
     
    
    ... ... @@ -282,7 +287,7 @@ button.spoof-button-disabled {
    282 287
       grid-area: button;
    
    283 288
     }
    
    284 289
     
    
    285
    -#tor-bridges-lox-label {
    
    290
    +#tor-bridges-lox-label:not([hidden]) {
    
    286 291
       display: flex;
    
    287 292
       align-items: center;
    
    288 293
       gap: 6px;
    
    ... ... @@ -317,7 +322,7 @@ button.spoof-button-disabled {
    317 322
       fill: currentColor;
    
    318 323
     }
    
    319 324
     
    
    320
    -#tor-bridges-built-in-display {
    
    325
    +#tor-bridges-built-in-display:not([hidden]) {
    
    321 326
       display: grid;
    
    322 327
       grid-template:
    
    323 328
         "type status" min-content
    
    ... ... @@ -327,10 +332,6 @@ button.spoof-button-disabled {
    327 332
       margin-block-end: 16px;
    
    328 333
     }
    
    329 334
     
    
    330
    -#tor-bridges-built-in-display:not(.built-in-active) {
    
    331
    -  display: none;
    
    332
    -}
    
    333
    -
    
    334 335
     #tor-bridges-built-in-type-name {
    
    335 336
       font-weight: 700;
    
    336 337
       grid-area: type;
    
    ... ... @@ -345,7 +346,7 @@ button.spoof-button-disabled {
    345 346
       grid-area: description;
    
    346 347
     }
    
    347 348
     
    
    348
    -.tor-bridges-grid {
    
    349
    +.tor-bridges-grid:not([hidden]) {
    
    349 350
       display: grid;
    
    350 351
       grid-template-columns: max-content repeat(4, max-content) 1fr;
    
    351 352
       --tor-bridges-grid-column-gap: 8px;
    
    ... ... @@ -355,10 +356,6 @@ button.spoof-button-disabled {
    355 356
       align-items: stretch;
    
    356 357
     }
    
    357 358
     
    
    358
    -#tor-bridges-grid-display:not(.grid-active) {
    
    359
    -  display: none;
    
    360
    -}
    
    361
    -
    
    362 359
     .tor-bridges-grid-row {
    
    363 360
       /* We want each row to act as a row of three items in the
    
    364 361
        * .tor-bridges-grid grid layout.
    
    ... ... @@ -483,7 +480,7 @@ button.spoof-button-disabled {
    483 480
       padding: 16px;
    
    484 481
     }
    
    485 482
     
    
    486
    -#tor-bridges-share {
    
    483
    +#tor-bridges-share:not([hidden]) {
    
    487 484
       display: grid;
    
    488 485
       grid-template:
    
    489 486
         "heading heading heading" min-content
    
    ... ... @@ -496,12 +493,9 @@ button.spoof-button-disabled {
    496 493
       align-items: center;
    
    497 494
     }
    
    498 495
     
    
    499
    -#tor-bridges-share-heading {
    
    496
    +.tor-bridges-share-heading {
    
    500 497
       grid-area: heading;
    
    501
    -  font-size: inherit;
    
    502
    -  margin: 0;
    
    503 498
       margin-block-end: 4px;
    
    504
    -  font-weight: 700;
    
    505 499
     }
    
    506 500
     
    
    507 501
     #tor-bridges-share-description {
    
    ... ... @@ -538,15 +532,7 @@ button.spoof-button-disabled {
    538 532
       margin-block-start: 8px;
    
    539 533
     }
    
    540 534
     
    
    541
    -#tor-bridges-lox-status:not(.show-next-unlock) #tor-bridges-lox-details {
    
    542
    -  display: none;
    
    543
    -}
    
    544
    -
    
    545
    -#tor-bridges-lox-status:not(.show-unlock-alert) #tor-bridges-lox-unlock-alert {
    
    546
    -  display: none;
    
    547
    -}
    
    548
    -
    
    549
    -.tor-bridges-lox-box {
    
    535
    +.tor-bridges-lox-box:not([hidden]) {
    
    550 536
       display: grid;
    
    551 537
       grid-template:
    
    552 538
         "image intro intro" min-content
    
    ... ... @@ -601,11 +587,8 @@ button.spoof-button-disabled {
    601 587
     
    
    602 588
     .tor-bridges-lox-intro {
    
    603 589
       grid-area: intro;
    
    604
    -  font-weight: 700;
    
    605
    -  font-size: inherit;
    
    606 590
       align-self: center;
    
    607 591
       justify-self: start;
    
    608
    -  margin: 0;
    
    609 592
     }
    
    610 593
     
    
    611 594
     .tor-bridges-lox-list {
    
    ... ... @@ -619,11 +602,11 @@ button.spoof-button-disabled {
    619 602
       gap: 8px 0;
    
    620 603
     }
    
    621 604
     
    
    622
    -.tor-bridges-lox-list-item {
    
    605
    +.tor-bridges-lox-list-item:not([hidden]) {
    
    623 606
       display: contents;
    
    624 607
     }
    
    625 608
     
    
    626
    -.tor-bridges-lox-list-item::before {
    
    609
    +.tor-bridges-lox-list-item:not([hidden])::before {
    
    627 610
       /* We use ::before rather than list-style-image to have more control. */
    
    628 611
       box-sizing: content-box;
    
    629 612
       width: 18px;
    
    ... ... @@ -655,52 +638,19 @@ button.spoof-button-disabled {
    655 638
       stroke: var(--in-content-success-icon-color);
    
    656 639
     }
    
    657 640
     
    
    658
    -#tor-bridges-lox-details:not(.lox-next-gain-bridges) #tor-bridges-lox-next-unlock-gain-bridges {
    
    659
    -  display: none;
    
    660
    -}
    
    661
    -
    
    662
    -#tor-bridges-lox-details:not(.lox-next-first-invites) #tor-bridges-lox-next-unlock-first-invites {
    
    663
    -  display: none;
    
    664
    -}
    
    665
    -
    
    666
    -#tor-bridges-lox-details:not(.lox-next-more-invites) #tor-bridges-lox-next-unlock-more-invites {
    
    667
    -  display: none;
    
    668
    -}
    
    669
    -
    
    670
    -
    
    671
    -#tor-bridges-lox-unlock-alert:not(.lox-unlock-gain-bridges) #tor-bridges-lox-unlock-alert-gain-bridges {
    
    672
    -  display: none;
    
    673
    -}
    
    674
    -
    
    675
    -#tor-bridges-lox-unlock-alert:not(.lox-unlock-new-bridges) #tor-bridges-lox-unlock-alert-new-bridges {
    
    676
    -  display: none;
    
    677
    -}
    
    678
    -
    
    679
    -#tor-bridges-lox-unlock-alert:not(.lox-unlock-invites) #tor-bridges-lox-unlock-alert-invites {
    
    680
    -  display: none;
    
    681
    -}
    
    682
    -
    
    683 641
     #tor-bridges-lox-remaining-invites {
    
    684 642
       grid-area: invites;
    
    685 643
       justify-self: end;
    
    686 644
       align-self: center;
    
    687 645
     }
    
    688 646
     
    
    689
    -#tor-bridges-lox-details:not(.lox-has-invites) :is(
    
    690
    -  #tor-bridges-lox-remaining-invites,
    
    691
    -  #tor-bridges-lox-show-invites-button
    
    692
    -) {
    
    693
    -  display: none;
    
    694
    -}
    
    695
    -
    
    696 647
     .tor-bridges-lox-button {
    
    697 648
       grid-area: button;
    
    698 649
       margin: 0;
    
    699 650
       align-self: center;
    
    700 651
     }
    
    701 652
     
    
    702
    -#tor-bridges-provider-heading {
    
    703
    -  font-size: 1.14em;
    
    653
    +.tor-bridges-provider-heading {
    
    704 654
       margin-block: 48px 8px;
    
    705 655
     }
    
    706 656