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

Commits:

9 changed files:

Changes:

  • browser/app/profile/001-base-profile.js
    ... ... @@ -220,6 +220,10 @@ pref("privacy.annotate_channels.strict_list.enabled", false);
    220 220
     // Disable the Pocket extension (Bug #18886 and #31602)
    
    221 221
     pref("extensions.pocket.enabled", false);
    
    222 222
     
    
    223
    +// Custom extensions preferences tor-browser#41581
    
    224
    +pref("extensions.hideNoScript", true);
    
    225
    +pref("extensions.hideUnifiedWhenEmpty", true);
    
    226
    +
    
    223 227
     // Disable activity stream/"Recommended by Pocket" in about:home (Bug #41029)
    
    224 228
     pref("browser.newtabpage.activity-stream.discoverystream.enabled", false);
    
    225 229
     pref("browser.newtabpage.activity-stream.feeds.section.topstories", false);
    

  • browser/base/content/browser-addons.js
    ... ... @@ -26,6 +26,9 @@ XPCOMUtils.defineLazyGetter(lazy, "l10n", function () {
    26 26
       );
    
    27 27
     });
    
    28 28
     
    
    29
    +const HIDE_NO_SCRIPT_PREF = "extensions.hideNoScript";
    
    30
    +const HIDE_UNIFIED_WHEN_EMPTY_PREF = "extensions.hideUnifiedWhenEmpty";
    
    31
    +
    
    29 32
     /**
    
    30 33
      * Mapping of error code -> [error-id, local-error-id]
    
    31 34
      *
    
    ... ... @@ -1186,6 +1189,18 @@ var gUnifiedExtensions = {
    1186 1189
         gNavToolbox.addEventListener("customizationstarting", this);
    
    1187 1190
         CustomizableUI.addListener(this);
    
    1188 1191
     
    
    1192
    +    // Listen out for changes in extensions.hideNoScript and
    
    1193
    +    // extension.hideUnifiedWhenEmpty, which can effect the visibility of the
    
    1194
    +    // unified-extensions-button.
    
    1195
    +    // See tor-browser#41581.
    
    1196
    +    this._hideNoScriptObserver = () => this._updateVisibility();
    
    1197
    +    Services.prefs.addObserver(HIDE_NO_SCRIPT_PREF, this._hideNoScriptObserver);
    
    1198
    +    Services.prefs.addObserver(
    
    1199
    +      HIDE_UNIFIED_WHEN_EMPTY_PREF,
    
    1200
    +      this._hideNoScriptObserver
    
    1201
    +    );
    
    1202
    +    this._updateVisibility();
    
    1203
    +
    
    1189 1204
         this._initialized = true;
    
    1190 1205
       },
    
    1191 1206
     
    
    ... ... @@ -1201,6 +1216,15 @@ var gUnifiedExtensions = {
    1201 1216
     
    
    1202 1217
         gNavToolbox.removeEventListener("customizationstarting", this);
    
    1203 1218
         CustomizableUI.removeListener(this);
    
    1219
    +
    
    1220
    +    Services.prefs.removeObserver(
    
    1221
    +      HIDE_NO_SCRIPT_PREF,
    
    1222
    +      this._hideNoScriptObserver
    
    1223
    +    );
    
    1224
    +    Services.prefs.removeObserver(
    
    1225
    +      HIDE_UNIFIED_WHEN_EMPTY_PREF,
    
    1226
    +      this._hideNoScriptObserver
    
    1227
    +    );
    
    1204 1228
       },
    
    1205 1229
     
    
    1206 1230
       onLocationChange(browser, webProgress, _request, _uri, flags) {
    
    ... ... @@ -1278,6 +1302,15 @@ var gUnifiedExtensions = {
    1278 1302
             return false;
    
    1279 1303
           }
    
    1280 1304
     
    
    1305
    +      // When an extensions is about to be removed, it may still appear in
    
    1306
    +      // getActiveExtensions.
    
    1307
    +      // This is needed for hasExtensionsInPanel, when called through
    
    1308
    +      // onWidgetDestroy when an extension is being removed.
    
    1309
    +      // See tor-browser#41581.
    
    1310
    +      if (extension.hasShutdown) {
    
    1311
    +        return false;
    
    1312
    +      }
    
    1313
    +
    
    1281 1314
           // Ignore hidden and extensions that cannot access the current window
    
    1282 1315
           // (because of PB mode when we are in a private window), since users
    
    1283 1316
           // cannot do anything with those extensions anyway.
    
    ... ... @@ -1292,6 +1325,20 @@ var gUnifiedExtensions = {
    1292 1325
         return policies;
    
    1293 1326
       },
    
    1294 1327
     
    
    1328
    +  /**
    
    1329
    +   * Potentially hide the unified-extensions-button if it would be empty.
    
    1330
    +   */
    
    1331
    +  // See tor-browser#41581.
    
    1332
    +  // The behaviour overlaps with a proposal in mozilla Bug 1778684, which has
    
    1333
    +  // not been implemented, or had much recent activity as of 5th October 2023.
    
    1334
    +  _updateVisibility() {
    
    1335
    +    this.button.classList.toggle(
    
    1336
    +      "hide-empty",
    
    1337
    +      Services.prefs.getBoolPref(HIDE_UNIFIED_WHEN_EMPTY_PREF, true) &&
    
    1338
    +        !this.hasExtensionsInPanel()
    
    1339
    +    );
    
    1340
    +  },
    
    1341
    +
    
    1295 1342
       /**
    
    1296 1343
        * Returns true when there are active extensions listed/shown in the unified
    
    1297 1344
        * extensions panel, and false otherwise (e.g. when extensions are pinned in
    
    ... ... @@ -1300,7 +1347,13 @@ var gUnifiedExtensions = {
    1300 1347
        * @returns {boolean} Whether there are extensions listed in the panel.
    
    1301 1348
        */
    
    1302 1349
       hasExtensionsInPanel() {
    
    1303
    -    const policies = this.getActivePolicies();
    
    1350
    +    let policies = this.getActivePolicies();
    
    1351
    +    // If the NoScript button is hidden, we won't count it towards the list of
    
    1352
    +    // extensions in the panel.
    
    1353
    +    // See tor-browser#41581.
    
    1354
    +    if (Services.prefs.getBoolPref(HIDE_NO_SCRIPT_PREF, true)) {
    
    1355
    +      policies = policies.filter(policy => !policy.extension?.isNoScript);
    
    1356
    +    }
    
    1304 1357
     
    
    1305 1358
         return !!policies
    
    1306 1359
           .map(policy => this.browserActionFor(policy)?.widget)
    
    ... ... @@ -1795,7 +1848,17 @@ var gUnifiedExtensions = {
    1795 1848
         }
    
    1796 1849
       },
    
    1797 1850
     
    
    1851
    +  onWidgetRemoved() {
    
    1852
    +    this._updateVisibility();
    
    1853
    +  },
    
    1854
    +
    
    1855
    +  onWidgetDestroyed() {
    
    1856
    +    this._updateVisibility();
    
    1857
    +  },
    
    1858
    +
    
    1798 1859
       onWidgetAdded(aWidgetId, aArea, aPosition) {
    
    1860
    +    this._updateVisibility();
    
    1861
    +
    
    1799 1862
         // When we pin a widget to the toolbar from a narrow window, the widget
    
    1800 1863
         // will be overflowed directly. In this case, we do not want to change the
    
    1801 1864
         // class name since it is going to be changed by `onWidgetOverflow()`
    
    ... ... @@ -1811,6 +1874,8 @@ var gUnifiedExtensions = {
    1811 1874
       },
    
    1812 1875
     
    
    1813 1876
       onWidgetOverflow(aNode, aContainer) {
    
    1877
    +    this._updateVisibility();
    
    1878
    +
    
    1814 1879
         // We register a CUI listener for each window so we make sure that we
    
    1815 1880
         // handle the event for the right window here.
    
    1816 1881
         if (window !== aNode.ownerGlobal) {
    
    ... ... @@ -1821,6 +1886,8 @@ var gUnifiedExtensions = {
    1821 1886
       },
    
    1822 1887
     
    
    1823 1888
       onWidgetUnderflow(aNode, aContainer) {
    
    1889
    +    this._updateVisibility();
    
    1890
    +
    
    1824 1891
         // We register a CUI listener for each window so we make sure that we
    
    1825 1892
         // handle the event for the right window here.
    
    1826 1893
         if (window !== aNode.ownerGlobal) {
    

  • browser/components/customizableui/CustomizableUI.sys.mjs
    ... ... @@ -65,7 +65,8 @@ var kVersion = 19;
    65 65
     /**
    
    66 66
      * The current version for base browser.
    
    67 67
      */
    
    68
    -var kVersionBaseBrowser = 1;
    
    68
    +var kVersionBaseBrowser = 2;
    
    69
    +const NoScriptId = "_73a6fe31-595d-460b-a920-fcc0f8843232_-browser-action";
    
    69 70
     
    
    70 71
     /**
    
    71 72
      * The current version for tor browser.
    
    ... ... @@ -858,6 +859,57 @@ var CustomizableUIInternal = {
    858 859
           delete gSavedState.placements["PanelUI-contents"];
    
    859 860
           delete gSavedState.placements["addon-bar"];
    
    860 861
         }
    
    862
    +
    
    863
    +    if (currentVersion < 2) {
    
    864
    +      // Matches against kVersion 19, i.e. when the unified-extensions-button
    
    865
    +      // was introduced and extensions were moved from the palette to
    
    866
    +      // AREA_ADDONS.
    
    867
    +      // For base browser, we want the NoScript addon to be moved from the
    
    868
    +      // default palette to AREA_NAVBAR, so that if it becomes shown through the
    
    869
    +      // preference extensions.hideNoScript it will appear in the toolbar.
    
    870
    +      // If the NoScript addon is already in AREA_NAVBAR, we instead flip the
    
    871
    +      // extensions.hideNoScript preference so that it remains visible.
    
    872
    +      // See tor-browser#41581.
    
    873
    +      const navbarPlacements =
    
    874
    +        gSavedState.placements[CustomizableUI.AREA_NAVBAR];
    
    875
    +      if (navbarPlacements) {
    
    876
    +        let noScriptVisible = false;
    
    877
    +        for (const [area, placements] of Object.entries(
    
    878
    +          gSavedState.placements
    
    879
    +        )) {
    
    880
    +          const index = placements.indexOf(NoScriptId);
    
    881
    +          if (index === -1) {
    
    882
    +            continue;
    
    883
    +          }
    
    884
    +          if (area === CustomizableUI.AREA_ADDONS) {
    
    885
    +            // Has been placed in the ADDONS area.
    
    886
    +            // Most likely, this is an alpha or nightly user who received the
    
    887
    +            // firefox update in a run before this one. In this case, we want to
    
    888
    +            // match the same behaviour as a stable user: hide the button and
    
    889
    +            // move it to the NAVBAR instead.
    
    890
    +            placements.splice(index, 1);
    
    891
    +          } else {
    
    892
    +            // It is in an area other than the ADDON (and not in the palette).
    
    893
    +            noScriptVisible = true;
    
    894
    +          }
    
    895
    +        }
    
    896
    +        if (noScriptVisible) {
    
    897
    +          // Keep the button where it is and make sure it is visible.
    
    898
    +          Services.prefs.setBoolPref("extensions.hideNoScript", false);
    
    899
    +        } else {
    
    900
    +          // Should appear just before unified-extensions-button, which is
    
    901
    +          // currently not part of the default placements.
    
    902
    +          const placeIndex = navbarPlacements.indexOf(
    
    903
    +            "unified-extensions-button"
    
    904
    +          );
    
    905
    +          if (placeIndex === -1) {
    
    906
    +            navbarPlacements.push(NoScriptId);
    
    907
    +          } else {
    
    908
    +            navbarPlacements.splice(placeIndex, 0, NoScriptId);
    
    909
    +          }
    
    910
    +        }
    
    911
    +      }
    
    912
    +    }
    
    861 913
       },
    
    862 914
     
    
    863 915
       _updateForTorBrowser() {
    
    ... ... @@ -3441,7 +3493,17 @@ var CustomizableUIInternal = {
    3441 3493
             CustomizableUI.isWebExtensionWidget(widgetId) &&
    
    3442 3494
             !oldAddonPlacements.includes(widgetId)
    
    3443 3495
           ) {
    
    3444
    -        this.addWidgetToArea(widgetId, CustomizableUI.AREA_ADDONS);
    
    3496
    +        // When resetting, NoScript goes to the toolbar instead. This matches
    
    3497
    +        // its initial placement anyway. And since the button may be hidden by
    
    3498
    +        // default by extensions.hideNoScript, we want to make sure that if it
    
    3499
    +        // becomes unhidden it is shown rather than in the unified extensions
    
    3500
    +        // panel. See tor-browser#41581.
    
    3501
    +        this.addWidgetToArea(
    
    3502
    +          widgetId,
    
    3503
    +          widgetId === NoScriptId
    
    3504
    +            ? CustomizableUI.AREA_NAVBAR
    
    3505
    +            : CustomizableUI.AREA_ADDONS
    
    3506
    +        );
    
    3445 3507
           }
    
    3446 3508
         }
    
    3447 3509
       },
    

  • browser/components/extensions/parent/ext-browserAction.js
    ... ... @@ -176,10 +176,6 @@ this.browserAction = class extends ExtensionAPIPersistent {
    176 176
       }
    
    177 177
     
    
    178 178
       build() {
    
    179
    -    // The extension ID for NoScript (WebExtension)
    
    180
    -    const isNoScript =
    
    181
    -      this.extension.id === "{73a6fe31-595d-460b-a920-fcc0f8843232}";
    
    182
    -
    
    183 179
         let { extension } = this;
    
    184 180
         let widgetId = makeWidgetId(extension.id);
    
    185 181
         let widget = CustomizableUI.createWidget({
    
    ... ... @@ -190,11 +186,7 @@ this.browserAction = class extends ExtensionAPIPersistent {
    190 186
           removable: true,
    
    191 187
           label: this.action.getProperty(null, "title"),
    
    192 188
           tooltiptext: this.action.getProperty(null, "title"),
    
    193
    -      // Do not want to add the NoScript extension to the toolbar by default.
    
    194
    -      // tor-browser#41736
    
    195
    -      defaultArea: isNoScript
    
    196
    -        ? null
    
    197
    -        : browserAreas[this.action.getDefaultArea()],
    
    189
    +      defaultArea: browserAreas[this.action.getDefaultArea()],
    
    198 190
           showInPrivateBrowsing: extension.privateBrowsingAllowed,
    
    199 191
           disallowSubView: true,
    
    200 192
     
    
    ... ... @@ -282,6 +274,22 @@ this.browserAction = class extends ExtensionAPIPersistent {
    282 274
             node.append(button, menuButton);
    
    283 275
             node.viewButton = button;
    
    284 276
     
    
    277
    +        if (extension.isNoScript) {
    
    278
    +          // Hide NoScript by default.
    
    279
    +          // See tor-browser#41581.
    
    280
    +          const HIDE_NO_SCRIPT_PREF = "extensions.hideNoScript";
    
    281
    +          const changeNoScriptVisibility = () => {
    
    282
    +            node.hidden = Services.prefs.getBoolPref(HIDE_NO_SCRIPT_PREF, true);
    
    283
    +          };
    
    284
    +          // Since we expect the NoScript widget to only be destroyed on exit,
    
    285
    +          // we do not set up to remove the observer.
    
    286
    +          Services.prefs.addObserver(
    
    287
    +            HIDE_NO_SCRIPT_PREF,
    
    288
    +            changeNoScriptVisibility
    
    289
    +          );
    
    290
    +          changeNoScriptVisibility();
    
    291
    +        }
    
    292
    +
    
    285 293
             return node;
    
    286 294
           },
    
    287 295
     
    

  • browser/locales/en-US/browser/base-browser.ftl
    ... ... @@ -24,6 +24,11 @@ basebrowser-rfp-restore-window-size-button-ak = R
    24 24
     basebrowser-addon-badge-recommended = Mozilla only recommends extensions that meet their standards for security and performance
    
    25 25
     basebrowser-addon-badge-verified = Mozilla has reviewed this extension to meet their standards for security and performance
    
    26 26
     
    
    27
    +## Option to show or hide the NoScript extension button/item.
    
    28
    +basebrowser-addon-noscript-visibility-label = Toolbar button
    
    29
    +basebrowser-addon-noscript-visibility-show = Show
    
    30
    +basebrowser-addon-noscript-visibility-hide = Hide
    
    31
    +
    
    27 32
     ## About dialog
    
    28 33
     
    
    29 34
     # "Mozilla Firefox" should be treated like a brand and it should be neither translated nor transliterated.
    

  • browser/themes/shared/addons/unified-extensions.css
    ... ... @@ -238,3 +238,21 @@ unified-extensions-item > .subviewbutton {
    238 238
         border-color: transparent;
    
    239 239
       }
    
    240 240
     }
    
    241
    +
    
    242
    +/* Extra rule for tor-browser. See tor-browser#41581.
    
    243
    + * We want to hide the unified-extensions-button when it is empty.
    
    244
    + * However, this button is needed as an anchor for addon notifications. E.g.
    
    245
    + * when installing another addon and permissions pop up.
    
    246
    + * If we simply marked it as "hidden" then it would not be used as an anchor, so
    
    247
    + * the popup would fall back to using the identity button as an anchor instead.
    
    248
    + * So instead, we use "visibility: collapse" whilst it is empty *and* it is not
    
    249
    + * being used as an anchor (the open attribute is missing). */
    
    250
    +#unified-extensions-button.hide-empty:not([open]) {
    
    251
    +  visibility: collapse;
    
    252
    +  /* Ensure getBoundingClientRect().width returns 0.
    
    253
    +   * Even though this button is collapsed, and therefore should not take up any
    
    254
    +   * layout space, getBoundingClientRect will still measure the padding.
    
    255
    +   * If this was not zero, OverflowableToolbar#getOverflowInfo would
    
    256
    +   * over-measure the children width and would always overflow. */
    
    257
    +  padding-inline: 0;
    
    258
    +}

  • toolkit/components/extensions/Extension.sys.mjs
    ... ... @@ -765,6 +765,15 @@ export class ExtensionData {
    765 765
         this.eventPagesEnabled = lazy.eventPagesEnabled;
    
    766 766
       }
    
    767 767
     
    
    768
    +  /**
    
    769
    +   * Whether this is the NoScript extension.
    
    770
    +   *
    
    771
    +   * @type {boolean}
    
    772
    +   */
    
    773
    +  get isNoScript() {
    
    774
    +    return this.id === "{73a6fe31-595d-460b-a920-fcc0f8843232}";
    
    775
    +  }
    
    776
    +
    
    768 777
       /**
    
    769 778
        * A factory function that allows the construction of ExtensionData, with
    
    770 779
        * the isPrivileged flag computed asynchronously.
    
    ... ... @@ -3498,7 +3507,7 @@ export class Extension extends ExtensionData {
    3498 3507
         }
    
    3499 3508
     
    
    3500 3509
         // Bug 40253: Explicitly allow NoScript in Private Browsing mode.
    
    3501
    -    if (this.id === "{73a6fe31-595d-460b-a920-fcc0f8843232}") {
    
    3510
    +    if (this.isNoScript) {
    
    3502 3511
           lazy.ExtensionPermissions.add(this.id, {
    
    3503 3512
             permissions: [PRIVATE_ALLOWED_PERMISSION],
    
    3504 3513
             origins: [],
    

  • toolkit/mozapps/extensions/content/aboutaddons.html
    ... ... @@ -508,6 +508,32 @@
    508 508
               <div class="addon-detail-sitepermissions">
    
    509 509
                 <addon-sitepermissions-list></addon-sitepermissions-list>
    
    510 510
               </div>
    
    511
    +          <!-- Add an option to show the NoScript toolbar button, if this is the
    
    512
    +             - NoScript addon. See tor-browser#41581. -->
    
    513
    +          <div
    
    514
    +            class="addon-detail-row addon-detail-row-noscript-visibility"
    
    515
    +            role="radiogroup"
    
    516
    +            hidden="hidden"
    
    517
    +          >
    
    518
    +            <span
    
    519
    +              class="addon-noscript-visibility-label"
    
    520
    +              data-l10n-id="basebrowser-addon-noscript-visibility-label"
    
    521
    +            ></span>
    
    522
    +            <div class="addon-detail-actions">
    
    523
    +              <label class="radio-container-with-text">
    
    524
    +                <input type="radio" name="noscript-visibility" value="show" />
    
    525
    +                <span
    
    526
    +                  data-l10n-id="basebrowser-addon-noscript-visibility-show"
    
    527
    +                ></span>
    
    528
    +              </label>
    
    529
    +              <label class="radio-container-with-text">
    
    530
    +                <input type="radio" name="noscript-visibility" value="hide" />
    
    531
    +                <span
    
    532
    +                  data-l10n-id="basebrowser-addon-noscript-visibility-hide"
    
    533
    +                ></span>
    
    534
    +              </label>
    
    535
    +            </div>
    
    536
    +          </div>
    
    511 537
               <div class="addon-detail-row addon-detail-row-updates">
    
    512 538
                 <label data-l10n-id="addon-detail-updates-label"></label>
    
    513 539
                 <div class="addon-detail-actions">
    

  • toolkit/mozapps/extensions/content/aboutaddons.js
    ... ... @@ -2063,6 +2063,8 @@ class AddonSitePermissionsList extends HTMLElement {
    2063 2063
     }
    
    2064 2064
     customElements.define("addon-sitepermissions-list", AddonSitePermissionsList);
    
    2065 2065
     
    
    2066
    +const HIDE_NO_SCRIPT_PREF = "extensions.hideNoScript";
    
    2067
    +
    
    2066 2068
     class AddonDetails extends HTMLElement {
    
    2067 2069
       connectedCallback() {
    
    2068 2070
         if (!this.children.length) {
    
    ... ... @@ -2070,12 +2072,61 @@ class AddonDetails extends HTMLElement {
    2070 2072
         }
    
    2071 2073
         this.deck.addEventListener("view-changed", this);
    
    2072 2074
         this.descriptionShowMoreButton.addEventListener("click", this);
    
    2075
    +
    
    2076
    +    // If this is for the NoScript extension, we listen for changes in the
    
    2077
    +    // visibility of its toolbar button.
    
    2078
    +    // See tor-browser#41581.
    
    2079
    +    // NOTE: The addon should be set before being connected, so isNoScript will
    
    2080
    +    // return a correct value.
    
    2081
    +    if (this.isNoScript && !this._noScriptVisibilityObserver) {
    
    2082
    +      this._noScriptVisibilityObserver = () => this.updateNoScriptVisibility();
    
    2083
    +      Services.prefs.addObserver(
    
    2084
    +        HIDE_NO_SCRIPT_PREF,
    
    2085
    +        this._noScriptVisibilityObserver
    
    2086
    +      );
    
    2087
    +    }
    
    2073 2088
       }
    
    2074 2089
     
    
    2075 2090
       disconnectedCallback() {
    
    2076 2091
         this.inlineOptions.destroyBrowser();
    
    2077 2092
         this.deck.removeEventListener("view-changed", this);
    
    2078 2093
         this.descriptionShowMoreButton.removeEventListener("click", this);
    
    2094
    +
    
    2095
    +    if (this._noScriptVisibilityObserver) {
    
    2096
    +      Services.prefs.removeObserver(
    
    2097
    +        HIDE_NO_SCRIPT_PREF,
    
    2098
    +        this._noScriptVisibilityObserver
    
    2099
    +      );
    
    2100
    +      // Clear in case this is called again, or if connectedCallback is called.
    
    2101
    +      delete this._noScriptVisibilityObserver;
    
    2102
    +    }
    
    2103
    +  }
    
    2104
    +
    
    2105
    +  /**
    
    2106
    +   * Whether this is a description for the NoScript extension.
    
    2107
    +   *
    
    2108
    +   * @type {boolean}
    
    2109
    +   */
    
    2110
    +  get isNoScript() {
    
    2111
    +    return this.addon?.id === "{73a6fe31-595d-460b-a920-fcc0f8843232}";
    
    2112
    +  }
    
    2113
    +
    
    2114
    +  /**
    
    2115
    +   * Update the shown visibility value for the NoScript extension's toolbar
    
    2116
    +   * button.
    
    2117
    +   */
    
    2118
    +  updateNoScriptVisibility() {
    
    2119
    +    if (!this.isNoScript) {
    
    2120
    +      return;
    
    2121
    +    }
    
    2122
    +    const visibility = Services.prefs.getBoolPref(HIDE_NO_SCRIPT_PREF, true)
    
    2123
    +      ? "hide"
    
    2124
    +      : "show";
    
    2125
    +    for (const input of this.querySelectorAll(
    
    2126
    +      ".addon-detail-row-noscript-visibility input"
    
    2127
    +    )) {
    
    2128
    +      input.checked = input.value === visibility;
    
    2129
    +    }
    
    2079 2130
       }
    
    2080 2131
     
    
    2081 2132
       handleEvent(e) {
    
    ... ... @@ -2271,6 +2322,27 @@ class AddonDetails extends HTMLElement {
    2271 2322
           "upgrade"
    
    2272 2323
         );
    
    2273 2324
     
    
    2325
    +    // If this is the NoScript extension, we want to show an option to change
    
    2326
    +    // the visibility of its toolbar button.
    
    2327
    +    // See tor-browser#41581.
    
    2328
    +    const visibilityRow = this.querySelector(
    
    2329
    +      ".addon-detail-row-noscript-visibility"
    
    2330
    +    );
    
    2331
    +    visibilityRow.hidden = !this.isNoScript;
    
    2332
    +    if (this.isNoScript) {
    
    2333
    +      // Set up the aria-label for the role="radiogroup".
    
    2334
    +      const visibilityLabel = visibilityRow.querySelector(
    
    2335
    +        ".addon-noscript-visibility-label"
    
    2336
    +      );
    
    2337
    +      visibilityLabel.id = ExtensionCommon.makeWidgetId(
    
    2338
    +        `${addon.id}-noscript-visibility-label`
    
    2339
    +      );
    
    2340
    +      visibilityRow.setAttribute("aria-labelledby", visibilityLabel.id);
    
    2341
    +
    
    2342
    +      // Set the initial displayed value.
    
    2343
    +      this.updateNoScriptVisibility();
    
    2344
    +    }
    
    2345
    +
    
    2274 2346
         if (addon.type != "extension") {
    
    2275 2347
           // Don't show any private browsing related section for non-extension
    
    2276 2348
           // addon types, because not relevant or they are either always allowed
    
    ... ... @@ -2662,6 +2734,11 @@ class AddonCard extends HTMLElement {
    2662 2734
               // Update the card if the add-on isn't active.
    
    2663 2735
               this.update();
    
    2664 2736
             }
    
    2737
    +      } else if (name == "noscript-visibility") {
    
    2738
    +        // Update the NoScript toolbar button visibility.
    
    2739
    +        // See tor-browser#41581.
    
    2740
    +        const hide = e.target.value !== "show";
    
    2741
    +        Services.prefs.setBoolPref(HIDE_NO_SCRIPT_PREF, hide);
    
    2665 2742
           }
    
    2666 2743
         } else if (e.type == "mousedown") {
    
    2667 2744
           // Open panel on mousedown when the mouse is used.