Dies ist dann die 'große' Lösung:
Gut gemacht, das gefällt mir.![]()
Dies ist dann die 'große' Lösung:
Gut gemacht, das gefällt mir.![]()
Sieht hier im Nightly so aus:
Ich hab mal KI bemüht.

(function () {
if (!window.gBrowser)
return;
const tabContextMenu = document.getElementById("tabContextMenu");
if (!tabContextMenu)
return;
// ------------------------------------------------------------
// 1. "Mehrere Tabs schließen" komplett ausblenden
// ------------------------------------------------------------
const closeOptions = document.getElementById("context_closeTabOptions");
if (closeOptions) {
closeOptions.hidden = true;
closeOptions.setAttribute("hidden", "true");
closeOptions.style.display = "none";
}
// ------------------------------------------------------------
// 2. Die drei Einträge aus dem Untermenü holen
// ------------------------------------------------------------
const closeStart = document.getElementById("context_closeTabsToTheStart");
const closeEnd = document.getElementById("context_closeTabsToTheEnd");
const closeOther = document.getElementById("context_closeOtherTabs");
// ------------------------------------------------------------
// 3. Eigenen Eintrag "Linke Tabs schließen" nicht mehr
// zusätzlich erzeugen, da Firefox ihn bereits besitzt.
// ------------------------------------------------------------
// Wir verwenden die vorhandenen Firefox-Menüeinträge.
// Dadurch bleiben deren originale Befehle und Aktualisierungen erhalten.
// ------------------------------------------------------------
// 4. Alle drei wirklich an den Anfang des Menüs verschieben
// ------------------------------------------------------------
// Wichtig:
// insertBefore(..., firstChild) funktioniert hier nicht zuverlässig,
// wenn mehrere Elemente nacheinander verschoben werden.
//
// Deshalb immer wieder den jeweils aktuellen ersten sichtbaren
// Menüpunkt holen.
function moveToTop(element) {
if (!element)
return;
const first = tabContextMenu.firstElementChild;
if (first !== element)
tabContextMenu.insertBefore(element, first);
}
// Reihenfolge am Ende:
// Linke Tabs schließen
// Rechte Tabs schließen
// Andere Tabs schließen
moveToTop(closeOther);
moveToTop(closeEnd);
moveToTop(closeStart);
// ------------------------------------------------------------
// 5. Falls Firefox das Menü beim Öffnen wieder aktualisiert,
// erneut anwenden.
// ------------------------------------------------------------
if (!tabContextMenu._closeTabsCustomHandler) {
tabContextMenu._closeTabsCustomHandler = true;
tabContextMenu.addEventListener("popupshowing", () => {
const closeOptions =
document.getElementById("context_closeTabOptions");
if (closeOptions) {
closeOptions.hidden = true;
closeOptions.setAttribute("hidden", "true");
closeOptions.style.display = "none";
}
const closeStart =
document.getElementById("context_closeTabsToTheStart");
const closeEnd =
document.getElementById("context_closeTabsToTheEnd");
const closeOther =
document.getElementById("context_closeOtherTabs");
moveToTop(closeOther);
moveToTop(closeEnd);
moveToTop(closeStart);
});
}
})();
Alles anzeigen
Weil dein Script vom Aufbau her mein Interesse geweckt hat, so habe ich es in Nightly 156 getestet, aber es verweigert seinen Dienst. Es wird nichts angezeigt, nur #context_closeTabOptions wirkt. Order auch geändert, trotzdem nicht. Deshalb habe ich "Keine Intelligenz" gefragt, und das kam als Antwort:
Hast du eine andere Idee, um die Einfachheit beizubehalten?
Linke Tabs schließen ist jetzt nicht mehr ausgegraut, wenn es keinen linken Tab gibt wie im Screenshot zu sehen ist. Dort sind nur 2 rechte Tabs.
Ist geändert.
Danke für den Hinweis.
Eben in einem meiner Nightlys:
Guten Morgen, Andreas!![]()
Habe es gefunden, schon mit Fox 88 hatte ich das in der userChrome.css zu stehen, und so bei allen übernommen.![]()
Wenn du das Script für Linke Tabs schließen meintest, da ist es jetzt mit eingearbeitet, das der Eintrag versteckt wird.
(function() {
if (location.href !== 'chrome://browser/content/browser.xhtml')
return;
// --- Mehrere Tabs schließen mit CSS aus Kontext-Tabs versteckt -------------
const style = document.createElement('style');
style.textContent = '#context_closeTabOptions { display: none !important; }';
document.documentElement.appendChild(style);
// ---------------------------------------------------------------------------
const tabContextMenu = document.getElementById('tabContextMenu');
const refItem = document.getElementById('context_undoCloseTab');
if (!tabContextMenu || !refItem)
return;
// Bereits vorhanden?
if (document.getElementById('context_closeLeftTabs'))
return;
const menuitem = document.createXULElement('menuitem');
menuitem.id = 'context_closeLeftTabs';
menuitem.setAttribute('label', 'Linke Tabs schließen');
// Default: deaktiviert bis wir prüfen
menuitem.disabled = true;
tabContextMenu.insertBefore(menuitem, refItem);
// Vor dem Anzeigen prüfen und ggf. ausgrauen
tabContextMenu.addEventListener('popupshowing', () => {
const contextTab = TabContextMenu.contextTab;
if (!contextTab) {
menuitem.disabled = true;
return;
}
const index = gBrowser.tabs.indexOf(contextTab);
// Wenn Index <= 0 gibt es keine linken Tabs -> ausgegraut
menuitem.disabled = index <= 0;
});
menuitem.addEventListener('command', () => {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const index = gBrowser.tabs.indexOf(contextTab);
if (index <= 0)
return;
// Tabs links vom Kontext-Tab ermitteln.
// Kopie verwenden, da sich gBrowser.tabs beim Schließen verändert.
const tabs = Array.from(gBrowser.tabs).slice(0, index);
for (const tab of tabs) {
if (!tab.closing)
gBrowser.removeTab(tab, {
animate: false
});
}
});
})();
Alles anzeigen

Ich nutze ein noch anderes Skript, aber auch da musste ich das per extra CSS entfernen.
Nun habe ich schon einige Füchse durchstöbert, keiner hat den Eintrag mehr. Ein unbefleckter, da ist er vorhanden. Muss morgen mal einen intensiv durchforsten, eventuell ist ja auch ein Script daran beteiligt. Wenn ich das gefunden habe, dann kümmere ich mich mal um deinen Vorschlag. Gute Nacht!![]()
Perfekt fände ich es, wenn dieser Eintrag dann automatisch ausgeblendet wird gleich im Skript:
Hmmm, habe ich im Kontext nicht mehr. Da muss ich erst einmal auf Pirsch gehen, wo ich das abgestellt habe.![]()
Ja, da habe ich aus Versehen ein Testscript eingefügt, ist in #4 ausgetauscht. Danke für den Hinweis.
Im Script aus #5, meinem Favoriten, ändere Zeile 35 mal so:
item.className = "menuitem";
so werden keine leeren Vierecke mehr angezeigt wie auch auf deinem Bild ersichtlich.
Auch dafür sage ich danke, gut zu wissen.![]()
Und noch eine Version zur Auswahl, mit drei Einträgen.

