Skip to content
Snippets Groups Projects
Commit 76523927 authored by henry's avatar henry Committed by Pier Angelo Vendrame
Browse files

Bug 41736: Hide NoScript extension's toolbar button by default.

This hides it from both the toolbar and the unified extensions panel.

We also hide the unified-extension-button if the panel would be empty:
not including the NoScript button when it is hidden. As a result, this
will be hidden by default until a user installs another extension (or
shows the NoScript button and unpins it).
parent f51c7502
No related branches found
No related tags found
No related merge requests found
...@@ -26,6 +26,9 @@ XPCOMUtils.defineLazyGetter(lazy, "l10n", function () { ...@@ -26,6 +26,9 @@ XPCOMUtils.defineLazyGetter(lazy, "l10n", function () {
); );
}); });
const HIDE_NO_SCRIPT_PREF = "extensions.hideNoScript";
const HIDE_UNIFIED_WHEN_EMPTY_PREF = "extensions.hideUnifiedWhenEmpty";
/** /**
* Mapping of error code -> [error-id, local-error-id] * Mapping of error code -> [error-id, local-error-id]
* *
...@@ -1186,6 +1189,18 @@ var gUnifiedExtensions = { ...@@ -1186,6 +1189,18 @@ var gUnifiedExtensions = {
gNavToolbox.addEventListener("customizationstarting", this); gNavToolbox.addEventListener("customizationstarting", this);
CustomizableUI.addListener(this); CustomizableUI.addListener(this);
// Listen out for changes in extensions.hideNoScript and
// extension.hideUnifiedWhenEmpty, which can effect the visibility of the
// unified-extensions-button.
// See tor-browser#41581.
this._hideNoScriptObserver = () => this._updateVisibility();
Services.prefs.addObserver(HIDE_NO_SCRIPT_PREF, this._hideNoScriptObserver);
Services.prefs.addObserver(
HIDE_UNIFIED_WHEN_EMPTY_PREF,
this._hideNoScriptObserver
);
this._updateVisibility();
this._initialized = true; this._initialized = true;
}, },
...@@ -1201,6 +1216,15 @@ var gUnifiedExtensions = { ...@@ -1201,6 +1216,15 @@ var gUnifiedExtensions = {
gNavToolbox.removeEventListener("customizationstarting", this); gNavToolbox.removeEventListener("customizationstarting", this);
CustomizableUI.removeListener(this); CustomizableUI.removeListener(this);
Services.prefs.removeObserver(
HIDE_NO_SCRIPT_PREF,
this._hideNoScriptObserver
);
Services.prefs.removeObserver(
HIDE_UNIFIED_WHEN_EMPTY_PREF,
this._hideNoScriptObserver
);
}, },
onLocationChange(browser, webProgress, _request, _uri, flags) { onLocationChange(browser, webProgress, _request, _uri, flags) {
...@@ -1278,6 +1302,15 @@ var gUnifiedExtensions = { ...@@ -1278,6 +1302,15 @@ var gUnifiedExtensions = {
return false; return false;
} }
// When an extensions is about to be removed, it may still appear in
// getActiveExtensions.
// This is needed for hasExtensionsInPanel, when called through
// onWidgetDestroy when an extension is being removed.
// See tor-browser#41581.
if (extension.hasShutdown) {
return false;
}
// Ignore hidden and extensions that cannot access the current window // Ignore hidden and extensions that cannot access the current window
// (because of PB mode when we are in a private window), since users // (because of PB mode when we are in a private window), since users
// cannot do anything with those extensions anyway. // cannot do anything with those extensions anyway.
...@@ -1292,6 +1325,20 @@ var gUnifiedExtensions = { ...@@ -1292,6 +1325,20 @@ var gUnifiedExtensions = {
return policies; return policies;
}, },
/**
* Potentially hide the unified-extensions-button if it would be empty.
*/
// See tor-browser#41581.
// The behaviour overlaps with a proposal in mozilla Bug 1778684, which has
// not been implemented, or had much recent activity as of 5th October 2023.
_updateVisibility() {
this.button.classList.toggle(
"hide-empty",
Services.prefs.getBoolPref(HIDE_UNIFIED_WHEN_EMPTY_PREF, true) &&
!this.hasExtensionsInPanel()
);
},
/** /**
* Returns true when there are active extensions listed/shown in the unified * Returns true when there are active extensions listed/shown in the unified
* extensions panel, and false otherwise (e.g. when extensions are pinned in * extensions panel, and false otherwise (e.g. when extensions are pinned in
...@@ -1300,7 +1347,13 @@ var gUnifiedExtensions = { ...@@ -1300,7 +1347,13 @@ var gUnifiedExtensions = {
* @returns {boolean} Whether there are extensions listed in the panel. * @returns {boolean} Whether there are extensions listed in the panel.
*/ */
hasExtensionsInPanel() { hasExtensionsInPanel() {
const policies = this.getActivePolicies(); let policies = this.getActivePolicies();
// If the NoScript button is hidden, we won't count it towards the list of
// extensions in the panel.
// See tor-browser#41581.
if (Services.prefs.getBoolPref(HIDE_NO_SCRIPT_PREF, true)) {
policies = policies.filter(policy => !policy.extension?.isNoScript);
}
return !!policies return !!policies
.map(policy => this.browserActionFor(policy)?.widget) .map(policy => this.browserActionFor(policy)?.widget)
...@@ -1795,7 +1848,17 @@ var gUnifiedExtensions = { ...@@ -1795,7 +1848,17 @@ var gUnifiedExtensions = {
} }
}, },
onWidgetRemoved() {
this._updateVisibility();
},
onWidgetDestroyed() {
this._updateVisibility();
},
onWidgetAdded(aWidgetId, aArea, aPosition) { onWidgetAdded(aWidgetId, aArea, aPosition) {
this._updateVisibility();
// When we pin a widget to the toolbar from a narrow window, the widget // When we pin a widget to the toolbar from a narrow window, the widget
// will be overflowed directly. In this case, we do not want to change the // will be overflowed directly. In this case, we do not want to change the
// class name since it is going to be changed by `onWidgetOverflow()` // class name since it is going to be changed by `onWidgetOverflow()`
...@@ -1811,6 +1874,8 @@ var gUnifiedExtensions = { ...@@ -1811,6 +1874,8 @@ var gUnifiedExtensions = {
}, },
onWidgetOverflow(aNode, aContainer) { onWidgetOverflow(aNode, aContainer) {
this._updateVisibility();
// We register a CUI listener for each window so we make sure that we // We register a CUI listener for each window so we make sure that we
// handle the event for the right window here. // handle the event for the right window here.
if (window !== aNode.ownerGlobal) { if (window !== aNode.ownerGlobal) {
...@@ -1821,6 +1886,8 @@ var gUnifiedExtensions = { ...@@ -1821,6 +1886,8 @@ var gUnifiedExtensions = {
}, },
onWidgetUnderflow(aNode, aContainer) { onWidgetUnderflow(aNode, aContainer) {
this._updateVisibility();
// We register a CUI listener for each window so we make sure that we // We register a CUI listener for each window so we make sure that we
// handle the event for the right window here. // handle the event for the right window here.
if (window !== aNode.ownerGlobal) { if (window !== aNode.ownerGlobal) {
......
...@@ -274,6 +274,22 @@ this.browserAction = class extends ExtensionAPIPersistent { ...@@ -274,6 +274,22 @@ this.browserAction = class extends ExtensionAPIPersistent {
node.append(button, menuButton); node.append(button, menuButton);
node.viewButton = button; node.viewButton = button;
if (extension.isNoScript) {
// Hide NoScript by default.
// See tor-browser#41581.
const HIDE_NO_SCRIPT_PREF = "extensions.hideNoScript";
const changeNoScriptVisibility = () => {
node.hidden = Services.prefs.getBoolPref(HIDE_NO_SCRIPT_PREF, true);
};
// Since we expect the NoScript widget to only be destroyed on exit,
// we do not set up to remove the observer.
Services.prefs.addObserver(
HIDE_NO_SCRIPT_PREF,
changeNoScriptVisibility
);
changeNoScriptVisibility();
}
return node; return node;
}, },
......
...@@ -238,3 +238,21 @@ unified-extensions-item > .subviewbutton { ...@@ -238,3 +238,21 @@ unified-extensions-item > .subviewbutton {
border-color: transparent; border-color: transparent;
} }
} }
/* Extra rule for tor-browser. See tor-browser#41581.
* We want to hide the unified-extensions-button when it is empty.
* However, this button is needed as an anchor for addon notifications. E.g.
* when installing another addon and permissions pop up.
* If we simply marked it as "hidden" then it would not be used as an anchor, so
* the popup would fall back to using the identity button as an anchor instead.
* So instead, we use "visibility: collapse" whilst it is empty *and* it is not
* being used as an anchor (the open attribute is missing). */
#unified-extensions-button.hide-empty:not([open]) {
visibility: collapse;
/* Ensure getBoundingClientRect().width returns 0.
* Even though this button is collapsed, and therefore should not take up any
* layout space, getBoundingClientRect will still measure the padding.
* If this was not zero, OverflowableToolbar#getOverflowInfo would
* over-measure the children width and would always overflow. */
padding-inline: 0;
}
...@@ -508,6 +508,32 @@ ...@@ -508,6 +508,32 @@
<div class="addon-detail-sitepermissions"> <div class="addon-detail-sitepermissions">
<addon-sitepermissions-list></addon-sitepermissions-list> <addon-sitepermissions-list></addon-sitepermissions-list>
</div> </div>
<!-- Add an option to show the NoScript toolbar button, if this is the
- NoScript addon. See tor-browser#41581. -->
<div
class="addon-detail-row addon-detail-row-noscript-visibility"
role="radiogroup"
hidden="hidden"
>
<span
class="addon-noscript-visibility-label"
data-l10n-id="basebrowser-addon-noscript-visibility-label"
></span>
<div class="addon-detail-actions">
<label class="radio-container-with-text">
<input type="radio" name="noscript-visibility" value="show" />
<span
data-l10n-id="basebrowser-addon-noscript-visibility-show"
></span>
</label>
<label class="radio-container-with-text">
<input type="radio" name="noscript-visibility" value="hide" />
<span
data-l10n-id="basebrowser-addon-noscript-visibility-hide"
></span>
</label>
</div>
</div>
<div class="addon-detail-row addon-detail-row-updates"> <div class="addon-detail-row addon-detail-row-updates">
<label data-l10n-id="addon-detail-updates-label"></label> <label data-l10n-id="addon-detail-updates-label"></label>
<div class="addon-detail-actions"> <div class="addon-detail-actions">
......
...@@ -2062,6 +2062,8 @@ class AddonSitePermissionsList extends HTMLElement { ...@@ -2062,6 +2062,8 @@ class AddonSitePermissionsList extends HTMLElement {
} }
customElements.define("addon-sitepermissions-list", AddonSitePermissionsList); customElements.define("addon-sitepermissions-list", AddonSitePermissionsList);
const HIDE_NO_SCRIPT_PREF = "extensions.hideNoScript";
class AddonDetails extends HTMLElement { class AddonDetails extends HTMLElement {
connectedCallback() { connectedCallback() {
if (!this.children.length) { if (!this.children.length) {
...@@ -2069,12 +2071,61 @@ class AddonDetails extends HTMLElement { ...@@ -2069,12 +2071,61 @@ class AddonDetails extends HTMLElement {
} }
this.deck.addEventListener("view-changed", this); this.deck.addEventListener("view-changed", this);
this.descriptionShowMoreButton.addEventListener("click", this); this.descriptionShowMoreButton.addEventListener("click", this);
// If this is for the NoScript extension, we listen for changes in the
// visibility of its toolbar button.
// See tor-browser#41581.
// NOTE: The addon should be set before being connected, so isNoScript will
// return a correct value.
if (this.isNoScript && !this._noScriptVisibilityObserver) {
this._noScriptVisibilityObserver = () => this.updateNoScriptVisibility();
Services.prefs.addObserver(
HIDE_NO_SCRIPT_PREF,
this._noScriptVisibilityObserver
);
}
} }
disconnectedCallback() { disconnectedCallback() {
this.inlineOptions.destroyBrowser(); this.inlineOptions.destroyBrowser();
this.deck.removeEventListener("view-changed", this); this.deck.removeEventListener("view-changed", this);
this.descriptionShowMoreButton.removeEventListener("click", this); this.descriptionShowMoreButton.removeEventListener("click", this);
if (this._noScriptVisibilityObserver) {
Services.prefs.removeObserver(
HIDE_NO_SCRIPT_PREF,
this._noScriptVisibilityObserver
);
// Clear in case this is called again, or if connectedCallback is called.
delete this._noScriptVisibilityObserver;
}
}
/**
* Whether this is a description for the NoScript extension.
*
* @type {boolean}
*/
get isNoScript() {
return this.addon?.id === "{73a6fe31-595d-460b-a920-fcc0f8843232}";
}
/**
* Update the shown visibility value for the NoScript extension's toolbar
* button.
*/
updateNoScriptVisibility() {
if (!this.isNoScript) {
return;
}
const visibility = Services.prefs.getBoolPref(HIDE_NO_SCRIPT_PREF, true)
? "hide"
: "show";
for (const input of this.querySelectorAll(
".addon-detail-row-noscript-visibility input"
)) {
input.checked = input.value === visibility;
}
} }
handleEvent(e) { handleEvent(e) {
...@@ -2270,6 +2321,27 @@ class AddonDetails extends HTMLElement { ...@@ -2270,6 +2321,27 @@ class AddonDetails extends HTMLElement {
"upgrade" "upgrade"
); );
// If this is the NoScript extension, we want to show an option to change
// the visibility of its toolbar button.
// See tor-browser#41581.
const visibilityRow = this.querySelector(
".addon-detail-row-noscript-visibility"
);
visibilityRow.hidden = !this.isNoScript;
if (this.isNoScript) {
// Set up the aria-label for the role="radiogroup".
const visibilityLabel = visibilityRow.querySelector(
".addon-noscript-visibility-label"
);
visibilityLabel.id = ExtensionCommon.makeWidgetId(
`${addon.id}-noscript-visibility-label`
);
visibilityRow.setAttribute("aria-labelledby", visibilityLabel.id);
// Set the initial displayed value.
this.updateNoScriptVisibility();
}
if (addon.type != "extension") { if (addon.type != "extension") {
// Don't show any private browsing related section for non-extension // Don't show any private browsing related section for non-extension
// addon types, because not relevant or they are either always allowed // addon types, because not relevant or they are either always allowed
...@@ -2661,6 +2733,11 @@ class AddonCard extends HTMLElement { ...@@ -2661,6 +2733,11 @@ class AddonCard extends HTMLElement {
// Update the card if the add-on isn't active. // Update the card if the add-on isn't active.
this.update(); this.update();
} }
} else if (name == "noscript-visibility") {
// Update the NoScript toolbar button visibility.
// See tor-browser#41581.
const hide = e.target.value !== "show";
Services.prefs.setBoolPref(HIDE_NO_SCRIPT_PREF, hide);
} }
} else if (e.type == "mousedown") { } else if (e.type == "mousedown") {
// Open panel on mousedown when the mouse is used. // Open panel on mousedown when the mouse is used.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment