Bug 1880230 - Added support for private browsing only widgets in CustomizableUI. r=hsohaney,firefox-desktop-core-reviewers ,Gijs,desktop-theme-reviewers,dao

Differential Revision: https://phabricator.services.mozilla.com/D209857
This commit is contained in:
William Wen
2024-07-18 15:56:17 +00:00
parent 372939b5bf
commit 060019310e
7 changed files with 225 additions and 18 deletions

View File

@@ -1178,6 +1178,10 @@ var CustomizableUIInternal = {
continue;
}
if (!inPrivateWindow && widget?.hideInNonPrivateBrowsing) {
continue;
}
this.ensureButtonContextMenu(node, aAreaNode);
// This needs updating in case we're resetting / undoing a reset.
@@ -1410,6 +1414,8 @@ var CustomizableUIInternal = {
let showInPrivateBrowsing = gPalette.has(aWidgetId)
? gPalette.get(aWidgetId).showInPrivateBrowsing
: true;
let hideInNonPrivateBrowsing =
gPalette.get(aWidgetId)?.hideInNonPrivateBrowsing ?? false;
for (let areaNode of areaNodes) {
let window = areaNode.ownerGlobal;
@@ -1420,6 +1426,13 @@ var CustomizableUIInternal = {
continue;
}
if (
hideInNonPrivateBrowsing &&
!lazy.PrivateBrowsingUtils.isWindowPrivate(window)
) {
continue;
}
let container = this.getCustomizationTarget(areaNode);
let widgetNode = window.document.getElementById(aWidgetId);
if (widgetNode && isOverflowable) {
@@ -1597,6 +1610,8 @@ var CustomizableUIInternal = {
let showInPrivateBrowsing = gPalette.has(aWidgetId)
? gPalette.get(aWidgetId).showInPrivateBrowsing
: true;
let hideInNonPrivateBrowsing =
gPalette.get(aWidgetId)?.hideInNonPrivateBrowsing ?? false;
if (
!showInPrivateBrowsing &&
@@ -1605,6 +1620,13 @@ var CustomizableUIInternal = {
return;
}
if (
hideInNonPrivateBrowsing &&
!lazy.PrivateBrowsingUtils.isWindowPrivate(window)
) {
return;
}
let [, widgetNode] = this.getWidgetNode(aWidgetId, window);
if (!widgetNode) {
lazy.log.error("Widget '" + aWidgetId + "' not found, unable to move");
@@ -1866,6 +1888,12 @@ var CustomizableUIInternal = {
) {
return null;
}
if (
aWidget.hideInNonPrivateBrowsing &&
!lazy.PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView)
) {
return null;
}
lazy.log.debug("Building " + aWidget.id + " of type " + aWidget.type);
@@ -2364,7 +2392,10 @@ var CustomizableUIInternal = {
// gPalette.
for (let [id, widget] of gPalette) {
if (!widget.currentArea) {
if (widget.showInPrivateBrowsing || !isWindowPrivate) {
if (
(isWindowPrivate && widget.showInPrivateBrowsing) ||
(!isWindowPrivate && !widget.hideInNonPrivateBrowsing)
) {
widgets.add(id);
}
}
@@ -2991,6 +3022,7 @@ var CustomizableUIInternal = {
tooltiptext: null,
l10nId: null,
showInPrivateBrowsing: true,
hideInNonPrivateBrowsing: false,
_introducedInVersion: -1,
_introducedByPref: null,
keepBroadcastAttributesWhenCustomizing: false,
@@ -3033,6 +3065,7 @@ var CustomizableUIInternal = {
const kOptBoolProps = [
"removable",
"showInPrivateBrowsing",
"hideInNonPrivateBrowsing",
"overflows",
"tabSpecific",
"locationSpecific",
@@ -3567,7 +3600,12 @@ var CustomizableUIInternal = {
// that are present. This avoids including items that don't exist (e.g. ids
// of add-on items that the user has uninstalled).
let orderedPlacements = CustomizableUI.getWidgetIdsInArea(container.id);
return orderedPlacements.filter(w => currentWidgets.has(w));
return orderedPlacements.filter(w => {
return (
currentWidgets.has(w) ||
this.getWidgetProvider(w) == CustomizableUI.PROVIDER_API
);
});
},
get inDefaultState() {
@@ -4209,6 +4247,8 @@ export var CustomizableUI = {
* as the "$shortcut" variable to the fluent message.
* - showInPrivateBrowsing: whether to show the widget in private browsing
* mode (optional, default: true)
* - hideInNonPrivateBrowsing: whether to hide the widget in non private
* browsing mode windows (optional, default: false)
* - tabSpecific: If true, closes the panel if the tab changes.
* - locationSpecific: If true, closes the panel if the location changes.
* This is similar to tabSpecific, but also if the location
@@ -4263,6 +4303,8 @@ export var CustomizableUI = {
* - tooltiptext: for API-provided widgets, the tooltip of the widget;
* - showInPrivateBrowsing: for API-provided widgets, whether the widget is
* visible in private browsing;
* - hideInNonPrivateBrowsing: for API-provided widgets, whether the widget is
* hidden in non-private browsing;
*
* Single window wrappers obtained through forWindow(someWindow) or from the
* instances array have the following properties
@@ -4969,6 +5011,7 @@ function WidgetGroupWrapper(aWidget) {
"label",
"tooltiptext",
"showInPrivateBrowsing",
"hideInNonPrivateBrowsing",
"viewId",
"disallowSubView",
"webExtension",

View File

@@ -44,6 +44,8 @@ tags = "overflowable-toolbar"
# MacOS builds in browser_876926_customize_mode_wrapping.js
skip-if = ["os == 'mac' && debug"]
["browser_1880230_hideInNonPrivateBrowsing.js"]
["browser_694291_searchbar_preference.js"]
["browser_873501_handle_specials.js"]

View File

@@ -0,0 +1,172 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const kWidgetId = "pbm-only-test-widget";
function assertWidgetExists(aWindow, aExists) {
if (aExists) {
ok(
aWindow.document.getElementById(kWidgetId),
"Should have found test widget in the window"
);
} else {
is(
aWindow.document.getElementById(kWidgetId),
null,
"Should not have found test widget in the window"
);
}
}
// A widget that is created with hideInNonPrivateBrowsing undefined should
// have that value default to false.
add_task(function () {
let wrapper = CustomizableUI.createWidget({
id: kWidgetId,
});
ok(
!wrapper.hideInNonPrivateBrowsing,
"hideInNonPrivateBrowsing should have defaulted to false."
);
CustomizableUI.destroyWidget(kWidgetId);
});
// Add a widget via the API with hideInNonPrivateBrowsing set to true
// and ensure it does not appear in pre-existing or newly created
// non-private windows.
add_task(async function () {
let plain1 = await openAndLoadWindow();
let private1 = await openAndLoadWindow({ private: true });
CustomizableUI.createWidget({
id: kWidgetId,
removable: true,
hideInNonPrivateBrowsing: true,
});
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
assertWidgetExists(plain1, false);
assertWidgetExists(private1, true);
// Now open up some new windows. The widget should not exist in the new
// plain window, but exist in the new private window.
let plain2 = await openAndLoadWindow();
let private2 = await openAndLoadWindow({ private: true });
assertWidgetExists(plain2, false);
assertWidgetExists(private2, true);
// Try moving the widget around and make sure it doesn't get added
// to the non-private windows. We'll start by appending it to the tabstrip.
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_TABSTRIP);
assertWidgetExists(plain1, false);
assertWidgetExists(plain2, false);
assertWidgetExists(private1, true);
assertWidgetExists(private2, true);
// And then move it to the beginning of the tabstrip.
CustomizableUI.moveWidgetWithinArea(kWidgetId, 0);
assertWidgetExists(plain1, false);
assertWidgetExists(plain2, false);
assertWidgetExists(private1, true);
assertWidgetExists(private2, true);
CustomizableUI.removeWidgetFromArea(kWidgetId);
assertWidgetExists(plain1, false);
assertWidgetExists(plain2, false);
assertWidgetExists(private1, false);
assertWidgetExists(private2, false);
await Promise.all(
[plain1, plain2, private1, private2].map(promiseWindowClosed)
);
CustomizableUI.destroyWidget(kWidgetId);
});
// Add a widget via the API with hideInNonPrivateBrowsing set to false,
// and ensure that it appears in pre-existing or newly created
// private browsing windows.
add_task(async function () {
let plain1 = await openAndLoadWindow();
let private1 = await openAndLoadWindow({ private: true });
CustomizableUI.createWidget({
id: kWidgetId,
removable: true,
hideInNonPrivateBrowsing: false,
});
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
assertWidgetExists(plain1, true);
assertWidgetExists(private1, true);
// Now open up some new windows. The widget should exist in the new
// plain window, but not the new private window.
let plain2 = await openAndLoadWindow();
let private2 = await openAndLoadWindow({ private: true });
assertWidgetExists(plain2, true);
assertWidgetExists(private2, true);
// Try moving the widget around and make sure it doesn't get added
// to the private windows. We'll start by appending it to the tabstrip.
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_TABSTRIP);
assertWidgetExists(plain1, true);
assertWidgetExists(plain2, true);
assertWidgetExists(private1, true);
assertWidgetExists(private2, true);
// And then move it to the beginning of the tabstrip.
CustomizableUI.moveWidgetWithinArea(kWidgetId, 0);
assertWidgetExists(plain1, true);
assertWidgetExists(plain2, true);
assertWidgetExists(private1, true);
assertWidgetExists(private2, true);
CustomizableUI.removeWidgetFromArea(kWidgetId);
assertWidgetExists(plain1, false);
assertWidgetExists(plain2, false);
assertWidgetExists(private1, false);
assertWidgetExists(private2, false);
await Promise.all(
[plain1, plain2, private1, private2].map(promiseWindowClosed)
);
CustomizableUI.destroyWidget(kWidgetId);
});
// Add a widget via the API with hideInNonPrivateBrowsing set to true
// and ensure it does not appear in the list of unused widgets in private
// windows.
add_task(async function testPrivateBrowsingCustomizeModeWidget() {
CustomizableUI.createWidget({
id: kWidgetId,
hideInNonPrivateBrowsing: true,
});
let normalWidgetArray = CustomizableUI.getUnusedWidgets(gNavToolbox.palette);
normalWidgetArray = normalWidgetArray.map(w => w.id);
is(
normalWidgetArray.indexOf(kWidgetId),
-1,
"Widget should not appear as unused in non-private window"
);
let privateWindow = await openAndLoadWindow({ private: true });
let privateWidgetArray = CustomizableUI.getUnusedWidgets(
privateWindow.gNavToolbox.palette
);
privateWidgetArray = privateWidgetArray.map(w => w.id);
Assert.greater(
privateWidgetArray.indexOf(kWidgetId),
-1,
"Widget should appear as unused in private window"
);
await promiseWindowClosed(privateWindow);
CustomizableUI.destroyWidget(kWidgetId);
});
add_task(async function asyncCleanup() {
await resetCustomization();
});

View File

@@ -20,6 +20,8 @@ add_task(async function () {
ok(CustomizableUI.inDefaultState, "Should start in default state.");
let navbarTarget = CustomizableUI.getCustomizationTarget(navbar);
let oldChildCount = navbarTarget.childElementCount;
let placements = [...navbarTarget.children].map(node => node.id);
window.resizeTo(kForceOverflowWidthPx, window.outerHeight);
await TestUtils.waitForCondition(
() => navbar.hasAttribute("overflowing"),
@@ -51,9 +53,6 @@ add_task(async function () {
// Verify actual physical placements match those of the placement array:
let placementCounter = 0;
let placements = CustomizableUI.getWidgetIdsInArea(
CustomizableUI.AREA_NAVBAR
);
for (let node of navbarTarget.children) {
if (node.getAttribute("skipintoolbarset") == "true") {
continue;

View File

@@ -48,6 +48,7 @@ export const ResetPBMPanel = {
onViewHiding(aEvent) {
ResetPBMPanel.onViewHiding(aEvent);
},
hideInNonPrivateBrowsing: true,
};
if (this._enabled) {

View File

@@ -203,22 +203,16 @@ add_task(async function test_toolbar_button_visibility() {
});
info(
"Test that the toolbar button is never visible in a normal browsing window."
"Test that the toolbar button does not exist in a normal browsing window."
);
let toolbarButtonNormalBrowsing = document.querySelector(
SELECTOR_TOOLBAR_BUTTON
);
Assert.equal(
!!toolbarButtonNormalBrowsing,
isEnabled,
"Normal browsing toolbar button element exists, depending on enabled pref state."
false,
"Normal browsing toolbar button element does not exist."
);
if (toolbarButtonNormalBrowsing) {
Assert.ok(
!BrowserTestUtils.isVisible(toolbarButtonNormalBrowsing),
"Toolbar button is not visible in normal browsing"
);
}
info(
"Test that the toolbar button is visible in a private browsing window, depending on enabled pref state."

View File

@@ -192,10 +192,6 @@
#reset-pbm-toolbar-button {
list-style-image: url("chrome://browser/skin/flame.svg");
:root:not([privatebrowsingmode]) & {
display: none;
}
}
#email-link-button {