// ==UserScript==
// @name KontextCloseTabs.uc.js
// @description TabContextMenu = Linke, Rechte und Alle Tabs schließen
// Alle Tabs schließen mit Warnhinweis
// @compatibility Firefox 154+
// @include main
// @charset UTF-8
// @version 2026.08.20
// ==/UserScript==
(function () {
"use strict";
if (location.href !== "chrome://browser/content/browser.xhtml")
return;
const tabContextMenu = document.getElementById("tabContextMenu");
const undoCloseTab = document.getElementById("context_undoCloseTab");
if (!tabContextMenu || !undoCloseTab)
return;
// ------------------------------------------------------------
// Menüpunkt erzeugen (XUL)
// ------------------------------------------------------------
function createMenuItem(id, label) {
let item = document.getElementById(id);
if (!item) {
item = document.createXULElement("menuitem");
item.id = id;
item.setAttribute("label", label);
// role/class kann helfen, dass das Item korrekt dargestellt wird
item.className = "menuitem";
}
return item;
}
// ------------------------------------------------------------
// Linke Tabs schließen
// ------------------------------------------------------------
const closeLeft = createMenuItem(
"context_closeLeftTabs",
"Linke Tabs schließen"
);
closeLeft.addEventListener("command", function () {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const tabs = Array.from(gBrowser.tabs);
for (const tab of tabs) {
if (tab === contextTab)
break;
if (!tab.pinned)
gBrowser.removeTab(tab, { animate: true });
}
});
// ------------------------------------------------------------
// Rechte Tabs schließen
// ------------------------------------------------------------
const closeRight = createMenuItem(
"context_closeRightTabs",
"Rechte Tabs schließen"
);
closeRight.addEventListener("command", function () {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const tabs = Array.from(gBrowser.tabs);
const index = tabs.indexOf(contextTab);
if (index === -1)
return;
for (let i = tabs.length - 1; i > index; i--) {
const tab = tabs[i];
if (!tab.pinned)
gBrowser.removeTab(tab, { animate: true });
}
});
// ------------------------------------------------------------
// Alle Tabs schließen (mit Bestätigung)
// ------------------------------------------------------------
const closeAll = createMenuItem(
"context_closeAllTabsCustom",
"Alle Tabs schließen…"
);
closeAll.addEventListener("command", function () {
// Bestätigung (einfacher confirm)
const msg = "Wirklich alle Tabs schließen? Diese Aktion kann nicht rückgängig gemacht werden.";
if (!confirm(msg))
return;
const tabs = Array.from(gBrowser.tabs);
// Schließe alle nicht-gepinnten Tabs von rechts nach links
for (let i = tabs.length - 1; i >= 0; i--) {
const tab = tabs[i];
if (!tab.pinned) {
try {
gBrowser.removeTab(tab, { animate: true });
} catch (e) {
// ignore
}
}
}
});
// ------------------------------------------------------------
// Menü einfügen
// ------------------------------------------------------------
// Falls Items bereits existieren, werden sie wiederverwendet; sonst eingefügt
tabContextMenu.insertBefore(closeLeft, undoCloseTab);
tabContextMenu.insertBefore(closeRight, undoCloseTab);
tabContextMenu.insertBefore(closeAll, undoCloseTab);
// ------------------------------------------------------------
// Menü beim Öffnen aktualisieren (aktiv/disabled)
// ------------------------------------------------------------
tabContextMenu.addEventListener("popupshowing", function () {
const contextTab = TabContextMenu.contextTab;
if (!contextTab) {
// Wenn kein Kontext-Tab, die Einträge verstecken (sicherer)
closeLeft.hidden = true;
closeRight.hidden = true;
closeAll.hidden = true;
return;
}
closeLeft.hidden = false;
closeRight.hidden = false;
closeAll.hidden = false;
const tabs = Array.from(gBrowser.tabs);
const index = tabs.indexOf(contextTab);
if (index === -1)
return;
let hasLeft = false;
let hasRight = false;
let hasAnyCloseable = false;
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (tab.pinned)
continue;
if (i < index)
hasLeft = true;
if (i > index)
hasRight = true;
hasAnyCloseable = true;
}
closeLeft.disabled = !hasLeft;
closeRight.disabled = !hasRight;
// closeAll ist aktiviert, wenn es mindestens einen schließbaren Tab gibt
closeAll.disabled = !hasAnyCloseable;
});
})();
Alles anzeigen
Das hat KI dazu gesagt
Und das auch noch (für linke Tabs).![]()
(function() {
if (location.href !== 'chrome://browser/content/browser.xhtml')
return;
const tabContextMenu = document.getElementById('tabContextMenu');
const refItem = document.getElementById('context_undoCloseTab');
if (!tabContextMenu || !refItem)
return;
// Nicht doppelt einfügen
if (document.getElementById('context_closeLeftTabs'))
return;
const menuitem = document.createXULElement('menuitem');
menuitem.id = 'context_closeLeftTabs';
menuitem.setAttribute('label', 'Linke Tabs schließen');
tabContextMenu.insertBefore(menuitem, refItem);
menuitem.addEventListener('command', function() {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const pos = gBrowser.tabs.indexOf(contextTab);
if (pos <= 0)
return;
// Kopie erstellen, damit das Entfernen die Iteration nicht beeinflusst
const tabsToClose = Array.from(gBrowser.tabs).slice(0, pos);
for (const tab of tabsToClose) {
if (!tab.closing)
gBrowser.removeTab(tab, { animate: false });
}
});
})();
Alles anzeigen
Ich kann dir das anbieten, die vier Einträge werden damit abgedeckt.

