Bug 1899346 - Move CUI widgets out of the horizontal tabstrip when in vertical tabs mode. r=sidebar-reviewers,mconley,sclements,webdriver-reviewers,whimboo,tabbrowser-reviewers

* Ensure tabstrip widgets are temporarily removeable at browser init so the CUI placements are correctly applied
* Shuffle placements during initialization to build the correct toolbars for the verticalTabs pref value
* Notify on the 'tabstrip-orientation-change' topic when the verticalTabs pref changes and CUI placements have been updated
* Add tests for switching tabstrip orientation, and for initializing in verticalTabs mode

Differential Revision: https://phabricator.services.mozilla.com/D217161
This commit is contained in:
Sam Foster 2024-09-11 16:27:08 +00:00
Родитель 21b890d440
Коммит f96c44d7b0
11 изменённых файлов: 825 добавлений и 62 удалений

Просмотреть файл

@ -143,6 +143,18 @@ var gBrowserInit = {
gNavToolbox.palette = document.getElementById(
"BrowserToolbarPalette"
).content;
// We don't want these normally non-removable elements to get put back into the
// tabstrip if we're initializing with vertical tabs
let nonRemovables = [
gBrowser.tabContainer,
document.getElementById("alltabs-button"),
];
for (let elem of nonRemovables) {
elem.setAttribute("removable", "true");
// tell CUI to ignore this element when it builds the toolbar areas
elem.setAttribute("skipintoolbarset", "true");
}
for (let area of CustomizableUI.areas) {
let type = CustomizableUI.getAreaType(area);
if (type == CustomizableUI.TYPE_TOOLBAR) {
@ -150,6 +162,11 @@ var gBrowserInit = {
CustomizableUI.registerToolbarNode(node);
}
}
for (let elem of nonRemovables) {
elem.setAttribute("removable", "false");
elem.removeAttribute("skipintoolbarset");
}
BrowserSearch.initPlaceHolder();
// Hack to ensure that the various initial pages favicon is loaded

Просмотреть файл

@ -30,6 +30,10 @@ const kDefaultThemeID = "default-theme@mozilla.org";
const kSpecialWidgetPfx = "customizableui-special-";
const kPrefCustomizationState = "browser.uiCustomization.state";
const kPrefCustomizationHorizontalTabstrip =
"browser.uiCustomization.horizontalTabstrip";
const kPrefCustomizationHorizontalTabsBackup =
"browser.uiCustomization.horizontalTabsBackup";
const kPrefCustomizationAutoAdd = "browser.uiCustomization.autoAdd";
const kPrefCustomizationDebug = "browser.uiCustomization.debug";
const kPrefDrawInTitlebar = "browser.tabs.inTitlebar";
@ -175,6 +179,12 @@ var gUIStateBeforeReset = {
autoTouchMode: null,
};
/*
* The current tab orientation: initially null until initialization,
* true for vertical, false for horizontal
*/
var gCurrentVerticalTabs = null;
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gDebuggingEnabled",
@ -232,6 +242,26 @@ XPCOMUtils.defineLazyPreferenceGetter(
}
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"verticalTabsPref",
"sidebar.verticalTabs",
false,
(pref, oldVal, newVal) => {
lazy.log.debug(
`sidebar.verticalTabs change handler, calling updateTabStripOrientation with value: ${newVal}, gCurrentVerticalTabs: ${gCurrentVerticalTabs}`
);
CustomizableUIInternal.updateTabStripOrientation();
}
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"horizontalPlacementsPref",
kPrefCustomizationHorizontalTabstrip,
""
);
ChromeUtils.defineLazyGetter(lazy, "log", () => {
let { ConsoleAPI } = ChromeUtils.importESModule(
"resource://gre/modules/Console.sys.mjs"
@ -306,6 +336,11 @@ var CustomizableUIInternal = {
type: CustomizableUI.TYPE_TOOLBAR,
overflowable: true,
defaultPlacements: navbarPlacements,
verticalTabsDefaultPlacements: [
"firefox-view-button",
"new-tab-button",
"alltabs-button",
],
defaultCollapsed: false,
},
true
@ -333,10 +368,23 @@ var CustomizableUIInternal = {
"new-tab-button",
"alltabs-button",
],
verticalTabsDefaultPlacements: [],
defaultCollapsed: null,
},
true
);
this.registerArea(
CustomizableUI.AREA_VERTICAL_TABSTRIP,
{
type: "toolbar",
defaultPlacements: [],
verticalTabsDefaultPlacements: ["tabbrowser-tabs"],
defaultCollapsed: null,
},
true
);
this.registerArea(
CustomizableUI.AREA_BOOKMARKS,
{
@ -346,12 +394,20 @@ var CustomizableUIInternal = {
},
true
);
lazy.log.debug(`All the areas registered: ${[...gAreas.keys()]}`);
// At initialization, if we find vertical tabs enabled but not sidebar.revamp
// we'll enable revamp rather than disable vertical tabs.
this.reconcileSidebarPrefs(kPrefSidebarVerticalTabsEnabled);
this.initializeForTabsOrientation(CustomizableUI.verticalTabsEnabled);
SearchWidgetTracker.init();
Services.obs.addObserver(this, "browser-set-toolbar-visibility");
Services.prefs.addObserver(kPrefSidebarVerticalTabsEnabled, this);
Services.prefs.addObserver(kPrefSidebarRevampEnabled, this);
},
onEnabled(addon) {
@ -811,7 +867,17 @@ var CustomizableUIInternal = {
let futurePlacedWidgets = gFuturePlacements.get(aArea);
let savedPlacements =
gSavedState && gSavedState.placements && gSavedState.placements[aArea];
let defaultPlacements = gAreas.get(aArea).get("defaultPlacements");
let defaultPlacements;
if (
CustomizableUI.verticalTabsEnabled &&
gAreas.get(aArea).has("verticalTabsDefaultPlacements")
) {
defaultPlacements = gAreas
.get(aArea)
.get("verticalTabsDefaultPlacements");
} else {
defaultPlacements = gAreas.get(aArea).get("defaultPlacements");
}
if (
!savedPlacements ||
!savedPlacements.length ||
@ -982,6 +1048,9 @@ var CustomizableUIInternal = {
props.get("type") == CustomizableUI.TYPE_TOOLBAR &&
!gPlacements.has(aName)
) {
lazy.log.debug(
`registerArea ${aName}, no gPlacements yet, nothing to restore`
);
// Guarantee this area exists in gFuturePlacements, to avoid checking it in
// various places elsewhere.
if (!gFuturePlacements.has(aName)) {
@ -1121,6 +1190,9 @@ var CustomizableUIInternal = {
this.getCustomizationTarget(aToolbar)
);
} finally {
lazy.log.debug(
`registerToolbarNode for ${area}, tabstripAreasReady? ${this.tabstripAreasReady}`
);
this.endBatchUpdate();
}
},
@ -1798,6 +1870,10 @@ var CustomizableUIInternal = {
},
isSpecialWidget(aId) {
if (aId === null) {
lazy.log.debug("isSpecialWidget was passed null");
return false;
}
aId = this._getSpecialIdForNode(aId);
return (
aId.startsWith(kSpecialWidgetPfx) ||
@ -2647,6 +2723,22 @@ var CustomizableUIInternal = {
);
},
getSavedHorizontalSnapshotState() {
let state = null;
let prefValue = lazy.horizontalPlacementsPref;
if (prefValue) {
try {
state = JSON.parse(prefValue);
} catch (e) {
lazy.log.warn(
`Failed to parse value of ${kPrefCustomizationHorizontalTabstrip}`,
e
);
}
}
return state;
},
// Note that this does not populate gPlacements, which is done lazily.
// The panel area is an exception here.
loadSavedState() {
@ -2714,6 +2806,16 @@ var CustomizableUIInternal = {
if (!restored) {
lazy.log.debug("Restoring " + aArea + " from default state");
let defaults = gAreas.get(aArea).get("defaultPlacements");
if (
CustomizableUI.verticalTabsEnabled &&
gAreas.get(aArea).has("verticalTabsDefaultPlacements")
) {
lazy.log.debug(
"Using verticalTabsDefaultPlacements to restore " + aArea
);
defaults = gAreas.get(aArea).get("verticalTabsDefaultPlacements");
}
if (defaults) {
for (let id of defaults) {
this.addWidgetToArea(id, aArea, null, true);
@ -2749,31 +2851,115 @@ var CustomizableUIInternal = {
}
},
restoreSavedHorizontalTabStripState(
savedPlacements = this.getSavedHorizontalSnapshotState(),
isInitializing = false
) {
const tabstripAreaId = CustomizableUI.AREA_TABSTRIP;
lazy.log.debug(
`restoreSavedHorizontalTabStripState, ${kPrefCustomizationHorizontalTabstrip} contained:`,
savedPlacements
);
// If there's no saved state, or it doesn't pass the sniff test, use
// default placements instead
if (
!(
Array.isArray(savedPlacements) &&
savedPlacements.includes("tabbrowser-tabs")
)
) {
savedPlacements = gAreas.get(tabstripAreaId).get("defaultPlacements");
lazy.log.debug(`Using defaultPlacements for ${tabstripAreaId}`);
}
lazy.log.debug(
`Replacing existing placements: ${gPlacements.get(
tabstripAreaId
)}, with ${savedPlacements}.`
);
// Restore the tabstrip to either saved or default placements
this.beginBatchUpdate();
for (let [index, widgetId] of savedPlacements.entries()) {
this.addWidgetToArea(widgetId, tabstripAreaId, index, isInitializing);
}
// Wipe the pref now that state is restored
Services.prefs.clearUserPref(kPrefCustomizationHorizontalTabstrip);
// The vertical tabstrip area is supposed to be empty when we switch back to horizontal
if (gPlacements.get(CustomizableUI.AREA_VERTICAL_TABSTRIP)?.length) {
lazy.log.warn(
`Widgets remain in ${CustomizableUI.AREA_VERTICAL_TABSTRIP}:`,
gPlacements.get(CustomizableUI.AREA_VERTICAL_TABSTRIP)
);
}
this.endBatchUpdate();
},
saveHorizontalTabStripState(placements = []) {
if (!placements.length) {
placements = this.getAreaPlacementsForSaving(
CustomizableUI.AREA_TABSTRIP
);
}
let serialized = JSON.stringify(placements, this.serializerHelper);
lazy.log.debug("Saving horizontal tabstrip state.", serialized);
Services.prefs.setCharPref(
kPrefCustomizationHorizontalTabstrip,
serialized
);
},
getAreaPlacementsForSaving(area) {
// An early call to saveState can occur before all the lazy-area building is complete
let placements;
if (this.isAreaLazy(area) && gFuturePlacements.has(area)) {
placements = [...gFuturePlacements.get(area)];
} else if (gPlacements.has(area)) {
placements = gPlacements.get(area);
}
// Merge in previously saved areas if not present in gPlacements/gFuturePlacements.
// This way, state is still persisted for e.g. temporarily disabled
// add-ons - see bug 989338.
if (!placements && gSavedState && gSavedState.placements?.[area]) {
placements = gSavedState.placements[area];
}
lazy.log.debug(
`getAreaPlacementsForSaving for area: ${area}, gPlacements for area: ${gPlacements.get(
area
)}, returning: ${placements}`
);
return placements;
},
saveState() {
if (gInBatchStack || !gDirty) {
return;
}
// Clone because we want to modify this map:
let placements = new Map();
// Because of Bug 989338 and the risk of having area ids that aren't yet registered,
// we collect the areas from both gPlacements and gSavedState rather than gAreas.
let allAreaIds = new Set([...gPlacements.keys()]);
if (gSavedState?.placements) {
for (let area of Object.keys(gSavedState.placements)) {
allAreaIds.add(area);
}
}
for (let area of allAreaIds) {
placements.set(area, this.getAreaPlacementsForSaving(area));
}
let state = {
placements: new Map(gPlacements),
placements,
seen: gSeenWidgets,
dirtyAreaCache: gDirtyAreaCache,
currentVersion: kVersion,
newElementCount: gNewElementCount,
};
// Merge in previously saved areas if not present in gPlacements.
// This way, state is still persisted for e.g. temporarily disabled
// add-ons - see bug 989338.
if (gSavedState && gSavedState.placements) {
for (let area of Object.keys(gSavedState.placements)) {
if (!state.placements.has(area)) {
let placements = gSavedState.placements[area];
state.placements.set(area, placements);
}
}
}
lazy.log.debug("Saving state.");
let serialized = JSON.stringify(state, this.serializerHelper);
lazy.log.debug("State saved as: " + serialized);
@ -3041,7 +3227,7 @@ var CustomizableUIInternal = {
// Returns true if the area will eventually lazily restore (but hasn't yet).
isAreaLazy(aArea) {
if (gPlacements.has(aArea)) {
if (gPlacements.has(aArea) || !gAreas.has(aArea)) {
return false;
}
return gAreas.get(aArea).get("type") == CustomizableUI.TYPE_TOOLBAR;
@ -3320,6 +3506,10 @@ var CustomizableUIInternal = {
reset() {
gResetting = true;
// CUI reset also implies resetting verticalTabs back to false.
// We do this before the rest of the reset so widgets are reset to their non-vertical
// positions.
Services.prefs.setBoolPref("sidebar.verticalTabs", false);
this._resetUIState();
// Rebuild each registered area (across windows) to reflect the state that
@ -3651,6 +3841,9 @@ var CustomizableUIInternal = {
},
get inDefaultState() {
if (CustomizableUI.verticalTabsEnabled) {
return false;
}
for (let [areaId, props] of gAreas) {
let defaultPlacements = props
.get("defaultPlacements")
@ -3811,23 +4004,251 @@ var CustomizableUIInternal = {
CustomizableUI.setToolbarVisibility(toolbar, visibility == "true");
}
if (
aTopic === "nsPref:changed" &&
aData === kPrefSidebarVerticalTabsEnabled
) {
let sidebarRevampEnabled = Services.prefs.getBoolPref(
kPrefSidebarRevampEnabled,
false
if (aTopic === "nsPref:changed") {
this.reconcileSidebarPrefs(aData);
}
},
initializeForTabsOrientation(toVertical) {
lazy.log.debug(
`initializeForTabsOrientation, toVertical: ${toVertical}, gCurrentVerticalTabs: ${gCurrentVerticalTabs}`
);
if (!toVertical) {
const savedPlacements = this.getSavedHorizontalSnapshotState();
lazy.log.debug(
"initializeForTabsOrientation, savedPlacements",
savedPlacements
);
let verticalTabsEnabled = Services.prefs.getBoolPref(
kPrefSidebarVerticalTabsEnabled,
false
if (savedPlacements) {
// We're startup up with horizontal tabs, but there are saved placements for the
// horizontal tab strip, so its possible the verticalTabs pref was updated outside
// of normal use. Make sure to restore those tabstrip widget placements
this.restoreSavedHorizontalTabStripState(savedPlacements, true);
} else {
// This is the default state and normal initialization will do everything necessary
}
gCurrentVerticalTabs = false;
return;
}
// If the UI was already customized and saved, the earlier call to loadSavedState will
// have populated gSavedState from the pref. If not, we need to move the tabs into the
// vertical tabs area in the gSavedState. Then, the normal build-areas lifecycle
// can populate the needed toolbar placements and elements.
lazy.log.debug(
"initializeForTabsOrientation, toVertical=true, gSavedState",
gSavedState
);
// If there are saved placement customizations, we need to manually move widgets
// around before we restore this state
let savedPlacements = gSavedState?.placements || {};
if (!savedPlacements[CustomizableUI.AREA_VERTICAL_TABSTRIP]?.length) {
savedPlacements[CustomizableUI.AREA_VERTICAL_TABSTRIP] =
gAreas
.get(CustomizableUI.AREA_VERTICAL_TABSTRIP)
.get("verticalTabsDefaultPlacements") || [];
lazy.log.debug(
"initializeForTabsOrientation, using defaults for AREA_VERTICAL_TABSTRIP",
savedPlacements[CustomizableUI.AREA_VERTICAL_TABSTRIP]
);
if (verticalTabsEnabled && !sidebarRevampEnabled) {
Services.prefs.setBoolPref(kPrefSidebarRevampEnabled, true);
}
let tabstripPlacements =
savedPlacements[CustomizableUI.AREA_TABSTRIP] || [];
// also pick up any widgets already in gFuturePlacements so we can wipe that
if (gFuturePlacements.has(CustomizableUI.AREA_TABSTRIP)) {
for (let id of gFuturePlacements.get(CustomizableUI.AREA_TABSTRIP)) {
if (!tabstripPlacements.includes(id)) {
tabstripPlacements.push(id);
}
}
gFuturePlacements.delete(CustomizableUI.AREA_TABSTRIP);
}
// Take a copy we can save and restore to, ensuring there's a sane default
let savedTabstripPlacements = tabstripPlacements.length
? [...tabstripPlacements]
: gAreas.get(CustomizableUI.AREA_TABSTRIP).get("defaultPlacements");
// now we can remove the saved placements so they don't get picked back up again later in startup
delete savedPlacements[CustomizableUI.AREA_TABSTRIP];
let widgetsMoved = [];
for (let widgetId of tabstripPlacements) {
if (widgetId == "tabbrowser-tabs") {
lazy.log.debug(
`Moving saved tabbrowser-tabs to AREA_VERTICAL_TABSTRIP`
);
this.addWidgetToArea(
widgetId,
CustomizableUI.AREA_VERTICAL_TABSTRIP,
null,
true
);
continue;
}
// if this is a extension, those are handled in a toolbarvisibilitychange handler in browser-addons.js
if (CustomizableUI.isWebExtensionWidget(widgetId)) {
lazy.log.debug(`Skipping a webextension saved placement ${widgetId}`);
continue;
}
// Everything else gets moved to the nav-bar area while tabs are vertical
lazy.log.debug(`Moving saved placement ${widgetId} to nav-bar`);
this.addWidgetToArea(widgetId, CustomizableUI.AREA_NAVBAR, null, true);
widgetsMoved.push(widgetId);
}
lazy.log.debug(
"initializeForTabsOrientation, widgets moved:",
widgetsMoved
);
if (widgetsMoved.length) {
// We've updated the areas, so we don't need to do this again post-initialization
gCurrentVerticalTabs = true;
}
// If we've ended up with a non-default CUI state and vertical tabs enabled, ensure
// there's a sane snapshot to revert to
if (!lazy.horizontalPlacementsPref) {
lazy.log.debug(
`verticalTabsEnabled but ${kPrefCustomizationHorizontalTabstrip} is empty`
);
CustomizableUIInternal.saveHorizontalTabStripState(
savedTabstripPlacements
);
}
},
reconcileSidebarPrefs(prefChanged) {
let sidebarRevampEnabled = Services.prefs.getBoolPref(
kPrefSidebarRevampEnabled,
false
);
let verticalTabsEnabled = Services.prefs.getBoolPref(
kPrefSidebarVerticalTabsEnabled,
false
);
lazy.log.debug(
`reconcileSidebarPrefs, kPrefSidebarRevampEnabled: {sidebarRevampEnabled}, kPrefSidebarVerticalTabsEnabled: ${verticalTabsEnabled}`
);
switch (prefChanged) {
case kPrefSidebarVerticalTabsEnabled: {
// We need to also enable sidebar.revamp if vertical tabs gets enabled
if (verticalTabsEnabled && !sidebarRevampEnabled) {
Services.prefs.setBoolPref(kPrefSidebarRevampEnabled, true);
}
break;
}
case kPrefSidebarRevampEnabled: {
// We need to also disable vertical tabs if sidebar.revamp is no longer enabled
if (!sidebarRevampEnabled && verticalTabsEnabled) {
lazy.log.debug(
`{kPrefSidebarRevampEnabled} disabled, so also disabling ${kPrefSidebarVerticalTabsEnabled}`
);
Services.prefs.setBoolPref(kPrefSidebarVerticalTabsEnabled, false);
}
break;
}
}
},
get tabstripAreasReady() {
return (
gBuildAreas.get(CustomizableUI.AREA_TABSTRIP)?.size &&
gBuildAreas.get(CustomizableUI.AREA_VERTICAL_TABSTRIP)?.size
);
},
updateTabStripOrientation() {
if (!this.tabstripAreasReady) {
lazy.log.debug("tabstrip build areas not yet ready");
return;
}
let toVertical = CustomizableUI.verticalTabsEnabled;
if (toVertical === gCurrentVerticalTabs) {
lazy.log.debug("early return as the value hasn't changed");
return;
}
lazy.log.debug(
`verticalTabs changed, from ${gCurrentVerticalTabs}, to ${toVertical}`
);
if (toVertical && gCurrentVerticalTabs !== null) {
// Stash current placements as a state we can restore to when going back to horizontal tabs
lazy.log.debug(
"Switching to vertical tabs post-initialization, so capturing tabstrip placements snapshot"
);
CustomizableUIInternal.saveHorizontalTabStripState();
}
gCurrentVerticalTabs = toVertical;
function changeWidgetRemovability(widgetId, removable) {
let widget = CustomizableUI.getWidget(widgetId);
for (let { node } of widget.instances) {
if (node) {
node.setAttribute("removable", removable.toString());
}
}
}
// Normally these aren't removable, but for this operation only we need to move them
changeWidgetRemovability("tabbrowser-tabs", true);
changeWidgetRemovability("alltabs-button", true);
if (toVertical) {
lazy.log.debug(
`Switching to verticalTabs=true in updateTabStripOrientation`
);
gDirty = true;
if (
!Services.prefs.getCharPref(kPrefCustomizationHorizontalTabsBackup, "")
) {
// Before we switch for the first time, take a back up just in case we need an escape hatch
Services.prefs.setCharPref(
kPrefCustomizationHorizontalTabsBackup,
Services.prefs.getCharPref(kPrefCustomizationState, "")
);
}
CustomizableUI.beginBatchUpdate();
// Remove non-default widgets to the nav-bar
for (let id of CustomizableUI.getWidgetIdsInArea("TabsToolbar")) {
if (id == "tabbrowser-tabs") {
CustomizableUI.addWidgetToArea(
id,
CustomizableUI.AREA_VERTICAL_TABSTRIP
);
continue;
}
if (!CustomizableUI.isWidgetRemovable(id)) {
continue;
}
// if this is a extension, those are handled in a toolbarvisibilitychange handler in browser-addons.js
if (CustomizableUI.isWebExtensionWidget(id)) {
continue;
}
// Everything else gets moved to the nav-bar area while tabs are vertical
CustomizableUI.addWidgetToArea(id, CustomizableUI.AREA_NAVBAR);
}
CustomizableUI.endBatchUpdate();
} else {
// We're switching to vertical in this session; pull saved state from pref and update placements
this.restoreSavedHorizontalTabStripState();
}
// Give the sidebar a chance to adjust before we show/hide the toolbars
lazy.log.debug("CustomizableUI notifying tabstrip-orientation-change");
Services.obs.notifyObservers(null, "tabstrip-orientation-change", {
isVertical: toVertical,
});
lazy.log.debug("Reverting widgets to be non-removable");
changeWidgetRemovability("tabbrowser-tabs", false);
changeWidgetRemovability("alltabs-button", false);
this.setToolbarVisibility(
CustomizableUI.AREA_VERTICAL_TABSTRIP,
toVertical
);
},
};
Object.freeze(CustomizableUIInternal);
@ -3844,6 +4265,12 @@ export var CustomizableUI = {
* Constant reference to the ID of the tabstrip toolbar.
*/
AREA_TABSTRIP: "TabsToolbar",
/**
* Constant reference to the ID of the vertical tabstrip toolbar.
*/
AREA_VERTICAL_TABSTRIP: "vertical-tabs",
/**
* Constant reference to the ID of the bookmarks toolbar.
*/
@ -3920,6 +4347,10 @@ export var CustomizableUI = {
},
},
get verticalTabsEnabled() {
return lazy.verticalTabsPref;
},
/**
* Add a listener object that will get fired for various events regarding
* customization.
@ -4437,7 +4868,7 @@ export var CustomizableUI = {
throw new Error("Unknown customization area: " + aArea);
}
if (!gPlacements.has(aArea)) {
throw new Error("Area not yet restored");
throw new Error(`Area ${aArea} not yet restored`);
}
// We need to clone this, as we don't want to let consumers muck with placements
@ -4751,6 +5182,9 @@ export var CustomizableUI = {
* @return true if the widget was provided by an extension, false otherwise.
*/
isWebExtensionWidget(aWidgetId) {
if (typeof aWidgetId !== "string") {
return false;
}
let widget = CustomizableUI.getWidget(aWidgetId);
return widget?.webExtension || aWidgetId.endsWith("-browser-action");
},
@ -5563,6 +5997,7 @@ class OverflowableToolbar {
if (!this.#initialized) {
Services.obs.removeObserver(this, "browser-delayed-startup-finished");
Services.prefs.removeObserver(kPrefSidebarVerticalTabsEnabled, this);
Services.prefs.removeObserver(kPrefSidebarRevampEnabled, this);
return;
}

Просмотреть файл

@ -261,7 +261,7 @@ var SidebarController = {
return this._toolbarButton;
},
async init() {
init() {
// Initialize with side effects
this.SidebarManager;
@ -318,9 +318,6 @@ var SidebarController = {
this._mainResizeObserverAdded = true;
}
if (this.sidebarVerticalTabsEnabled) {
this.toggleTabstrip();
}
let newTabButton = document.getElementById("vertical-tabs-newtab-button");
if (!this._verticalNewTabListenerAdded) {
newTabButton.addEventListener("command", event => {
@ -343,6 +340,11 @@ var SidebarController = {
this._switcherListenersAdded = true;
}
}
// We need to update the tab strip for vertical tabs during init
// as there will be no tabstrip-orientation-change event
if (CustomizableUI.verticalTabsEnabled) {
this.toggleTabstrip();
}
// sets the sidebar to the left or right, based on a pref
this.setPosition();
@ -353,6 +355,10 @@ var SidebarController = {
Services.obs.addObserver(this, "intl:app-locales-changed");
this._localesObserverAdded = true;
}
if (!this._tabstripOrientationObserverAdded) {
Services.obs.addObserver(this, "tabstrip-orientation-change");
this._tabstripOrientationObserverAdded = true;
}
this._initDeferred.resolve();
},
@ -371,6 +377,8 @@ var SidebarController = {
}
Services.obs.removeObserver(this, "intl:app-locales-changed");
Services.obs.removeObserver(this, "tabstrip-orientation-change");
delete this._tabstripOrientationObserverAdded;
if (this._observer) {
this._observer.disconnect();
@ -406,6 +414,11 @@ var SidebarController = {
if (this.revampComponentsLoaded) {
this.sidebarMain.requestUpdate();
}
break;
}
case "tabstrip-orientation-change": {
this.promiseInitialized.then(() => this.toggleTabstrip());
break;
}
}
},
@ -592,7 +605,8 @@ var SidebarController = {
if (!this._sidebars.get(this.lastOpenedId)) {
this.lastOpenedId = this.DEFAULT_SIDEBAR_ID;
}
await this.init();
this._inited = false;
this.init();
},
/**
@ -1516,40 +1530,33 @@ var SidebarController = {
},
toggleTabstrip() {
let tabStrip = document.getElementById("tabbrowser-tabs");
let toVerticalTabs = CustomizableUI.verticalTabsEnabled;
let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
let verticalTabs = document.getElementById("vertical-tabs");
let currentScrollOrientation = arrowScrollbox.getAttribute("orient");
let tabsToolbarWidgets = CustomizableUI.getWidgetIdsInArea("TabsToolbar");
let tabstripPlacement = tabsToolbarWidgets.findIndex(
item => item == "tabbrowser-tabs"
);
if (
(!toVerticalTabs && currentScrollOrientation !== "vertical") ||
(toVerticalTabs && currentScrollOrientation === "vertical")
) {
// Nothing to update
return;
}
if (this.sidebarVerticalTabsEnabled) {
let tabStrip = gBrowser.tabContainer;
if (toVerticalTabs) {
this.toggleExpanded(this._sidebarMain.expanded);
arrowScrollbox.setAttribute("orient", "vertical");
tabStrip.setAttribute("orient", "vertical");
verticalTabs.append(tabStrip);
} else {
arrowScrollbox.setAttribute("orient", "horizontal");
tabStrip.removeAttribute("expanded");
tabStrip.setAttribute("orient", "horizontal");
// make sure we put the tabstrip back in its original position in the TabsToolbar
if (tabstripPlacement < tabsToolbarWidgets.length) {
document
.getElementById("TabsToolbar-customization-target")
.insertBefore(
tabStrip,
document.getElementById(tabsToolbarWidgets[tabstripPlacement + 1])
);
} else {
document
.getElementById("TabsToolbar-customization-target")
.append(tabStrip);
}
}
verticalTabs.toggleAttribute("visible", this.sidebarVerticalTabsEnabled);
let verticalToolbar = document.getElementById(
CustomizableUI.AREA_VERTICAL_TABSTRIP
);
verticalToolbar.toggleAttribute("visible", toVerticalTabs);
},
};
@ -1602,10 +1609,5 @@ XPCOMUtils.defineLazyPreferenceGetter(
SidebarController,
"sidebarVerticalTabsEnabled",
"sidebar.verticalTabs",
false,
() => {
if (!SidebarController.uninitializing) {
SidebarController.toggleTabstrip();
}
}
false
);

Просмотреть файл

@ -4,6 +4,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
MARIONETTE_MANIFESTS += ["tests/marionette/manifest.toml"]
JAR_MANIFESTS += ["jar.mn"]
BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.toml"]

Просмотреть файл

@ -40,6 +40,10 @@ run-if = ["os == 'mac'"] # Mac only feature
["browser_toolbar_sidebar_button.js"]
["browser_verticalTabs_widget_placements.js"]
["browser_vertical_tabs.js"]
["browser_vertical_tabs_cui_reset.js"]
["browser_view_sidebar_menu.js"]

Просмотреть файл

@ -0,0 +1,115 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
registerCleanupFunction(async function resetToolbar() {
await CustomizableUI.reset();
Services.prefs.clearUserPref(kPrefCustomizationState);
Services.prefs.clearUserPref(kPrefCustomizationHorizontalTabstrip);
});
/**
* Test that with some widgets customized into the TabsToolbar,
* they are moved to the nav-bar as expected when the verticalTabs pref is enabled,
* and confirm everything was restored as expected when we flip back.
*/
add_task(async function moveAndRestoreTabsToolbarWidgets() {
const defaultNavbarPlacements = CustomizableUI.getWidgetIdsInArea(
CustomizableUI.AREA_NAVBAR
);
const defaultHorizontalTabStripPlacements = CustomizableUI.getWidgetIdsInArea(
CustomizableUI.AREA_TABSTRIP
);
CustomizableUI.addWidgetToArea(
"home-button",
CustomizableUI.AREA_TABSTRIP,
0
);
CustomizableUI.addWidgetToArea("panic-button", CustomizableUI.AREA_TABSTRIP);
CustomizableUI.addWidgetToArea(
"privatebrowsing-button",
CustomizableUI.AREA_TABSTRIP
);
const horizontalTabStripPlacements = CustomizableUI.getWidgetIdsInArea(
CustomizableUI.AREA_TABSTRIP
);
Assert.equal(
horizontalTabStripPlacements.length,
defaultHorizontalTabStripPlacements.length + 3,
"tabstrip has 3 new widget placements"
);
await SpecialPowers.pushPrefEnv({ set: [["sidebar.verticalTabs", true]] });
Assert.ok(
CustomizableUI.verticalTabsEnabled,
"CustomizableUI verticalTabsEnabled getter reflects pref value"
);
Assert.ok(
BrowserTestUtils.isVisible(document.getElementById("TabsToolbar")),
"#TabsToolbar is still visible"
);
// The tabs-widget should be in the vertical tabs area
Assert.ok(
CustomizableUI.getWidgetIdsInArea(
CustomizableUI.AREA_VERTICAL_TABSTRIP
).includes("tabbrowser-tabs"),
"The tabs container moved to the vertical tabs area"
);
// The widgets we added to TabsToolbar should have been appened to nav-bar
let newNavbarPlacements = CustomizableUI.getWidgetIdsInArea(
CustomizableUI.AREA_NAVBAR
);
let expectedMovedWidgetIds = [
"alltabs-button",
"panic-button",
"privatebrowsing-button",
];
Assert.deepEqual(
newNavbarPlacements.slice(
newNavbarPlacements.length - expectedMovedWidgetIds.length
),
expectedMovedWidgetIds,
"All the tabstrip widgets were appended to the nav-bar"
);
let prefSnapshot = JSON.parse(
Services.prefs.getStringPref("browser.uiCustomization.horizontalTabstrip")
);
Assert.deepEqual(
prefSnapshot,
horizontalTabStripPlacements,
"The previous tab strip placements were stored in the pref"
);
await SpecialPowers.pushPrefEnv({ set: [["sidebar.verticalTabs", false]] });
Assert.ok(
!CustomizableUI.verticalTabsEnabled,
"CustomizableUI verticalTabsEnabled getter reflects pref value"
);
Assert.ok(
BrowserTestUtils.isVisible(document.getElementById("TabsToolbar")),
"#TabsToolbar is now visible"
);
Assert.ok(
CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_TABSTRIP).includes(
"tabbrowser-tabs"
),
"The tabs container moved back to the tab strip"
);
Assert.deepEqual(
CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_TABSTRIP),
horizontalTabStripPlacements,
"The tabstrip widgets were restored in the expected order"
);
Assert.deepEqual(
CustomizableUI.getWidgetIdsInArea(CustomizableUI.AREA_NAVBAR),
defaultNavbarPlacements,
"The nav-bar widgets are back in their original state"
);
});

Просмотреть файл

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_setup(() =>
SpecialPowers.pushPrefEnv({
set: [["sidebar.verticalTabs", true]],
})
);
add_task(async function test_cui_reset_vertical_tabs() {
ok(
Services.prefs.getBoolPref("sidebar.verticalTabs"),
"Vertical tabs enabled"
);
CustomizableUI.reset();
ok(
!Services.prefs.getBoolPref("sidebar.verticalTabs"),
"Vertical tabs disabled"
);
});

Просмотреть файл

@ -14,6 +14,13 @@ function imageBufferFromDataURI(encodedImageData) {
return Uint8Array.from(decodedImageData, byte => byte.charCodeAt(0)).buffer;
}
const kPrefCustomizationState = "browser.uiCustomization.state";
const kPrefCustomizationHorizontalTabstrip =
"browser.uiCustomization.horizontalTabstrip";
// Ensure we clear any previous uiCustomization pref values
Services.prefs.clearUserPref(kPrefCustomizationState);
Services.prefs.clearUserPref(kPrefCustomizationHorizontalTabstrip);
/* global browser */
const extData = {
manifest: {

Просмотреть файл

@ -0,0 +1,4 @@
[DEFAULT]
tags = "local"
["test_initialize_vertical_tabs.py"]

Просмотреть файл

@ -0,0 +1,154 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import json
from marionette_harness import MarionetteTestCase
vertical_parent_id = "vertical-tabs"
horizontal_parent_id = "TabsToolbar-customization-target"
snapshot_pref = "browser.uiCustomization.horizontalTabstrip"
customization_pref = "browser.uiCustomization.state"
class TestInitializeVerticalTabs(MarionetteTestCase):
def setUp(self):
super().setUp()
self.marionette.set_context("chrome")
def tearDown(self):
try:
# Make sure subsequent tests get a clean profile
self.marionette.restart(in_app=False, clean=True)
finally:
super().tearDown()
def restart_with_prefs(self, prefs):
# We need to quit the browser and restart with the prefs already set
# in order to examine the startup behavior
for name, value in prefs.items():
if value is None:
self.marionette.clear_pref(name)
else:
self.marionette.set_pref(name, value)
self.marionette.restart(clean=False, in_app=True)
self.marionette.set_context("chrome")
def get_area_widgets(self, area):
return self.marionette.execute_script(
f"return CustomizableUI.getWidgetIdsInArea(CustomizableUI.{area})"
)
def test_vertical_widgets_in_area(self):
# A clean startup in verticalTabs mode; we should get all the defaults
self.restart_with_prefs(
{
"sidebar.revamp": True,
"sidebar.verticalTabs": True,
customization_pref: None,
snapshot_pref: None,
}
)
horiz_tab_ids = self.get_area_widgets("AREA_TABSTRIP")
vertical_tab_ids = self.get_area_widgets("AREA_VERTICAL_TABSTRIP")
self.assertEqual(
len(horiz_tab_ids),
0,
msg="The horizontal tabstrip area is empty",
)
self.assertEqual(
len(vertical_tab_ids),
1,
msg="The vertical tabstrip area has a single widget in it",
)
# Check we're able to recover if we initialize with vertical tabs enabled
# and no saved pref for the horizontal tab strip placements
self.marionette.set_pref("sidebar.verticalTabs", False)
horiz_tab_ids = self.get_area_widgets("AREA_TABSTRIP")
vertical_tab_ids = self.get_area_widgets("AREA_VERTICAL_TABSTRIP")
# Make sure we ended up with sensible defaults
self.assertEqual(
horiz_tab_ids,
[
"firefox-view-button",
"tabbrowser-tabs",
"new-tab-button",
"alltabs-button",
],
msg="The tabstrip was populated with the expected defaults",
)
self.assertEqual(
len(vertical_tab_ids),
0,
msg="The vertical tabstrip area was emptied",
)
def test_restore_tabstrip_customizations(self):
fixture_prefs = {
"sidebar.revamp": True,
"sidebar.verticalTabs": False,
}
self.restart_with_prefs(
{
**fixture_prefs,
customization_pref: None,
snapshot_pref: None,
}
)
# Add a widget at the start of the horizontal tabstrip
# This is synchronous and should result in updating the UI and the saved state in uiCustomization pref
self.marionette.execute_script(
"CustomizableUI.addWidgetToArea('panic-button', CustomizableUI.AREA_TABSTRIP, 0)"
)
saved_state = json.loads(self.marionette.get_pref(customization_pref))
horiz_tab_ids = self.get_area_widgets("AREA_TABSTRIP")
self.assertTrue(
"panic-button" in horiz_tab_ids, "The widget we added is in the tabstrip"
)
self.assertTrue(
"panic-button" in saved_state["placements"]["TabsToolbar"],
"The widget we added is included in the saved customization state",
)
# Restart with vertical tabs enabled, leaving the uiCustomizations prefs as-is
# We want to ensure initialization puts us in a good state without needing user
# input to trigger the orientation change
fixture_prefs["sidebar.verticalTabs"] = True
self.restart_with_prefs(fixture_prefs)
saved_state = json.loads(self.marionette.get_pref(customization_pref))
horiz_tab_ids = self.get_area_widgets("AREA_TABSTRIP")
nav_bar_ids = self.get_area_widgets("AREA_NAVBAR")
self.assertEqual(
len(horiz_tab_ids),
0,
msg="The horizontal tabstrip area is empty",
)
self.assertTrue(
"panic-button" in nav_bar_ids, "The widget we added has moved to the navbar"
)
# Restart with horizontal tabs enabled. We want to ensure customizing the
# panic-button into the tabstrip is correctly restored at initialization,
# without needing user-input to trigger the orientation change
fixture_prefs["sidebar.verticalTabs"] = False
self.restart_with_prefs(fixture_prefs)
horiz_tab_ids = self.get_area_widgets("AREA_TABSTRIP")
self.assertEqual(
horiz_tab_ids[0],
"panic-button",
msg="The customization was preserved after restarting in horizontal tabs mode",
)

Просмотреть файл

@ -19,6 +19,7 @@
["include:../../../../../browser/components/places/tests/marionette/manifest.toml"]
["include:../../../../../browser/components/search/test/marionette/manifest.toml"]
["include:../../../../../browser/components/sessionstore/test/marionette/manifest.toml"]
["include:../../../../../browser/components/sidebar/tests/marionette/manifest.toml"]
["include:../../../../../browser/components/tests/marionette/manifest.toml"]
# DOM tests