(function () {
"use strict";
if (location.href !== "chrome://browser/content/browser.xhtml")
return;
const tabContextMenu = document.getElementById("tabContextMenu");
const undoCloseTab = document.getElementById("context_undoCloseTab");
if (!tabContextMenu || !undoCloseTab)
return;
// ------------------------------------------------------------
// Menüpunkt erzeugen
// ------------------------------------------------------------
function createMenuItem(id, label) {
let item = document.getElementById(id);
if (!item) {
item = document.createXULElement("menuitem");
item.id = id;
item.setAttribute("label", label);
}
return item;
}
// ------------------------------------------------------------
// Linke Tabs schließen
// ------------------------------------------------------------
const closeLeft = createMenuItem(
"context_closeLeftTabs",
"Linke Tabs schließen"
);
closeLeft.addEventListener("command", function () {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const tabs = Array.from(gBrowser.tabs);
for (const tab of tabs) {
if (tab === contextTab)
break;
if (!tab.pinned)
gBrowser.removeTab(tab, { animate: true });
}
});
// ------------------------------------------------------------
// Rechte Tabs schließen
// ------------------------------------------------------------
const closeRight = createMenuItem(
"context_closeRightTabs",
"Rechte Tabs schließen"
);
closeRight.addEventListener("command", function () {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const tabs = Array.from(gBrowser.tabs);
const index = tabs.indexOf(contextTab);
if (index === -1)
return;
for (let i = tabs.length - 1; i > index; i--) {
const tab = tabs[i];
if (!tab.pinned)
gBrowser.removeTab(tab, { animate: true });
}
});
// ------------------------------------------------------------
// Andere Tabs schließen
// ------------------------------------------------------------
const closeOthers = createMenuItem(
"context_closeOtherTabsCustom",
"Andere Tabs schließen"
);
closeOthers.addEventListener("command", function () {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const tabs = Array.from(gBrowser.tabs);
for (const tab of tabs) {
if (tab !== contextTab && !tab.pinned)
gBrowser.removeTab(tab, { animate: true });
}
});
// ------------------------------------------------------------
// Menü einfügen
// ------------------------------------------------------------
tabContextMenu.insertBefore(closeLeft, undoCloseTab);
tabContextMenu.insertBefore(closeRight, undoCloseTab);
tabContextMenu.insertBefore(closeOthers, undoCloseTab);
// ------------------------------------------------------------
// Menü beim Öffnen aktualisieren
// ------------------------------------------------------------
tabContextMenu.addEventListener("popupshowing", function () {
const contextTab = TabContextMenu.contextTab;
if (!contextTab)
return;
const tabs = Array.from(gBrowser.tabs);
const index = tabs.indexOf(contextTab);
if (index === -1)
return;
let hasLeft = false;
let hasRight = false;
let hasOther = false;
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (tab.pinned)
continue;
if (i < index)
hasLeft = true;
if (i > index)
hasRight = true;
if (tab !== contextTab)
hasOther = true;
}
closeLeft.disabled = !hasLeft;
closeRight.disabled = !hasRight;
closeOthers.disabled = !hasOther;
});
})();
Alles anzeigen
Prima. passt nun.
Vielen Dank.
Endor Sehr schön, ist gern geschehen, aber nur mit Hilfe KI.![]()
ließe sich das eventuell auch nur auf Menüleiste - Lesezeichen Popup beschränken?
In Zeile 16 und 17 beides getrennt einstellbar, bitte testen.
// ==UserScript==
// @name BMMultiColumn.uc.js
// @description Lesezeichenmenü automatisch in Spalten anzeigen (mit manueller Breiteneinstellung für Menü- und Toolbar-Items).
// @author Ryan, ding, angepasst für manuelle Breite
// @include main
// @charset UTF-8
// @version 2026.08.20
// @compatibility Firefox 153
// @async
// @shutdown window.BMMultiColumn.destroy();
// @homepageURL https://github.com/benzBrake/FirefoxCustomize/blob/master/userChromeJS
// @note 2026.08.20 Manuelle Breiteneinstellung für Menü- und Toolbar-Items hinzugefügt
// ==/UserScript==
// --- BENUTZEREINSTELLUNG: Hier die gewünschte Breite für Menü- und Toolbar-Items in Pixel eintragen ---
const USER_MENU_ITEM_WIDTH = 400; // Breite für Menü-Popups (Lesezeichen, Chronik)
const USER_TOOLBAR_ITEM_WIDTH = 300; // Breite für Toolbar-Popups (Lesezeichenordner in der Personal-Toolbar)
location.href.startsWith("chrome://browser/content/browser.x") && (function (css) {
function uninitBMMultiColumn(instance) {
for (let i = 0; i < instance.menupopup.length; i++) {
clearInterval(instance.timer[i]);
instance.timer[i] = null;
const menupopup = document.getElementById(instance.menupopup[i]);
if (!menupopup) continue;
instance.resetPopup(menupopup);
menupopup.removeEventListener('popupshowing', instance, false);
menupopup.removeEventListener('DOMMenuItemActive', instance, false);
}
}
function hasOtherBMMultiColumnWindow() {
const windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
const win = windows.getNext();
if (win !== window && win.BMMultiColumn && !win.BMMultiColumn._destroyed) {
return true;
}
}
return false;
}
function destroyBMMultiColumn(instance) {
if (!instance || instance._destroyed) return;
instance._destroyed = true;
window.removeEventListener('unload', instance, false);
window.removeEventListener("aftercustomization", instance, false);
if (window.BMMultiColumn === instance) {
delete window.BMMultiColumn;
}
uninitBMMultiColumn(instance);
if (!hasOtherBMMultiColumnWindow()
&& instance.style
&& instance.sss.sheetRegistered(instance.style, instance.sss.AUTHOR_SHEET)) {
instance.sss.unregisterSheet(instance.style, instance.sss.AUTHOR_SHEET);
}
instance.style = null;
}
destroyBMMultiColumn(window.BMMultiColumn);
window.BMMultiColumn = {
_destroyed: false,
menupopup: ["historyMenuPopup", "bookmarksMenuPopup", 'PlacesToolbar', 'BMB_bookmarksPopup'],
timer: [],
count: [],
sss: Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService),
init: function() {
this.style = makeURI('data:text/css;charset=UTF-8,' + encodeURIComponent(css));
if (!this.sss.sheetRegistered(this.style, this.sss.AUTHOR_SHEET)) {
this.sss.loadAndRegisterSheet(this.style, this.sss.AUTHOR_SHEET);
}
window.removeEventListener("load", this, false);
window.addEventListener('unload', this, false);
window.addEventListener("aftercustomization", this, false);
this.delayedStartup();
},
delayedStartup: function() {
if (this._destroyed) return;
for (var i = 0; i < this.menupopup.length; i++) {
clearInterval(this.timer[i]);
this.count[i] = 0;
this.timer[i] = setInterval(function(self, i) {
if (self._destroyed || ++self.count[i] > 50 || document.getElementById(self.menupopup[i])) {
clearInterval(self.timer[i]);
var menupopup = document.getElementById(self.menupopup[i]);
if (!self._destroyed && menupopup) {
menupopup.addEventListener('popupshowing', self, false);
menupopup.addEventListener('DOMMenuItemActive', self, false);
}
}
}, 250, this, i);
}
},
uninit: function() {
uninitBMMultiColumn(this);
},
destroy() {
destroyBMMultiColumn(this);
},
hasOtherActiveWindow() {
return hasOtherBMMultiColumnWindow();
},
handleEvent(event) {
if (this._destroyed) return;
switch (event.type) {
case 'popupshowing':
let menupopup;
if (event.target.tagName == 'menu' || event.target.tagName == 'toolbarbutton') {
menupopup = event.target.menupopup;
} else if (event.target.tagName == 'menupopup') {
menupopup = event.target;
} else return;
this.initHorizontalScroll(event);
if (menupopup.id === "PlacesChevronPopup") {
setTimeout(_ => {
if (!this._destroyed) {
this.initMultiColumn(menupopup, event);
}
}, 10);
} else {
this.initMultiColumn(menupopup, event);
}
break;
case 'aftercustomization':
setTimeout(() => this.delayedStartup(), 0);
break;
case 'unload':
this.destroy();
break;
}
},
initHorizontalScroll(event) {
let scrollBox = event.originalTarget.scrollBox;
scrollBox.scrollbox.style.setProperty("overflow-y", "auto", "important");
scrollBox.scrollbox.style.setProperty("margin-top", "0", "important");
scrollBox.scrollbox.style.setProperty("margin-bottom", "0", "important");
scrollBox.scrollbox.style.setProperty("padding-top", "0", "important");
scrollBox.scrollbox.style.setProperty("padding-bottom", "0", "important");
event.originalTarget.on_DOMMenuItemActive = function(event) {
let elt = event.target;
if (elt.parentNode != this) {
return;
}
if (window.XULBrowserWindow) {
let placesNode = elt._placesNode;
var linkURI;
if (placesNode && PlacesUtils.nodeIsURI(placesNode)) {
linkURI = placesNode.uri;
} else if (elt.hasAttribute("targetURI")) {
linkURI = elt.getAttribute("targetURI");
}
if (linkURI) {
window.XULBrowserWindow.setOverLink(linkURI);
}
}
}.bind(event.originalTarget);
scrollBox._scrollButtonUp.style.display = "none";
scrollBox._scrollButtonDown.style.display = "none";
},
initMultiColumn(menupopup) {
menupopup.style.maxWidth = "calc(100vw - 20px)";
let arrowscrollbox = menupopup.shadowRoot.querySelector("::part(arrowscrollbox)");
let scrollbox = arrowscrollbox.shadowRoot.querySelector('[part~=scrollbox]');
if (scrollbox) {
Object.assign(scrollbox.style, {
minHeight: "21px",
height: "auto",
display: "flex",
flexFlow: "column wrap",
width: "unset",
scrollSnapType: "x mandatory",
});
arrowscrollbox.style.width = "auto";
arrowscrollbox.style.maxHeight = "calc(100vh - 129px)";
let slot = scrollbox.querySelector('slot');
slot.style.display = "contents";
// --- MANUELLE BREITE: Unterschiedliche Breite für Menü- und Toolbar-Popups ---
let columnWidth;
let isToolbarPopup = false;
// Prüfe, ob das Popup in der Personal-Toolbar liegt
let parent = menupopup.parentNode;
while (parent) {
if (parent.id === "PlacesToolbar" || parent.id === "BMB_bookmarksPopup" || parent.id === "PersonalToolbar") {
isToolbarPopup = true;
break;
}
parent = parent.parentNode;
}
columnWidth = isToolbarPopup ? USER_TOOLBAR_ITEM_WIDTH : USER_MENU_ITEM_WIDTH;
let maxWidth = calcWidth(-129);
for (let item = menupopup.firstElementChild; item; item = item.nextElementSibling) {
if (!item.matches?.("menu, menuitem, menuseparator")) continue;
item.style.setProperty("min-width", "0", "important");
item.style.setProperty("max-width", columnWidth + "px", "important");
item.style.setProperty("width", columnWidth + "px", "important");
}
if (maxWidth < scrollbox.scrollWidth) {
scrollbox.style.setProperty("overflow-x", "auto", "important");
scrollbox.style.setProperty("width", maxWidth + "px");
} else {
scrollbox.style.setProperty("width", scrollbox.scrollWidth + "px", "important");
scrollbox.clientWidth = scrollbox.scrollWidth;
}
bindWheelEvent(scrollbox);
}
function bindWheelEvent(item) {
if (item._bmMultiColumnWheelHandler) return;
const wheelHandler = (e) => {
e.preventDefault();
const delta = e.deltaY || e.detail || e.wheelDelta;
item.scrollLeft += delta * 50;
};
item.addEventListener('wheel', wheelHandler, { passive: false });
item._bmMultiColumnWheelHandler = wheelHandler;
}
},
resetPopup(menupopup) {
let arrowscrollbox = menupopup?.shadowRoot?.querySelector("::part(arrowscrollbox)");
if (!arrowscrollbox) return;
menupopup.style.maxWidth = "";
arrowscrollbox.style.width = "";
arrowscrollbox.style.maxHeight = "";
let scrollbox = arrowscrollbox.shadowRoot.querySelector('[part~=scrollbox]');
if (!scrollbox) return;
if (scrollbox._bmMultiColumnWheelHandler) {
scrollbox.removeEventListener('wheel', scrollbox._bmMultiColumnWheelHandler);
delete scrollbox._bmMultiColumnWheelHandler;
}
let slot = scrollbox.querySelector('slot');
if (slot) {
slot.style.display = "";
}
Object.assign(scrollbox.style, {
minHeight: "",
height: "",
display: "",
flexFlow: "",
overflow: "",
overflowX: "",
overflowY: "",
maxHeight: "",
width: "",
scrollSnapType: "",
});
let menuitem = menupopup.lastElementChild;
while (menuitem) {
menuitem.style.minWidth = "";
menuitem.style.maxWidth = "";
menuitem.style.width = "";
menuitem = menuitem.previousElementSibling;
}
}
};
function calcWidth(offset) {
if (typeof offset == 'number') {
return window.innerWidth + offset;
} else if (typeof offset == 'string') {
if (/^-?\d+px$/.test(offset.trim())) {
return window.innerWidth + parseInt(offset.trim().match(/^-?(\d+)px$/)[1]);
}
}
throw new Error('Invalid offset value');
}
window.BMMultiColumn.init();
})(`
#PlacesToolbarItems menupopup {
max-width: calc(100vw - 20px);
max-height: calc(100vh - 129px);
}
#PlacesToolbarItems menupopup > * {
scroll-snap-align: start;
}
`);
Alles anzeigen
Eine andere Möglichkeit wäre die Einstellung der Breite direkt im Script, was in Zeile 16 (eingestellt sind 400px) erfolgen kann.
Dann teste bitte mal:
// ==UserScript==
// @name BMMultiColumn.uc.js
// @description Lesezeichenmenü automatisch in Spalten anzeigen (mit manueller Breiteneinstellung für Items).
// @author Ryan, ding, angepasst für manuelle Breite
// @include main
// @charset UTF-8
// @version 2026.08.20
// @compatibility Firefox 153
// @async
// @shutdown window.BMMultiColumn.destroy();
// @homepageURL https://github.com/benzBrake/FirefoxCustomize/blob/master/userChromeJS
// @note 2026.08.20 Manuelle Breiteneinstellung für Menü-Items hinzugefügt
// ==/UserScript==
// --- BENUTZEREINSTELLUNG: Hier die gewünschte Breite für alle Menü-Items in Pixel eintragen ---
const USER_MENU_ITEM_WIDTH = 400; // Standard: 400px - einfach diesen Wert ändern!
location.href.startsWith("chrome://browser/content/browser.x") && (function (css) {
function uninitBMMultiColumn(instance) {
for (let i = 0; i < instance.menupopup.length; i++) {
clearInterval(instance.timer[i]);
instance.timer[i] = null;
const menupopup = document.getElementById(instance.menupopup[i]);
if (!menupopup) continue;
instance.resetPopup(menupopup);
menupopup.removeEventListener('popupshowing', instance, false);
menupopup.removeEventListener('DOMMenuItemActive', instance, false);
}
}
function hasOtherBMMultiColumnWindow() {
const windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
const win = windows.getNext();
if (win !== window && win.BMMultiColumn && !win.BMMultiColumn._destroyed) {
return true;
}
}
return false;
}
function destroyBMMultiColumn(instance) {
if (!instance || instance._destroyed) return;
instance._destroyed = true;
window.removeEventListener('unload', instance, false);
window.removeEventListener("aftercustomization", instance, false);
if (window.BMMultiColumn === instance) {
delete window.BMMultiColumn;
}
uninitBMMultiColumn(instance);
if (!hasOtherBMMultiColumnWindow()
&& instance.style
&& instance.sss.sheetRegistered(instance.style, instance.sss.AUTHOR_SHEET)) {
instance.sss.unregisterSheet(instance.style, instance.sss.AUTHOR_SHEET);
}
instance.style = null;
}
destroyBMMultiColumn(window.BMMultiColumn);
window.BMMultiColumn = {
_destroyed: false,
menupopup: ["historyMenuPopup", "bookmarksMenuPopup", 'PlacesToolbar', 'BMB_bookmarksPopup'],
timer: [],
count: [],
sss: Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService),
init: function() {
this.style = makeURI('data:text/css;charset=UTF-8,' + encodeURIComponent(css));
if (!this.sss.sheetRegistered(this.style, this.sss.AUTHOR_SHEET)) {
this.sss.loadAndRegisterSheet(this.style, this.sss.AUTHOR_SHEET);
}
window.removeEventListener("load", this, false);
window.addEventListener('unload', this, false);
window.addEventListener("aftercustomization", this, false);
this.delayedStartup();
},
delayedStartup: function() {
if (this._destroyed) return;
for (var i = 0; i < this.menupopup.length; i++) {
clearInterval(this.timer[i]);
this.count[i] = 0;
this.timer[i] = setInterval(function(self, i) {
if (self._destroyed || ++self.count[i] > 50 || document.getElementById(self.menupopup[i])) {
clearInterval(self.timer[i]);
var menupopup = document.getElementById(self.menupopup[i]);
if (!self._destroyed && menupopup) {
menupopup.addEventListener('popupshowing', self, false);
menupopup.addEventListener('DOMMenuItemActive', self, false);
}
}
}, 250, this, i);
}
},
uninit: function() {
uninitBMMultiColumn(this);
},
destroy() {
destroyBMMultiColumn(this);
},
hasOtherActiveWindow() {
return hasOtherBMMultiColumnWindow();
},
handleEvent(event) {
if (this._destroyed) return;
switch (event.type) {
case 'popupshowing':
let menupopup;
if (event.target.tagName == 'menu' || event.target.tagName == 'toolbarbutton') {
menupopup = event.target.menupopup;
} else if (event.target.tagName == 'menupopup') {
menupopup = event.target;
} else return;
this.initHorizontalScroll(event);
if (menupopup.id === "PlacesChevronPopup") {
setTimeout(_ => {
if (!this._destroyed) {
this.initMultiColumn(menupopup, event);
}
}, 10);
} else {
this.initMultiColumn(menupopup, event);
}
break;
case 'aftercustomization':
setTimeout(() => this.delayedStartup(), 0);
break;
case 'unload':
this.destroy();
break;
}
},
initHorizontalScroll(event) {
let scrollBox = event.originalTarget.scrollBox;
scrollBox.scrollbox.style.setProperty("overflow-y", "auto", "important");
scrollBox.scrollbox.style.setProperty("margin-top", "0", "important");
scrollBox.scrollbox.style.setProperty("margin-bottom", "0", "important");
scrollBox.scrollbox.style.setProperty("padding-top", "0", "important");
scrollBox.scrollbox.style.setProperty("padding-bottom", "0", "important");
event.originalTarget.on_DOMMenuItemActive = function(event) {
let elt = event.target;
if (elt.parentNode != this) {
return;
}
if (window.XULBrowserWindow) {
let placesNode = elt._placesNode;
var linkURI;
if (placesNode && PlacesUtils.nodeIsURI(placesNode)) {
linkURI = placesNode.uri;
} else if (elt.hasAttribute("targetURI")) {
linkURI = elt.getAttribute("targetURI");
}
if (linkURI) {
window.XULBrowserWindow.setOverLink(linkURI);
}
}
}.bind(event.originalTarget);
scrollBox._scrollButtonUp.style.display = "none";
scrollBox._scrollButtonDown.style.display = "none";
},
initMultiColumn(menupopup) {
menupopup.style.maxWidth = "calc(100vw - 20px)";
let arrowscrollbox = menupopup.shadowRoot.querySelector("::part(arrowscrollbox)");
let scrollbox = arrowscrollbox.shadowRoot.querySelector('[part~=scrollbox]');
if (scrollbox) {
Object.assign(scrollbox.style, {
minHeight: "21px",
height: "auto",
display: "flex",
flexFlow: "column wrap",
width: "unset",
scrollSnapType: "x mandatory",
});
arrowscrollbox.style.width = "auto";
arrowscrollbox.style.maxHeight = "calc(100vh - 129px)";
let slot = scrollbox.querySelector('slot');
slot.style.display = "contents";
// --- MANUELLE BREITE: Hier wird die eingestellte Breite verwendet ---
let columnWidth = USER_MENU_ITEM_WIDTH;
let maxWidth = calcWidth(-129);
for (let item = menupopup.firstElementChild; item; item = item.nextElementSibling) {
if (!item.matches?.("menu, menuitem, menuseparator")) continue;
item.style.setProperty("min-width", "0", "important");
item.style.setProperty("max-width", columnWidth + "px", "important");
item.style.setProperty("width", columnWidth + "px", "important");
}
if (maxWidth < scrollbox.scrollWidth) {
scrollbox.style.setProperty("overflow-x", "auto", "important");
scrollbox.style.setProperty("width", maxWidth + "px");
} else {
scrollbox.style.setProperty("width", scrollbox.scrollWidth + "px", "important");
scrollbox.clientWidth = scrollbox.scrollWidth;
}
bindWheelEvent(scrollbox);
}
function bindWheelEvent(item) {
if (item._bmMultiColumnWheelHandler) return;
const wheelHandler = (e) => {
e.preventDefault();
const delta = e.deltaY || e.detail || e.wheelDelta;
item.scrollLeft += delta * 50;
};
item.addEventListener('wheel', wheelHandler, { passive: false });
item._bmMultiColumnWheelHandler = wheelHandler;
}
},
resetPopup(menupopup) {
let arrowscrollbox = menupopup?.shadowRoot?.querySelector("::part(arrowscrollbox)");
if (!arrowscrollbox) return;
menupopup.style.maxWidth = "";
arrowscrollbox.style.width = "";
arrowscrollbox.style.maxHeight = "";
let scrollbox = arrowscrollbox.shadowRoot.querySelector('[part~=scrollbox]');
if (!scrollbox) return;
if (scrollbox._bmMultiColumnWheelHandler) {
scrollbox.removeEventListener('wheel', scrollbox._bmMultiColumnWheelHandler);
delete scrollbox._bmMultiColumnWheelHandler;
}
let slot = scrollbox.querySelector('slot');
if (slot) {
slot.style.display = "";
}
Object.assign(scrollbox.style, {
minHeight: "",
height: "",
display: "",
flexFlow: "",
overflow: "",
overflowX: "",
overflowY: "",
maxHeight: "",
width: "",
scrollSnapType: "",
});
let menuitem = menupopup.lastElementChild;
while (menuitem) {
menuitem.style.minWidth = "";
menuitem.style.maxWidth = "";
menuitem.style.width = "";
menuitem = menuitem.previousElementSibling;
}
}
};
function calcWidth(offset) {
if (typeof offset == 'number') {
return window.innerWidth + offset;
} else if (typeof offset == 'string') {
if (/^-?\d+px$/.test(offset.trim())) {
return window.innerWidth + parseInt(offset.trim().match(/^-?(\d+)px$/)[1]);
}
}
throw new Error('Invalid offset value');
}
window.BMMultiColumn.init();
})(`
#PlacesToolbarItems menupopup {
max-width: calc(100vw - 20px);
max-height: calc(100vh - 129px);
}
#PlacesToolbarItems menupopup > * {
scroll-snap-align: start;
}
`);
Alles anzeigen
Sobald der Text des Ordners länger ist wird er überlagert.
Das wird wohl nur funktionieren, wenn die Breite der Popup-Menüs angepasst wird. Bei mir kein Problem, weil das schon immer so war. Ich habe mal auszugsweise nur den Teil für die Bookmarks genommen, und ihn so angepasst, dass beide Scripte zusammen funktionieren. Teste es mal bei dir, wobei der Code aber auch für den Einsatz von Icons vorbereitet ist.
@-moz-document url(chrome://browser/content/browser.xhtml) {
:root {
--arrowpanel-border-radius: 0px !important;
}
:is(menupopup, panel)[type="arrow"] {
--panel-border-radius: 0px !important;
}
/* Popup-Menüs (äußerer Rahmen) / außer Lesezeichen und Urlbar Suche */
menupopup {
appearance: none!important;
background: #00d4ff !important;
border: 3px solid #0070ff !important; /* Umrandung/Farbe */
padding: 5px 5px 5px 5px !important; /* Ausrichtung */
font-size: 9pt !important; /* Schriftgröße */
border-radius: 0px !important;
--border-radius: 0px !important;
}
/* Popup-Menüs (inneres Panel) / außer Lesezeichen */
arrowscrollbox:not(#tabbrowser-arrowscrollbox), menupopup, popup {
appearance: none!important;
min-width: 400px !important; /* vorgegebene Breite der Popup-Fenster */
--panel-background: #ffd700 !important;
--panel-border-color: magenta !important;
--panel-border-radius: 0px !important;
padding: 5px 5px 5px 5px !important;
}
arrowscrollbox:not(menupopup){
border-radius: 6px !important;
}
/* für alle Bookmark-Item */
#PlacesToolbarItems toolbarbutton.bookmark-item menupopup.toolbar-menupopup menu.menu-iconic.bookmark-item,
#PlacesToolbarItems toolbarbutton.bookmark-item menupopup.toolbar-menupopup menuitem.menuitem-iconic.bookmark-item,
#bookmarksMenuPopup .menu-iconic.bookmark-item,
#bookmarksMenuPopup menuitem {
width: 100% !important;
max-width: 400px !important;
min-width: 0 !important;
background-color: white !important;
min-height: 24px;
padding: 0 0 0 40px !important;
margin: 0.5px !important;
border-width: thin !important;
border: 1px solid #a6a6a6;
border-radius: 4px !important;
background-repeat: no-repeat !important;
background-position: 10px 3px;
background-size: 15px 15px !important;
}
/* aktive Menüeinträge bei hover */
menupopup :is(menu, menuitem):hover {
background-color: #ccf5ff !important;
}
/* die inaktiven Menüeinträge bei hover grau */
menupopup :is(menu[disabled="true"], menuitem[disabled="true"]):hover {
background-color: #cccccc !important;
}
}
Alles anzeigen
Geändert habe ich vor allem die Icon-Erzeugung, die explizite Größe und die URL-Behandlung. Außerdem wird der / wieder tatsächlich ausgegeben. Sieht hier so aus: ![]()
Dann teste bitte mal:
// basiert auf Script von BrokenHeart
// Firefox 154+
(function() {
if (!window.gBrowser)
return;
setTimeout(function() {
setFunction();
}, 50);
// ============================================================
// Custom icons in profile/chrome/icons folder
// ============================================================
let ProfilePath = PathUtils.toFileURI(
PathUtils.join(PathUtils.profileDir, 'chrome', 'icons')
);
let icon1 = "folder.png";
let icon2 = "Bild36.png";
let icon1URL = `${ProfilePath}/${icon1}`;
let icon2URL = `${ProfilePath}/${icon2}`;
// ============================================================
// Einstellungen
// ============================================================
let cs_font_size = 14;
let cs_font_weight = 600;
let cs_font_color = "brown";
// feste Spaltenbreite
let colWidthCh = 4;
// ============================================================
// CSS
// ============================================================
function setFunction() {
const css = `
/*===== Counter rechtsbündig =====*/
#bmContent {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: -15px;
margin-right: 0px;
font-family: Consolas, "Lucida Console", "Courier New", monospace;
font-size: ${cs_font_size}px;
font-weight: ${cs_font_weight};
height: 18px;
white-space: nowrap;
}
/*============*/
/* Icon 1 */
/*============*/
.bmIcon1 {
display: inline-block;
width: 18px;
min-width: 18px;
height: 18px;
min-height: 18px;
flex: 0 0 18px;
background-image: url("${icon1URL}");
background-repeat: no-repeat;
background-position: center;
background-size: 16px 16px;
margin-left: 6px;
margin-right: 0;
margin-top: 0px;
}
/*============*/
/* Icon 2 */
/*============*/
.bmIcon2 {
display: inline-block;
width: 13px;
min-width: 13px;
height: 13px;
min-height: 13px;
flex: 0 0 13px;
background-image: url("${icon2URL}");
background-repeat: no-repeat;
background-position: center;
background-size: 13px 13px;
margin-left: 6px;
margin-right: 0;
margin-top: 0px;
}
/*===== Zahlen =====*/
.bmValue {
color: ${cs_font_color} !important;
flex: 0 0 auto;
min-width: ${colWidthCh}ch;
text-align: right;
}
/*===== Slash =====*/
.bmSlash {
color: ${cs_font_color} !important;
margin-left: 3px !important;
margin-right: -7px !important;
}
/*===== Hover-Farbwechsel =====*/
menupopup[placespopup="true"]
> menu.bookmark-item:hover
> .menu-text
> #bmContent
.bmValue {
color: red !important;
}
menupopup[placespopup="true"]
> menu.bookmark-item:hover
> .menu-text
> #bmContent
.bmSlash {
color: #000 !important;
}
`;
const sss =
Cc['@mozilla.org/content/style-sheet-service;1']
.getService(Ci.nsIStyleSheetService);
const uri = Services.io.newURI(
'data:text/css,' + encodeURIComponent(css)
);
sss.loadAndRegisterSheet(
uri,
sss.AGENT_SHEET
);
// ========================================================
// Bookmarks-Menü
// ========================================================
let bookMenu =
document.getElementById('bookmarksMenu');
let persToolBar =
document.getElementById('PersonalToolbar');
if (bookMenu) {
bookMenu.addEventListener(
'popupshown',
onPopupShowing
);
}
if (persToolBar) {
persToolBar.addEventListener(
'popupshowing',
onPopupShowing
);
}
}
// ============================================================
// Popup
// ============================================================
function onPopupShowing(aEvent) {
let popup = aEvent.originalTarget;
for (let item of popup.children) {
if (
item.localName != 'menu' ||
item.id?.startsWith('history')
)
continue;
setTimeout(() => {
let itemPopup = item.menupopup;
if (!itemPopup)
return;
// ------------------------------------------------
// Popup kurz öffnen, damit die Children
// zuverlässig vorhanden sind
// ------------------------------------------------
itemPopup.hidden = true;
itemPopup.collapsed = true;
itemPopup.openPopup();
itemPopup.hidePopup();
// ------------------------------------------------
// Zähler
// ------------------------------------------------
let menuitemCount = 0;
let menuCount = 0;
for (let subitem of itemPopup.children) {
if (
subitem.classList.contains('bookmark-item') &&
!subitem.disabled &&
!subitem.hidden
) {
if (subitem.localName == 'menuitem') {
menuitemCount++;
}
else if (subitem.localName == 'menu') {
menuCount++;
}
}
}
itemPopup.hidden = false;
itemPopup.collapsed = false;
// ------------------------------------------------
// Container für den Counter
// ------------------------------------------------
let bmCounta = item.childNodes[1];
if (!bmCounta)
return;
bmCounta.innerHTML = "";
let bmContent =
document.createXULElement("box");
bmContent.id = "bmContent";
// =================================================
// Anzahl Ordner
// =================================================
let v1 =
document.createXULElement("label");
v1.className = "bmValue";
v1.textContent = menuCount;
// =================================================
// Icon 1 = Ordner
// =================================================
let icon1El =
document.createXULElement("box");
icon1El.className = "bmIcon1";
// =================================================
// Slash
// =================================================
let slash =
document.createXULElement("label");
slash.className = "bmSlash";
slash.textContent = "/";
// =================================================
// Anzahl Lesezeichen
// =================================================
let v2 =
document.createXULElement("label");
v2.className = "bmValue";
v2.textContent = menuitemCount;
// =================================================
// Icon 2 = Lesezeichen
// =================================================
let icon2El =
document.createXULElement("box");
icon2El.className = "bmIcon2";
// =================================================
// Reihenfolge
//
// Zahl1 Icon1 / Zahl2 Icon2
// =================================================
bmContent.appendChild(v1);
bmContent.appendChild(icon1El);
bmContent.appendChild(slash);
bmContent.appendChild(v2);
bmContent.appendChild(icon2El);
// =================================================
// Einfügen
// =================================================
bmCounta.appendChild(bmContent);
}, 100);
}
}
})();
Alles anzeigen
Zusammen getestet mit deinem zweiten Script:
Ich habe vor langer Zeit base64-Symbole durch Icons aus Browserressourcen ersetzt.
Ich habe sie ganz aus dem Script entfernt, dafür die Icon mit einer css-Datei erstellt.
In dem Script Appmenu.uc.js (https://github.com/Endor8/userChr…5/Appmenu.uc.js ) lassen sich schon ab Firefox 153 einige Funktionen nicht mehr aufrufen. Mein Script konnte ich noch mit KI neu einrichten, dieses mit den vielen Base64-Codes will nicht gelingen. Wenn gewünscht, dann stelle ich auch mein Script zur Verfügung.
Das ist der Ansatz, der zum Erfolg führte: Appmenü ab Fox 153.txt
Edit:
Nun habe ich den Base64-Code entfernt, müsste dann wieder hier image: 'den Base64-Code hier einfügen ' eingefügt werden. Das Script funktioniert dann wieder, hier dann zum Test.
// ==UserScript==
// @name Appmenu.uc.js
// @namespace Appmenu@gmail.com
// @description Firefox-Menübutton für Firefox 155+
// @include main
// @version 2025.01.01
// @author bege
// @charset UTF-8
// ==/UserScript==
var Appmenu = {
// ==============================
// Konfiguration
// ==============================
editor: Services.prefs.getCharPref(
'view_source.editor.path',
''
),
fileManager: '',
FMParameter: '/T',
autohideEmptySubDirs: true,
moveSubDirstoBottom: false,
// 0 = TabsToolbar
// 1 = Navigationsleiste
// 2 = Menüleiste
isUrlbar: 1,
// 0 = kleines Symbol
// 1 = großes Firefox-Symbol
isButton: 1,
hotkey: 'x',
hotkeyModifier: 'alt',
subdirPopupHash: Object.create(null),
subdirMenuHash: Object.create(null),
toolbar: {
subdirs: [
{
name: 'Firefox Verzeichnisse',
image: ' '
},
{
name: 'Firefox Profil-Dateien',
image: ' '
},
{
name: 'Firefox Funktionen',
image: ' '
},
{
name: 'about:',
image: ' '
}
],
apps: [
{
name: 'userChrome.css',
root: 'ProfD',
path: '\\chrome\\userChrome.css',
subdir: 'Firefox Profil-Dateien',
image: ' '
},
{
name: 'userContent.css',
root: 'ProfD',
path: '\\chrome\\userContent.css',
subdir: 'Firefox Profil-Dateien',
image: ' '
},
{
name: 'userChrome.js',
root: 'ProfD',
path: '\\chrome\\userChrome.js',
subdir: 'Firefox Profil-Dateien',
image: ' '
},
{
name: 'prefs.js',
root: 'ProfD',
path: '\\prefs.js',
subdir: 'Firefox Profil-Dateien',
image: ' '
},
{
name: 'user.js',
root: 'ProfD',
path: '\\user.js',
subdir: 'Firefox Profil-Dateien',
image: ' '
},
{
name: 'Profil',
root: 'ProfD',
path: '\\',
subdir: 'Firefox Verzeichnisse',
image: ' '
},
{
name: 'chrome',
root: 'ProfD',
path: '\\chrome',
subdir: 'Firefox Verzeichnisse',
image: ' '
},
{
name: 'CSS',
root: 'ProfD',
path: '\\chrome\\CSS',
subdir: 'Firefox Verzeichnisse',
image: ' '
},
{
name: 'JS',
root: 'ProfD',
path: '\\chrome\\JS',
subdir: 'Firefox Verzeichnisse',
image: ' '
},
{
name: 'Addons',
root: 'ProfD',
path: '\\extensions',
subdir: 'Firefox Verzeichnisse',
image: ' '
},
{
name: 'Programm',
root: 'CurProcD',
path: '\\',
subdir: 'Firefox Verzeichnisse',
image: ' '
},
{
name: 'Startup-Cache',
root: 'ProfLD',
path: '\\startupCache',
subdir: 'Firefox Verzeichnisse',
image: ' '
}
],
configs: [
{
name: 'Anpassen',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Neustart im abgesicherten Modus',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Browser-Konsole',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Entwickler-Werkzeuge',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Browser-Werkzeuge',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Firefox synchronisieren',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Zugangsdaten und Passwörter',
tooltiptext: 'about:logins',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Task Manager',
tooltiptext: 'about:processes',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'Offline arbeiten',
subdir: 'Firefox Funktionen',
image: ' '
},
{
name: 'about:about',
subdir: 'about:',
image: ' '
},
{
name: 'about:cache',
subdir: 'about:',
image: ' '
},
{
name: 'about:certificate',
subdir: 'about:',
image: ' '
},
{
name: 'about:checkerboard',
subdir: 'about:',
image: ' '
},
{
name: 'about:compat',
subdir: 'about:',
image: ' '
},
{
name: 'about:config',
subdir: 'about:',
image: ' '
},
{
name: 'about:crashes',
subdir: 'about:',
image: ' '
},
{
name: 'about:debugging',
subdir: 'about:',
image: ' '
},
{
name: 'about:downloads',
subdir: 'about:',
image: ' '
},
{
name: 'about:logging',
subdir: 'about:',
image: ' '
},
{
name: 'about:logins',
subdir: 'about:',
image: ' '
},
{
name: 'about:memory',
subdir: 'about:',
image: ' '
},
{
name: 'about:networking',
subdir: 'about:',
image: ' '
},
{
name: 'about:processes',
tooltiptext: 'Task Manager',
subdir: 'about:',
image: ' '
},
{
name: 'about:policies',
subdir: 'about:',
image: ' '
},
{
name: 'about:profiles',
subdir: 'about:',
image: ' '
},
{
name: 'about:profiling',
subdir: 'about:',
image: ' '
},
{
name: 'about:protections',
subdir: 'about:',
image: ' '
},
{
name: 'about:rights',
subdir: 'about:',
image: ' '
},
{
name: 'about:serviceworkers',
subdir: 'about:',
image: ' '
},
{
name: 'about:studies',
subdir: 'about:',
image: ' '
},
{
name: 'about:support',
subdir: 'about:',
image: ' '
},
{
name: 'about:sync-log',
subdir: 'about:',
image: ' '
},
{
name: 'about:telemetry',
subdir: 'about:',
image: ' '
},
{
name: 'about:third-party',
subdir: 'about:',
image: ' '
},
{
name: 'about:unloads',
tooltiptext: 'Tabs entladen',
subdir: 'about:',
image: ' '
},
{
name: 'about:url-classifier',
subdir: 'about:',
image: ' '
},
{
name: 'about:webrtc',
subdir: 'about:',
image: ' '
},
{
name: 'about:windows-messages',
subdir: 'about:',
image: ' '
},
{
name: 'Neues privates Fenster',
id: 'AMprivate',
image: ' '
},
{
name: 'separator'
},
{
name: 'Einstellungen',
id: 'AMsettings',
image: ' '
},
{
name: 'Add-ons',
id: 'AMaddons',
image: ' '
},
{
name: 'separator'
},
{
name: 'Lesezeichen-Verwaltung',
id: 'AMbookmarks',
image: ' '
},
{
name: 'Chronik',
id: 'AMhistory',
image: ' '
},
{
name: 'Downloads',
id: 'AMdownloads',
image: ' '
},
{
name: 'Seite speichern unter...',
id: 'AMsave',
image: ' '
},
{
name: 'Chronik löschen',
id: 'AMsanitize',
image: ' '
},
{
name: 'separator'
},
{
name: 'Neustart',
id: 'AMreboot',
tooltiptext: 'userChrome.js-Cache wird geleert',
image: ' '
},
{
name: 'Beenden',
id: 'AMquit',
image: ' '
}
]
},
_externalAppPopup: null,
_isready: false,
// ==============================
// Initialisierung
// ==============================
init: function() {
this.handleRelativePath(this.toolbar.apps);
const XULNS =
'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
const ExternalAppBtn = document.createElementNS(
XULNS,
'toolbarbutton'
);
ExternalAppBtn.id = 'AppMenuButton';
ExternalAppBtn.setAttribute('label', 'AppButton');
ExternalAppBtn.setAttribute('tooltiptext', 'Firefox Menü');
ExternalAppBtn.setAttribute('type', 'menu');
ExternalAppBtn.setAttribute('removable', 'true');
// Firefox-internes Symbol statt Base64
//ExternalAppBtn.style.listStyleImage =
// 'url("chrome://browser/skin/menu.svg")';
if (Appmenu.isButton) {
ExternalAppBtn.style.listStyleImage = "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAAUCAYAAAAwaEt4AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAF3SURBVFhH7ZUxS8RAEIX9M1fb29vb+wP8AdaCYCVY2FmInY1YWmlrp7WtNp6ihyKKCDYrb7kPxsuMJiG5CO6DR5LZmd2Zl9ndhduNUSqssggTsAgTsAgTsAgTsLEw71enyYPGHvZW8vvHzUUlrinvd5e/reX59Mk/K8zn5DrPBTyfPtlamOeTTXe8K4LHg1V3vG92KgwdI2ADT8fr+UmcfNVZ4OVspzIH0Joau9teSq/n+1Nryl1l8yA3/LUdgWLxq8NOthKJ/CQMUCE2YQuJEwkz3lqsbC+AOCoeaB5y1U8hn7qcmzB0hArkr79dHuVvhFLhs3GaU9+Tw7WpJWV/2SQIIA4bIpJbU85tK1Gg6IkLojgKtoV664kWbc+oQYRRpwh0DHZLQJztGM6LOh3T9oYcRBjrZ2F9ADYJGIFcZg9bxPFy/Y2DCCOqxe2tpHd7cwAbp3G6TVDh9mBlPp1h+va6rC4bC/NfWIQJWIQJWIQJWIRxOUpfQ+jqXwPuQ7IAAAAASUVORK5CYII=)";
} else {
ExternalAppBtn.style.listStyleImage = "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA6SURBVDhPYxgcIDg+6z8IQ7lwPiEMVT4YDBh4gO4kZGfiw1Dlg8GAgQfoTkJ2Jj4MVT4YDCAfMDAAAFSm42US1bMnAAAAAElFTkSuQmCC)";
}
let toolbar;
if (this.isUrlbar === 1) {
toolbar = document.getElementById(
'nav-bar-customization-target'
);
} else if (this.isUrlbar === 2) {
toolbar = document.getElementById(
'toolbar-menubar'
);
} else {
toolbar = document.getElementById(
'TabsToolbar'
);
}
if (toolbar) {
toolbar.insertBefore(
ExternalAppBtn,
toolbar.firstChild
);
}
ExternalAppBtn.addEventListener('click', event => {
event.preventDefault();
event.stopPropagation();
});
const ExternalAppPopup = document.createElementNS(
XULNS,
'menupopup'
);
ExternalAppPopup.id = 'AMpopup';
ExternalAppPopup.addEventListener('click', event => {
event.stopPropagation();
});
this._externalAppPopup = ExternalAppPopup;
ExternalAppBtn.appendChild(ExternalAppPopup);
this.onpopupshowing();
if (this.hotkey) {
const key = document.createXULElement('key');
key.id = 'key_AppMenuPopup';
key.setAttribute('key', this.hotkey);
if (this.hotkeyModifier) {
key.setAttribute(
'modifiers',
this.hotkeyModifier
);
}
key.addEventListener('command', () => {
document
.getElementById('AMpopup')
.openPopup();
});
document
.getElementById('mainKeyset')
.appendChild(key);
}
},
// ==============================
// Menü aufbauen
// ==============================
onpopupshowing: function() {
if (this._isready || !this._externalAppPopup) {
return;
}
const popup = this._externalAppPopup;
for (const subdir of this.toolbar.subdirs) {
if (subdir.name === 'separator') {
popup.appendChild(
document.createXULElement('menuseparator')
);
continue;
}
const menu = document.createXULElement('menu');
const subpopup = document.createXULElement(
'menupopup'
);
menu.setAttribute('class', 'menu-iconic');
menu.setAttribute('label', subdir.name);
menu.setAttribute('image', subdir.image);
menu.appendChild(subpopup);
popup.appendChild(menu);
this.subdirPopupHash[subdir.name] = subpopup;
this.subdirMenuHash[subdir.name] = menu;
}
for (const app of this.toolbar.apps) {
let item;
if (app.name === 'separator') {
item = document.createXULElement(
'menuseparator'
);
} else {
item = document.createXULElement('menuitem');
item.setAttribute(
'class',
'menuitem-iconic'
);
item.setAttribute('label', app.name);
item.setAttribute('image', app.image);
item.setAttribute(
'tooltiptext',
app.name
);
item.path = app.path;
item.args = app.args;
item.addEventListener('command', function() {
Appmenu.exec(
this.path,
this.args
);
});
}
if (
app.subdir &&
this.subdirPopupHash[app.subdir]
) {
this.subdirPopupHash[app.subdir]
.appendChild(item);
} else {
popup.appendChild(item);
}
}
for (const config of this.toolbar.configs) {
let item;
if (config.name === 'separator') {
item = document.createXULElement(
'menuseparator'
);
} else {
item = document.createXULElement(
'menuitem'
);
item.setAttribute(
'class',
'menuitem-iconic'
);
item.setAttribute(
'label',
config.name
);
item.setAttribute(
'image',
config.image
);
item.setAttribute(
'tooltiptext',
config.tooltiptext ||
config.name
);
if (config.id) {
item.setAttribute(
'id',
config.id
);
}
item.addEventListener(
'command',
event => {
Appmenu.executeCommand(
config.name,
event
);
}
);
}
if (
config.subdir &&
this.subdirPopupHash[config.subdir]
) {
this.subdirPopupHash[config.subdir]
.appendChild(item);
} else {
popup.appendChild(item);
}
}
if (this.autohideEmptySubDirs) {
for (const name in this.subdirPopupHash) {
const subpopup =
this.subdirPopupHash[name];
const menu =
this.subdirMenuHash[name];
if (
!subpopup.hasChildNodes() &&
menu
) {
menu.setAttribute(
'hidden',
'true'
);
}
}
}
if (this.moveSubDirstoBottom) {
let count = popup.childNodes.length;
while (
popup.firstChild &&
popup.firstChild.getAttribute(
'class'
) !== 'menuitem-iconic' &&
count-- > 0
) {
popup.appendChild(
popup.firstChild
);
}
}
this._isready = true;
},
// ==============================
// Neue Befehlsausführung
// ==============================
executeCommand: function(name, event) {
if (name.startsWith('about:')) {
openTrustedLinkIn(
name,
gBrowser.selectedTab.isEmpty
? 'current'
: 'tab'
);
return;
}
switch (name) {
case 'Anpassen':
gCustomizeMode.enter();
break;
case 'Neustart im abgesicherten Modus':
safeModeRestart();
break;
case 'Browser-Konsole': {
const { require } =
ChromeUtils.importESModule(
'resource://devtools/shared/loader/Loader.sys.mjs',
{}
);
const { BrowserConsoleManager } =
require(
'resource://devtools/client/webconsole/browser-console-manager'
);
BrowserConsoleManager
.openBrowserConsoleOrFocus();
break;
}
case 'Entwickler-Werkzeuge': {
const { require } =
ChromeUtils.importESModule(
'resource://devtools/shared/loader/Loader.sys.mjs',
{}
);
const { gDevToolsBrowser } =
require(
'devtools/client/framework/devtools-browser'
);
gDevToolsBrowser
.toggleToolboxCommand(
window.gBrowser,
Cu.now()
);
break;
}
case 'Browser-Werkzeuge': {
const { require } =
ChromeUtils.importESModule(
'resource://devtools/shared/loader/Loader.sys.mjs',
{}
);
const { BrowserToolboxLauncher } =
require(
'resource://devtools/client/framework/browser-toolbox/Launcher.sys.mjs'
);
BrowserToolboxLauncher.init();
break;
}
case 'Firefox synchronisieren':
gSync.openPrefs('menubar');
break;
case 'Zugangsdaten und Passwörter':
LoginHelper.openPasswordManager(
window,
{
entryPoint: 'mainmenu'
}
);
break;
case 'Task Manager':
switchToTabHavingURI(
'about:processes',
true
);
break;
case 'Offline arbeiten':
BrowserOffline.toggleOfflineStatus();
break;
case 'Neues privates Fenster':
OpenBrowserWindow({
private: true
});
break;
case 'Einstellungen':
openPreferences();
break;
case 'Add-ons':
BrowserAddonUI.openAddonsMgr();
break;
case 'Lesezeichen-Verwaltung':
PlacesCommandHook
.showPlacesOrganizer(
'AllBookmarks'
);
break;
case 'Chronik':
PlacesCommandHook
.showPlacesOrganizer(
'History'
);
break;
case 'Downloads':
BrowserCommands.downloadsUI();
break;
case 'Seite speichern unter...':
saveBrowser(
gBrowser.selectedBrowser
);
break;
case 'Chronik löschen':
Sanitizer.showUI(window);
break;
case 'Neustart':
Services.appinfo
.invalidateCachesOnRestart();
Services.startup.quit(
Ci.nsIAppStartup.eRestart |
Ci.nsIAppStartup.eAttemptQuit
);
break;
case 'Beenden':
goQuitApplication(event);
break;
default:
console.error(
'Appmenu: Unbekannter Befehl: ' +
name
);
}
},
// ==============================
// Pfade verarbeiten
// ==============================
handleRelativePath: function(apps) {
for (const app of apps) {
if (!app.path) {
continue;
}
app.path = app.path.replace(
/\//g,
'\\'
);
const ffdir = Cc[
'@mozilla.org/file/directory_service;1'
]
.getService(Ci.nsIProperties)
.get(app.root, Ci.nsIFile)
.path;
if (/^\\/.test(app.path)) {
app.path = ffdir + app.path;
}
}
},
// ==============================
// Dateien und Ordner öffnen
// ==============================
exec: function(path, args) {
args = args || [];
const args_t = args.slice(0);
for (let i = 0; i < args_t.length; i++) {
args_t[i] = args_t[i].replace(
/%u/g,
gBrowser.currentURI.spec
);
}
const file = Cc[
'@mozilla.org/file/local;1'
].createInstance(Ci.nsIFile);
file.initWithPath(path);
if (!file.exists()) {
alert(
'Datei nicht gefunden: ' +
path
);
return;
}
if (
file.isExecutable() &&
!path.endsWith('.js')
) {
const process = Cc[
'@mozilla.org/process/util;1'
].createInstance(Ci.nsIProcess);
process.init(file);
process.run(
false,
args_t,
args_t.length
);
return;
}
if (file.isFile()) {
if (this.editor) {
const converter = Cc[
'@mozilla.org/intl/scriptableunicodeconverter'
].createInstance(
Ci.nsIScriptableUnicodeConverter
);
converter.charset =
window.navigator.platform
.toLowerCase()
.includes('win')
? 'Shift_JIS'
: 'UTF-8';
const convertedPath =
converter.ConvertFromUnicode(
file.path
);
const editor =
Cc[
'@mozilla.org/file/local;1'
].createInstance(Ci.nsIFile);
editor.initWithPath(
this.editor
);
const process = Cc[
'@mozilla.org/process/util;1'
].createInstance(Ci.nsIProcess);
process.init(editor);
process.run(
false,
[convertedPath],
1
);
} else {
file.launch();
}
return;
}
if (file.isDirectory()) {
if (this.fileManager) {
const fmArgs = [
this.FMParameter,
path
];
const manager = Cc[
'@mozilla.org/file/local;1'
].createInstance(Ci.nsIFile);
manager.initWithPath(
this.fileManager
);
const process = Cc[
'@mozilla.org/process/util;1'
].createInstance(Ci.nsIProcess);
process.init(manager);
process.run(
false,
fmArgs,
fmArgs.length
);
} else {
file.launch();
}
}
}
};
if (window.gBrowser) {
Appmenu.init();
}
Alles anzeigen