Полезная информация

В мире Mozilla происходит много интересных событий. Но вам не нужно постоянно посещать новостные сайты, чтобы быть в курсе всех изменений. Зайдите на ленту новостей Mozilla Россия.

№1722608-02-2025 15:17:48

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

скрытый текст

А чего это там код
два раза два раза
повторяется?


Исправь, если у тебя так.


А правка, может такую попробуй

скрытый текст

Выделить код

Код:

/*
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "if(!event.button) this.handleEvent(event);"); // Ignore middle-click in Firefox 89+
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");
*/
addEventListener("popupshowing", () => mp.updateMenu(), false, mp);
addEventListener("command", mp.onmousedown = mp.onclick = e => {
	if (!e.button || e.type.endsWith("k")) mp.handleEvent(e);
}, false, mp);
mp.toggleAttribute("context");
addEventListener("popuphidden", () => mp.destroyMenu(), false, mp);

Отсутствует

 

№1722708-02-2025 21:22:48

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 133.0

Re: Custom Buttons

Dumby
Спасибо! Кнопка заработала.
Огромная просьба. Посмотри еще раз код
Дополнительные пункты в контекстном меню кнопки из моего
предыдущего поста. Очень полезная кнопка. Твоя правка к сожалению не сработала.
Ни один пункт меню не работает.

Отредактировано Garalf (08-02-2025 23:31:32)

Отсутствует

 

№1722809-02-2025 21:47:01

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

Огромная просьба. Посмотри еще раз код
Дополнительные пункты в контекстном меню кнопки

Посмотрел.
На сей раз на 137.0a1 (2025-02-09).


Создал кнопку с предоставленным кодом инициализации.
Сделал предложенные правки. Рестарт.


Пункты «Показать Id кнопки» и «Переместить кнопку...» точно работают.
Остальные не проверял, поскольку, таким образом, утверждение
«Ни один пункт меню не работает» уже не подтвердилось.

Отсутствует

 

№1722909-02-2025 23:58:26

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Да, все верно. Перегрузил лису и кнолка зареботала.

Dumby пишет

Остальные не проверял

Проверь пожалуйста пункт меню Редактирование кнопки.
У меня не работает.

Отсутствует

 

№1723010-02-2025 09:59:59

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

Проверь пожалуйста пункт меню Редактирование кнопки.

Это не представляется возможным,
поскольку меня такого пункта вообще нет.


Есть пункт «Редактировать…».
#custombuttons-contextpopup-edit,
ну тот, который самый первый сверху.


Но этот пункт не из добавляемых кодом, а пункт самого расширения.


Если имееется в виду именно он, и если не работает именно он,
то, может, обновление встало криво,
или startupCache при этом, после рестарта не очистился,
или, даже не знаю что ещё.

Отсутствует

 

№1723111-02-2025 10:18:29

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Раз теме тишина попрошу полечить еще одну кнопку.
UserCSSLoader

скрытый текст

Выделить код

Код:

(obj => {
    this.onclick = obj.click.bind(obj);
    this.oncontextmenu = obj.contextmenu.bind(obj);
    this.tooltipText = "L: Reload userChrome.css\nM: CB Menu\nR: Reload userContent.css";
})({
    async click(e) {
        if (e.button == 1) return gShowPopup(self);
        if (e.button || !this.chromeSheet) return;
        await this.reload(this.chromeSheet);
        this.restyle(0);
    },
    async contextmenu(e) {
        if (e.ctrlKey || e.shiftKey || e.detail != 1 || !this.contentSheetURL) return;
        e.preventDefault();

        var count = Services.ppmm.childCount, one = count == 1;
        var data = await this.reloadTab("chrome://extensions/content/dummy.xul", one ? false : {});
        if (one) this.reloadTab();
        else if (data) {
            var url = "data:," + encodeURIComponent(
                self.Help + this.contentSheetURL + '", ' + JSON.stringify(data) + ");"
            );
            var types = ["web", "file", "extension"];
            for(var ind = 0; ind < count; ind++) {
                var child = Services.ppmm.getChildAt(ind);
                types.includes(child.remoteType) && child.loadProcessScript(url, false);
            }
        }
        this.restyle(250);
    },
    async reload(sheet, obj) {
        try {var style = await (await fetch(sheet.href)).text();}
        catch (ex) {return obj;}
        InspectorUtils.parseStyleSheet(sheet, style);
        if (obj) obj[sheet.href] = style;
        for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
            var rule = sheet.cssRules.item(ind);

            rule.type == rule.IMPORT_RULE
            && rule.styleSheet.href.startsWith("file:///")
            && await this.reload(rule.styleSheet, obj);
        }
        return obj;
    },
    reloadTab(url, obj) {
        var tab = gBrowser.addTab(url, {skipAnimation: true});
        tab.style.setProperty("display", "none", "important");
        return new Promise(resolve => {
            var result, stop, destroy = () => {
                if (!stop) resolve(result), gBrowser.removeTab(tab), stop = true;
            }
            setTimeout(destroy, 500);
            try {
                tab.linkedBrowser.addEventListener("DOMContentLoaded", async e => {
                    var sheet = this.getSheet(e.target, this.contentSheetURL);
                    if (sheet) result = await this.reload(sheet, obj);
                    destroy();
                }, {once: true});
            } catch(ex) {
                destroy();
            }
        });
    },
    getSheet(doc, href) {
        var sheets = InspectorUtils.getAllStyleSheets(doc);
        return sheets.find(sheet => sheet.href == href);
    },
    get contentSheetURL() {
        var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
        file.append("userContent.css");
        if (!file.exists()) return null;
        delete this.contentSheetURL;
        return this.contentSheetURL = Services.io.newFileURI(file).spec;
    },
    get restyle() {
        var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
        var uri = Services.io.newURI("data:text/css,:root{}"), type = sss.USER_SHEET;
        delete this.restyle; return this.restyle = delay => setTimeout(() => {
            sss.loadAndRegisterSheet(uri, type);
            sss.unregisterSheet(uri, type);
        }, delay);
    },
    get chromeSheet() {
        var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
        file.append("userChrome.css");
        if (!file.exists()) return null;

        var href = Services.io.newFileURI(file).spec;
        var sheet = this.getSheet(document, href);
        if (!sheet) return null;

        delete this.chromeSheet; return this.chromeSheet = sheet;
    }
});

Напомню - это 136.b3  Кстати, пункт меню Edit в контекстном меню кнопки
неожиданно заработал.

Отредактировано Garalf (11-02-2025 10:19:25)

Отсутствует

 

№1723211-02-2025 14:47:20

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

еще одну кнопку

Содержимое вкладки «Справка» предлагается угадать?

Отсутствует

 

№1723311-02-2025 16:27:54

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 133.0

Re: Custom Buttons

Dumby
Справка

скрытый текст

Выделить код

Код:

((href, data) => {
	var en = Services.ww.getWindowEnumerator(null);
	if (!en.hasMoreElements()) return;
	var doc = en.getNext().document;

	var du = Components.classes["@mozilla.org/inspector/dom-utils;1"]
		.getService(Components.interfaces.inIDOMUtils);
	var reload = sheet => {
		var style = data[sheet.href]; if (!style) return;

		du.parseStyleSheet(sheet, style);
		for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
			var rule = sheet.cssRules.item(ind);

			rule.type == rule.IMPORT_RULE
			&& rule.styleSheet.href.startsWith("file:///")
			&& reload(rule.styleSheet);
		}
	}
	var sheet = du.getAllStyleSheets(doc).find(sheet => sheet.href == href);
	if (sheet) reload(sheet);
})("

Отсутствует

 

№1723412-02-2025 11:36:42

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

Справка

Другое дело.

Напомню - это 136.b3

Что-то я не вижу в кнопке
ни использования «on…» атрибутов, ни импорта модулей.


Но многовато чего-то совсем древнего,
поэтому попробую чуть переделать.
Код со Справки перенёс в Инициализацию.

скрытый текст

Выделить код

Код:

(code => {
	this._handleClick = () => {
		var href = getHref("hrome");
		if (!href) return;
		var sheet = getSheet(document, href);
		sheet && (this._handleClick = () => reload(sheet).then(restyle))();
	}
	this.onauxclick = e => e.button > 1 || gShowPopup(this);
	this.oncontextmenu = e => {
		if (e.ctrlKey || e.shiftKey || e.detail != 1) return;
		e.preventDefault();
		reloadContentSheet();
	}
	this.tooltipText = "L: Reload userChrome.css\nM: CB Menu\nR: Reload userContent.css";

	var getHref = str => {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append(`userC${str}.css`);
		if (file.exists()) return Services.io.newFileURI(file).spec;
	}
	var getSheet = (doc, href) =>
		InspectorUtils.getAllStyleSheets(doc).find(sheet => sheet.href == href);

	var reload = async (sheet, obj) => {
		try {var style = await (await fetch(sheet.href)).text();}
		catch {return obj;}

		InspectorUtils.parseStyleSheet(sheet, style);
		if (obj) obj[sheet.href] = style;
		for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
			var rule = sheet.cssRules.item(ind);

			rule.type == rule.IMPORT_RULE
			&& rule.styleSheet.href.startsWith("file:///")
			&& await reload(rule.styleSheet, obj);
		}
		return obj;
	}
	var restyle = delay => {
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		var uri = Services.io.newURI("data:text/css,:root{}"), type = sss.USER_SHEET;
		var regUnreg = () => {
			sss.loadAndRegisterSheet(uri, type);
			sss.unregisterSheet(uri, type);
		};
		(restyle = delay => setTimeout(regUnreg, delay))(delay);
	}
	var reloadContentSheet = async () => {
		var href = getHref("ontent");
		if (!href) return;

		var {br} = this;
		if (!br) {
			br = this.br = document.createXULElement("browser");
			br.setAttribute("type", "content");
			this.append(br);
			await new Promise(resolve => br.addEventListener("pageshow", resolve, {once: true}));
		}
		var sheet = getSheet(br.contentDocument, href);
		if (!sheet) return;

		br.remove();
		delete this.br;
		var {ppmm} = Services;
		var re = /^(?:web|file|extension|privileged)/;
		var prfx = "data:," + encodeURIComponent(code.trim() + sheet.href + '", ');

		(reloadContentSheet = async () => {
			var data = await reload(sheet, Object.create(null));
			var url = prfx + encodeURIComponent(JSON.stringify(data)) + ");"
			for(var ind = 0, count = ppmm.childCount; ind < count; ind++) {
				var child = ppmm.getChildAt(ind);
				re.test(child.remoteType) && child.loadProcessScript(url, false);
			}
			restyle(350);
		})();
	}
})(`

((href, data) => {
	var en = Services.ww.getWindowEnumerator(null);
	if (!en.hasMoreElements()) return;

	var doc = en.getNext().document;

	var reload = sheet => {
		var style = data[sheet.href];
		if (!style) return;

		InspectorUtils.parseStyleSheet(sheet, style);
		for(var ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
			var rule = sheet.cssRules.item(ind);

			rule.type == rule.IMPORT_RULE
			&& rule.styleSheet.href.startsWith("file:///")
			&& reload(rule.styleSheet);
		}
	}
	var sheet = InspectorUtils.getAllStyleSheets(doc).find(sheet => sheet.href == href);
	sheet && reload(sheet);
})("
`);

Отсутствует

 

№1723520-02-2025 18:00:22

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Нашел еще две удобные неработающие кнопки
1. "Два в одном"

скрытый текст

Выделить код

Код:

//Настройка функций кликов мыши для кнопки ....................
this.onclick =e=> {if(e.button==0)BrowserOpenAddonsMgr();//ЛКМ
                   if(e.button==1)gShowPopup(this);      //СКМ
                   if(e.button==2 && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){//ПКМ
                   setTimeout(function(){document.getElementById("custombuttons-contextpopup").hidePopup();},0);openPreferences();}};
                   this.tooltipText="Л: Addons\nС: CBMenu\nП: Preferences";
//data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAABuwAAAbsBOuzj4gAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAMJSURBVDiNdVPNa1N5FD2/30uMeX1IPjFmE3BKERdhRKqgwyBqibsuZqGYRRf2H3Ax0J28je22XcRCwwhNKh0ki24GSn20iE3TNMKjONKkHyYUokmbMR++F/Oa966baUkVL9zNvfdc7jmHy4gIP4uFhcUFABgejgz/dIiITjKZTF3JZtXKzEziPgC+tbV3tLW1dwSAz8wk7mezaiWZTF3pxZxaEI2O3mk2NSoWy918/kO73e5Qu92hQuFDu1gsd5tNjaLR0Tu9GEZEiMdf/Orz+Ry6rndu3rz21u/3ciLCp08VAEAgcB6MMRwc1KzV1exVURQdh4eHndHRhyoDwBYXX38Mhy+f17S26fW6hHR6HUtLKzBNEwAgCAKGhm7hxo3rqNXqZl+fU9jcfF+JRH6/wImIEolZeX+/bElSn5DLqUins0TUKXBuRDk3okSdQjqdpVxOhST1Cfv7ZSuRmJXpRAiAK0pGL5UqNDUVt2T5ab6XJxFBlp/mp6biVqlUIUVZ0wFwIgKfnIwnl5fXW8FgwKlpOiRJZB6PW/7eLY/HLUuSyDRNRzB4wbm8vN6anIwnbaq6yTWtaw+FfoHLJcHn8+PsWdsPdnu9XkiSG3t7H1Gvf0GptGvf3n7PTyiMj0+35uaWqFAoWoqy8gMFRVnJFwpFa35eofHx6dYxBRsAxGKz94jsgig6oOsGGxi41K+qm3nLIhkAOGdPBgYu9TcaGjt3ToTH4xZisdl7AP7hjDFWLldjLpfHqesNq15v4etXg4dCF/sDgeBcIBCcC4Uu9h8ddXmzqUHXm5bf73dWq7UYY4zZiIgikeEHq6uvXA6Hvf748Z9vOh2XcHBQ46LoAGMMnz/XuWmaMIwv5vPnz34zDMNltzvqRESs95mmp/+6Ojh4faPR+K9rWQaFw+EzjDG8e/evYbefYW63z5bJpAcfPRp5e4zhvUpnMmlbLpepplJ/jwwN3XXu7u5oOzvb2u3bt5wvX86PbGysVTOZtVMWnboAABhjjP4vTkxMpABgbGzsj+97x/ENhTvQG3f/6bsAAAAASUVORK5CYII=
//forum.mozilla-russia.org/viewtopic.php?pid=704725#p704725
//openWebPanel("Downloads","about:downloads");


Страница настроек - вызывается, расширения - нет.


2. Поиск по изображению

скрытый текст

Выделить код

Код:

/*Initialization Code*/
// Добавить подменю "Поиск изображения в" в контекстном меню изображений, от 31.05.2019. .............
(()=> {
   var copyimage = document.getElementById("context-copyimage-contents");
   var contextMenu = copyimage.parentNode;

    var array = [
      ['Google', 'https://www.google.lv/favicon.ico', 'http://www.google.com/searchbyimage?image_url='],
      ['Яндекс', 'http://yastatic.net/morda-logo/i/favicon_islands.ico', 'https://yandex.com/images/search?rpt=imageview&img_url='],
      ['Bing', 'https://www.bing.com/s/a/bing_p.ico', 'https://www.bing.com/images/searchbyimage?FORM=IRSBIQ&cbir=sbi&imgurl='],
      ['Tineye', 'http://tineye.com/favicon.ico', 'http://tineye.com/search?pluginver=bookmark_1.0&url='],
      ['SauceNao', 'https://saucenao.com/favicon.ico', 'https://saucenao.com/search.php?url='],
      ['IQDB', 'https://iqdb.org/favicon.ico', 'https://iqdb.org/?url='],
   ];
   var menu = contextMenu.insertBefore(document.createXULElement("menu"), copyimage); 
   menu.setAttribute("label", "Поиск изображения в ...");
   menu.setAttribute("class", "menu-iconic");
   
   
   menu.onclick =e=> { e.target.nodeName == 'menu' && search(array[0][2]); setTimeout(()=> contextMenu.hidePopup(), 20) };
   addEventListener("popupshowing", ()=> menu.hidden = copyimage.hidden, true, contextMenu);
    addDestructor(() => menu.remove());
   var menuPopup = menu.appendChild(document.createXULElement("menupopup"));              
   array.forEach(m=> {
      var mItem = menuPopup.appendChild(document.createXULElement("menuitem"));
      mItem.setAttribute("label", m[0]);       
      mItem.setAttribute("image", m[1]);
      mItem.setAttribute("class", "menuitem-iconic");
      mItem.onclick =()=> search(m[2]);
   });
  function search(finder) { gBrowser.selectedTab = gBrowser.addTrustedTab(finder + encodeURIComponent(gContextMenu.imageURL)) };
  
   var mItem = menuPopup.appendChild(document.createXULElement("menuitem"));
   mItem.setAttribute("label", 'Искать во всех поисковиках');
     mItem.setAttribute("class","menuitem-iconic");
        mItem.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADZElEQVR42oXSe0xbVRwH8N8597a35e7SAmUFukGhY66AMiNzw4GvTCQLCssc4pQsmTOa+ERMlrktW1zigpnEuWn8x2eMmiVGMBDCFjS+cJHEDIk8RoSNV1+3tLWX3t72nnO8sETJFrfvP7/k5Pw+Ob9fDoLrYi8ow9GFUbq+5aRLrGraqQcX6hlF2wkFEWn6EDIJAyQy9+3VT58cWb6PrmvmjGZS/FSHlxbXtbtLobGqJlPKMhNepWb0V8hKpi8FNd/gzCCogbdmvtg/8C9gz9+Eor5x5nn2tDOZ98Dx2i3C/uYai9mOGCxiDQIIg1/PgUCEhyvDMox9PXYBkPzaaoAzAFL0yle7ShtqO/eWJ9w1vJ1oGsVzYhTNATMQG4Q1G40ldDw6PB339fs7VgMmA0jnHfioo6F9Z3tDYYJzKSZKMYcDvAILiICfZUNYEYFixMKpKBk8MfTTasBsACnngU/6Wg4+VL/VFSOmBHAYY1BAhzAvgmyMEIkh0EUOBD1Eu49eStwA5D3z4TcPv9TYdHvxEhXSGgaEIMkJkKAS+ONmSKYolOVqMBlRSd8bv8v/AQXlJoiHUE5rZ1tFa/Nhj1uVrCaNMoQxYSaIqDxAGqAuQ2XZ1iS8HUTpkZMXe1eATOdtiCcJblGe1a3PnWp0uEvfv3tbeX5RhSet/q3wxh6xgAncZUmw2hxEryQx9/LHE7I2RV5HNudGFAtcZsuQd919u/kiz56JOwobcjd6xerqKnB4HIB4RvMzCM1DGj8X0GD64pL2Xdf850z+4djKC8wl24XUpj1P75sdfe/+sSHoq3uQnSuwa+s2Vy5uubd2jQWUTAYchJK8HpyIzvv7p/rMkV/OzPeeGEWi+x6LufiR573h8VO7Zr4n9fEZNeiqXHOofFvPsKPkS1fKsX5JU6uNZQh8Wh/hRcuvOC3/fLXrcGj52yOo7zzU9udvbzbN9uglggK2MhsnvfiE0tY9tfed7vM98D+x53tx1DdG0Z2b97Ejf3SRSiGGCysyENvdoipZa4+/8NmPp8+N+Ei2ZEVpQhG71rdcqFFozD+xcoQugFUvlDS8wWsl8Uebk5C79ujjHwyc6R/309ysDBRauEzhJkG9ErAdG0xEe6yVkwqcr+549/zZgcmQ7pAEkH2TDG6RfwCoJI0uEWJP5QAAAABJRU5ErkJggg==");   
   mItem.onclick =()=> { array.forEach(m=> {  gBrowser.selectedTab = gBrowser.addTrustedTab(m[2] + encodeURIComponent(gContextMenu.imageURL)) }) };
  
})();

var style = custombutton.buttonGetHelp(self).replace(/id/g, _id);
var uri = makeURI('data:text/css,'+ encodeURIComponent(style));
var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
sss.loadAndRegisterSheet(uri, 0);

Отредактировано Garalf (20-02-2025 18:45:49)

Отсутствует

 

№1723620-02-2025 18:55:55

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

Страница настроек - вызывается, расширения - нет.

BrowserOpenAddonsMgr
BrowserAddonUI.openAddonsMgr

2. Поиск по изображению

Может (в двух местах)
imageURL
imageInfo.currentSrc

Отсутствует

 

№1723720-02-2025 21:08:16

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Спасибо! Все работает.

Отсутствует

 

№1723801-03-2025 12:41:31

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

Dumby
В 136 пререлизе у меня не устанавливаются CB и ATB
Появляется сообщение: Дополнение не может быть установлено,
так как оно не было проверено.
Может, что то в config.js не так?

скрытый текст

Выделить код

Код:

//
try {(jsval => {
	var dbg, gref, genv = func => {
		var sandbox = new Cu.Sandbox(g, {freshCompartment: true});
		Cc["@mozilla.org/jsdebugger;1"].createInstance(Ci.IJSDebugger).addClass(sandbox);
		(dbg = new sandbox.Debugger()).addDebuggee(g);
		gref = dbg.makeGlobalObjectReference(g);
		return (genv = func => func && gref.makeDebuggeeValue(func).environment)(func);
	}
	var g = Cu.getGlobalForObject(jsval), o = g.Object, {freeze} = o, disleg;

	var AC = "AppConstants", uac = `resource://gre/modules/${AC}.`;
	var lexp = () => lockPref("extensions.experiments.enabled", true);
	if (o.isFrozen(o)) { // Fx 102.0b7+
		lexp(); disleg = true;
		var env, def = g.ChromeUtils.defineModuleGetter;
		g.ChromeUtils.defineModuleGetter = (...args) => {
			try {
				genv();
				dbg.addDebuggee(globalThis);
				var e = dbg.getNewestFrame().older.environment;
				var obj = e.parent.type == "object" && e.parent.object;
				if (obj && obj.class.startsWith("N")) // JSM, NSVO
					obj.unsafeDereference().Object = {
						freeze: ac => (ac.MOZ_REQUIRE_SIGNING = false) || freeze(ac)
					};
				else env = e; // ESM, Lexy "var"(?)
			}
			catch(ex) {Cu.reportError(ex);}
			(g.ChromeUtils.defineModuleGetter = def)(...args);
		}
		ChromeUtils.import(uac + "jsm");
		// (?)
		env && env.setVariable(AC, gref.makeDebuggeeValue(freeze(o.assign(
			new o(), env.getVariable(AC).unsafeDereference(), {MOZ_REQUIRE_SIGNING: false}
		))));
	}
	else o.freeze = obj => {
		if (!Components.stack.caller.filename.startsWith(uac)) return freeze(obj);
		obj.MOZ_REQUIRE_SIGNING = false;

		if ((disleg = "MOZ_ALLOW_ADDON_SIDELOAD" in obj)) lexp();
		else
			obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
			lockPref("extensions.legacy.enabled", true);

		return (o.freeze = freeze)(obj);
	}
	lockPref("extensions.experiments.enabled", true);
	lockPref("xpinstall.signatures.required", false);
	lockPref("extensions.langpacks.signatures.required", false);

	var useDbg = true, xpii = "resource://gre/modules/addons/XPIInstall.jsm";
	if (Ci.nsINativeFileWatcherService) { // Fx < 100
		jsval = Cu.import(xpii, {});
		var shouldVerify = jsval.shouldVerifySignedState;
		if (shouldVerify.length == 1)
			useDbg = false,
			jsval.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);
	}
	if (useDbg) {
		jsval = g.ChromeUtils.import(xpii);

		var env = genv(jsval.XPIInstall.installTemporaryAddon);
		var ref = name => {try {return env.find(name).getVariable(name).unsafeDereference();} catch {}}
		jsval.XPIDatabase = (ref("lazy") || {}).XPIDatabase || ref("XPIDatabase");

		var proto = ref("Package").prototype;
		var verify = proto.verifySignedState;
		proto.verifySignedState = function(id) {
			return id ? {cert: null, signedState: undefined} : verify.apply(this, arguments);
		}
		dbg.removeAllDebuggees();
	}
	if (disleg) jsval.XPIDatabase.isDisabledLegacy = () => false;
})(
	"permitCPOWsInScope" in Cu ? Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}) : Cu
);}
catch(ex) {Cu.reportError(ex);}
//
//
(async xp => {
	var imp, ids = [
		"custombuttons@xsms.org",
	];
	if (Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).inSafeMode) return;

	if (Cr.NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
		var {XPIInternal} = (imp = url => Cu.import(url, {}))(xp + "jsm");

	else { // Fx 101+
		var g = Cu.getGlobalForObject(Cu), te = new g.TextEncoder();
		var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
		var rph = ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);

		var impJSM = g.ChromeUtils.import, impESM = g.ChromeUtils.importESModule;
		try {var exp = impESM(xp + "sys.mjs");} catch {exp = impJSM(xp + "jsm");}
		var {XPIInternal} = exp;

		var ss = (subst, uri, ext) => rph.setSubstitution(
			subst, ios.newURI(uri.resolve("startup." + ext))
		);
		try {var useESM = parseInt(Services.appinfo.platformVersion) >= 108;} catch {}

		imp = (uri, id) => {
			var subst = te.encode(id).join("");
			var url = `resource://${subst}/`;
			if (useESM) try {
				ss(subst, uri, "mjs");
				return impESM(url);
			} catch(ex) {
				if (!ex || ex.message != "Failed to load " + url)
					return Cu.reportError(ex);
			}
			ss(subst, uri, "jsm");
			return impJSM(url);
		}
	}
	var load = async (file, id) => {
		var rootURI = XPIInternal.getURIForResourceInFile(file, "");
		imp(rootURI, id).start(rootURI);
	}
	var proto = XPIInternal.BootstrapScope.prototype;
	var func = proto._beforeCallBootstrapMethod;

	proto._beforeCallBootstrapMethod = () => {
		proto._beforeCallBootstrapMethod = func;
		for(var {id, loader, file} of XPIInternal.XPIStates.enabledAddons())
			ids.includes(id) && !loader && load(file, id);
	}
})("resource://gre/modules/addons/XPIProvider.");
//

// version, date year-month-day: 2024-6-12
(async () => {
    var file = Services.dirsvc.get("UChrm", Ci.nsIFile), iname;
    file.append("user_chrome_files");
    file.append("user_chrome.manifest");
    if (!file.exists() || !file.isFile())
        return;
    switch (Services.appinfo.name) {
        case "Firefox":
            iname = "user_chrome.js";
            break;
        case "Thunderbird":
            iname = "user_chrome_tb.js";
            break;
        default:
            return;
    }
    Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
    .autoRegister(file);
    var sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), {
        wantComponents: true,
        sandboxName: "UserChromeFiles",
        wantGlobalProperties: ["ChromeUtils"],
    });
    Services.scriptloader.loadSubScript(`chrome://user_chrome_files/content/user_chrome/${iname}`, sandbox, "UTF-8");
})();

policies.json

скрытый текст

Выделить код

Код:

{
	"policies": {
		"BlockAboutAddons": false,
		"BlockAboutConfig": false,
		"BlockAboutProfiles": false,
		"BlockAboutSupport": false,
		"DisableSetDesktopBackground": true,
		"DisableMasterPasswordCreation": true,
		"DisableFeedbackCommands": true,
		"DisableFirefoxStudies": true,
		"DisableProfileRefresh": true,
		"DisableTelemetry": true,
		"NoDefaultBookmarks": false,
		"OverrideFirstRunPage": "",
		"OverridePostUpdatePage": "",
    "SearchEngines": {
      "Add": [
				{
					"Name": "Google RU",
					"URLTemplate": "https://www.google.com/search?q={searchTerms}&region=ru-RU",
					"Method": "GET",
					"Description": "metager",
					"IconURL": "https://www.google.com/favicon.ico"
				},
				{
					"Name": "Metager",
					"URLTemplate": "https://metager.de/meta/meta.ger3?eingabe={searchTerms}",
					"Description": "metager",
					"IconURL": "https://metager.de/favicon.ico"
				}
			],
			"Default": "Google RU"
		},
				"Preferences": {
			"browser.search.update": false,
			"browser.search.newSearchConfig.enabled": false,
			"browser.warnOnQuitShortcut": false,
			"datareporting.policy.dataSubmissionPolicyBypassNotification": true,
			"extensions.blocklist.enabled": false,
			"extensions.getAddons.showPane": false,
			"extensions.experiments.enabled": true,
			"xpinstall.signatures.required": false,
			"extensions.legacy.enabled": true,
			"extensions.htmlaboutaddons.recommendations.enabled": false,
			"intl.accept_languages": "ru-RU,ru,en-US,en",
			"print.more-settings.open": true,
			"security.ssl.errorReporting.enabled": false,
			"browser.newtabpage.activity-stream.feeds.telemetry": false,
			"toolkit.legacyUserProfileCustomizations.stylesheets": true,
			"widget.macos.native-context-menus": false
		}
	}
}

Отсутствует

 

№1723901-03-2025 16:54:05

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

Может, что то в config.js не так?

Антиподписячий код не выглядит как последний выкладывавшийся.
Проверил с последним на 136.0 RC build3, CB и ATB установились нормально.
Ещё, UCF-стафф не последний, но это к вопросу отношения не имеет.

Отсутствует

 

№1724001-03-2025 21:01:33

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Спасибо за подсказку. Теперь все работает.
Ну и нашел еще одну кнопку, которая перестала работать в 136
Консоль браузера

скрытый текст

Выделить код

Код:

/*Initialization Code*/
({
	title: "Browser Console",
	url: "chrome://devtools/content/webconsole/index.html",

	icon: "chrome://devtools/skin/images/tool-webconsole.svg",
	init() {
		var trg = document.getElementById("browser");
		trg && addEventListener("DOMContentLoaded", this, false, trg);
		var id = "viewBrowserConsoleSidebar";

		var menuitem = this.element("menuitem", {
			type: "checkbox",
			label: this.title,
			id: "menu_browserConsoleSidebar",
			oncommand: `(globalThis.SidebarController || globalThis.SidebarUI).toggle("${id}");`
		}, document.getElementById("viewSidebarMenu"));

		var btn = this.element("toolbarbutton", {
			type: "checkbox",
			label: this.title,
			id: "sidebar-switcher-browserConsole",
			oncommand: `(globalThis.SidebarController || globalThis.SidebarUI).show("${id}");`,
			class: "subviewbutton subviewbutton-iconic"
		});
		document.querySelector(
			parseInt(Services.appinfo.platformVersion) >= 116
			? 'menuitem[id^="sidebar-switcher-"] + menuseparator'
			: 'toolbarbutton[id^="sidebar-switcher-"] + toolbarseparator'
		).before(btn);

		(globalThis.SidebarController || globalThis.SidebarUI).sidebars.set(id, {
			url: this.url,
			buttonId: btn.id,
			title: this.title,
			menuId: menuitem.id
		});
		(globalThis.SidebarController || globalThis.SidebarUI).isOpen && (globalThis.SidebarController || globalThis.SidebarUI).currentID == id && (globalThis.SidebarController || globalThis.SidebarUI).selectMenuItem(id);

		var popupset = this.popupset = this.element("popupset", {
			id: `CB${_id.slice(20)}-browserConsole-popupset`
		}, document.documentElement);

		var css = `\
			#${btn.id} > .toolbarbutton-icon,
			#sidebar-box[sidebarcommand="${id}"] > #sidebar-header > #sidebar-switcher-target > #sidebar-icon {
				list-style-image: url(${this.icon});
			}`;
		var str = (cbu.cb || "") + "data:text/css," + encodeURIComponent(css), type = windowUtils.USER_SHEET;
		windowUtils.loadSheetUsingURIString(str, type);

		addDestructor(() => {
			(globalThis.SidebarController || globalThis.SidebarUI).sidebars.delete(id);
			btn.remove(); menuitem.remove(); popupset.remove();
			windowUtils.removeSheetUsingURIString(str, type);
		});
		parseInt(Services.appinfo.platformVersion) < 73 
			&& "insertFTLIfNeeded" in MozXULElement
			&& MozXULElement.insertFTLIfNeeded("toolkit/main-window/editmenu.ftl");

		self.onclick = e => {
			if (e.button == 2) return;
			if (!e.button && !e.shiftKey) return (globalThis.SidebarController || globalThis.SidebarUI).toggle(id);
			var st = gBrowser.selectedTab, tab;
			if (!e.ctrlKey) tab = gBrowser.visibleTabs.find(tab => {
				var br = gBrowser.getBrowserForTab(tab);
				return br.currentURI.spec == this.url || (
					"_cachedCurrentURI" in br
					&& br._cachedCurrentURI.spec == this.url
				)
			});
			if (tab == st) return;
			if (!tab) tab = gBrowser.addTrustedTab(this.url);
			gBrowser.moveTabTo(tab, st._tPos + 1);
			gBrowser.selectedTab = tab;
		}
		for(var br of gBrowser.browsers) {
			if (br.currentURI.spec != this.url) continue;
			var doc = br.contentDocument;
			if (doc && (
				doc.readyState == "complete" ||
				doc.readyState == "interactive"
			))
				doc.querySelector(
					"main#app-wrapper,div#output-container"
				).childElementCount
					? this.defineDocPopupset(doc)
					: this.handleEvent({target: doc});
		}
		if (!btn.hasAttribute("checked")) return;
		var doc = (globalThis.SidebarController || globalThis.SidebarUI).browser.contentDocument;
		if (doc.documentURI != this.url) btn.doCommand();
		else if (doc.readyState == "complete") this.defineDocPopupset(doc);
	},
	defineDocPopupset(doc) {
		this.definePopupset(
			doc.querySelector("popupset") ||
			doc.documentElement.appendChild(doc.createXULElement("popupset"))
		);
	},
	get definePopupset() {
		var append = customElements.get("menuitem")
			? popup => {
				this.popupset.appendChild(popup);
				popup.setAttribute("oncommand", "event.target.cmd()");
				for(var node of [...popup.querySelectorAll("menuitem")]) {
					var menuitem = document.importNode(node, true);
					menuitem.cmd = Services.els.getListenerInfoFor(node)
						.find(inf => inf.type == "command").listenerObject;
					popup.replaceChild(menuitem, node);
				}
				return popup;
			}
			: this.popupset.appendChild.bind(this.popupset);

		delete this.definePopupset;
		return this.definePopupset = popupset => popupset.appendChild = append;
	},
	lss: Services.scriptloader.loadSubScript,
	async handleEvent({target: doc}) {
		if (!doc || doc.documentURI != this.url) return;

		var win = doc.defaultView;
		if (
			win.docShell.name == "toolbox-panel-iframe-webconsole" ||
			doc.DOMContentLoadedEventHandled
		)
			return;
		doc.DOMContentLoadedEventHandled = true;
		if (this instanceof XULElement) // Custom Buttons
		"custombuttonsConsole" in win || this.lss(
			"chrome://custombuttons/content/consoleOverlay.js", win
		);
		var cw = win.isChromeWindow, bc;
		if (!cw) {
			if (doc.visibilityState == "hidden") {
				var {focus} = win;
				win.focus = () => win.focus = focus;
			}
			doc.title = this.title;
			var link = doc.createElement("link");
			link.setAttribute("rel", "shortcut icon");
			link.setAttribute("href", this.icon);
			doc.head.prepend(link);

			var br = win.docShell.chromeEventHandler;
			var cmAttr = br.getAttribute("contextmenu");
			cmAttr && br.removeAttribute("contextmenu");
			win.onbeforeunload = () => {
				if (bc) bc.chromeWindow = {close() {}};
				cmAttr && br.setAttribute("contextmenu", cmAttr);
			}
		}
		bc = await this.console(win);
	},
	get console() {
		// Bug 1579090 - WebConsole should handle ObjectFront when needed (for non-primitive Console API args + Evaluation results) (Firefox 73+)
		https://bugzilla.mozilla.org/show_bug.cgi?id=1579090
		var vers = parseInt(Services.appinfo.platformVersion);
		this.bug1579090 = vers > 73 || (vers == 73 && !(
			"_setCurrentURI" in gBrowser.selectedBrowser // https://bugzil.la/1431214
		));
		delete this.console;
		return this.console = this.bug1579090 ? async win => {
			//await this.loader.bcm._browserConsoleInitializing;
			var key = "CBBrowserConsolePromise", {wins} = this.loader;
			win[key] = win.Object.create(null);
			win[key].promise = new win.Promise(resolve => win[key].resolve = resolve);
			win[key].destroy = () => {
				win[key].resolve();
				delete win[key];
				wins.splice(wins.indexOf(win), 1);
			}
			wins.unshift(win);
			wins.length > 1 && await wins[1][key].promise;

			var bc = await new this.loader.console(win).toggleBrowserConsole();
			win[key].destroy();
			return bc;

		} : async win => {
			this.loader.Services.ww.wins.push(win);
			return await new this.loader.HUDService().toggleBrowserConsole();
		}
	},
	get loader() {
		delete this.loader;
		var url = parseInt(Services.appinfo.platformVersion) >= 96
			? "resource://devtools/shared/loader/Loader.jsm"
			: "resource://devtools/shared/Loader.jsm";
		if (this.bug1579090) {
			var g = Cu.import(url, {}), key = "CBBrowserConsoleLoader";
			addDestructor(reason => reason[5] == e && key in g && g[key].destroy());
			if (key in g) return this.loader = g[key];
			var {BrowserConsoleManager} = g.require(
				"devtools/client/webconsole/browser-console-manager"
			);
			return this.loader = g[key] = {
				wins: [],
				bcm: BrowserConsoleManager,
				console: class extends BrowserConsoleManager.constructor {
					constructor(win) {
						super();
						this.win = win;
					}
					openWindow() {
						var {win} = this;
						win.addEventListener("unload", () => {
							win.CBBrowserConsolePromise &&
								win.CBBrowserConsolePromise.destroy();
							this.closeBrowserConsole.call(this);
						}, {once: true});
						delete this.win;
						return win;
					}
				},
				destroy() {
					this.wins = null;
					delete g[key];
				}
			};
		}
		var id = _id + "-browser-console";
		url += "?" + id;
		var loader = {exports: {}}, nsvo = Cu.import(url, loader);
		addDestructor(reason => reason[5] == "e" && Cu.unload(url));

		if (id in nsvo) return this.loader = nsvo[id];

		var dir = "resource://devtools/client/webconsole/";
		try {
			this.lss(dir + "hudservice.js", loader);
		} catch(ex) {
			// Bug 1570320 - Rename hudservice.js into browser-console-manager.js (Firefox 70+)
			// https://bugzilla.mozilla.org/show_bug.cgi?id=1570320
			this.lss(dir + "browser-console-manager.js", loader);
			this.lss("data:,this.HUDService=BrowserConsoleManager", loader);
		}
		var e = new CustomEvent("DOMContentLoaded", {bubbles: false}), ww = loader.Services.ww;
		loader.Services.ww = Cu.getGlobalForObject(nsvo).Object.create(ww, {
			wins: {value: []},
			openWindow: {value: function() {
				var win = this.wins.shift();
				win.setTimeout(() => win.dispatchEvent(e), 0);
				return win;
			}}
		});
		return this.loader = nsvo[id] = loader;
	},
	element(name, attrs, parent) {
		var node = document.createXULElement(name);
		for(var attr in attrs) node.setAttribute(attr, attrs[attr]);
		parent && parent.append(node);
		return node;
	}
}).init();
this.tooltipText = "Консоль браузера" +"\n"+"\n"+
                   "ЛКМ: В боковой панели" +"\n"+
                   "СКМ: В новой вкладке" +"\n"+
                   "ПКМ: Стандартное меню "

Отсутствует

 

№1724102-03-2025 19:14:34

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

Ну и нашел еще одну кнопку, которая перестала работать в 136
Консоль браузера

Нее, от кнопки я решил отказаться.


На новых версиях, перехожу на скрипт для UCF.
Для сандбокса, custom_script.js, scriptsbackground
(в окна не совать!)


Локализация там, по сути, особо не нужна,
просто эксперимент с mock-fluent'ом.

скрытый текст

Выделить код

Код:

(async url => {

	var img = "chrome://devtools/skin/images/tool-webconsole.svg";

	var strings = {
		ru: {
			label: "Консоль браузера",
			tooltip: [
				"Л: Сайдбар",
				"С: Вкладка",
				"П: Окно"
			]
		},
		def: {
			label: "Browser console",
			tooltip: [
				"L: Sidebar",
				"M: Tab",
				"R: Window"
			]
		},
	};

	var fluentLabel = "browser-console-label";
	var fluentButton = "browser-console-button";
	var fluentFile = "user/browser-console.ftl";

	var version = parseInt(Services.appinfo.platformVersion);

	for (var [loc, data] of Object.entries(strings)) {
		var lab = data.label;
		strings[loc] = `${
			fluentButton
		} =\n.label = ${lab}\n.tooltiptext = ${
			data.tooltip.map(str => "\n " + str).join("")
		}\n${
			fluentLabel
		} =\n.label = ${lab}`;
	}
	var registry = L10nRegistry.getInstance();
	var sourceName = sfx => "u-browser-console-" + sfx;

	var getSource = (locs, meta, name) => {
		var sources = Array.from(locs, loc => ({
			path: `/localization/${loc}/${fluentFile}`,
			source: strings[loc] || strings.def
		}));
		return L10nFileSource.createMock(
			name || sourceName("pkg"), meta,
			locs, "/localization/{locale}/", sources
		);
	}
	registry.registerSources([
		getSource(Services.locale.packagedLocales, "app")
	]);

	var fileSources = new Map();
	var register = langpacks => {
		var sourcesToReg = [];
		for(var langpack of langpacks) {

			var {langpackId, languages} = langpack.startupData;
			var name = sourceName(langpackId);
			if (registry.hasSource(name)) continue;

			var fileSource = fileSources.get(langpackId);
			fileSource || fileSources.set(
				langpackId, fileSource = getSource(languages, langpackId, name)
			);
			sourcesToReg.push(fileSource);
		}
		registry.registerSources(sourcesToReg);
	}

	var file = Services.dirsvc.get("ProfD", Ci.nsIFile);
	file.append("extensions.json");
	try {
		var langpacks = JSON.parse(Cu.readUTF8File(file))
			.addons.filter(a => a.startupData?.langpackId && a.active);
		langpacks?.length && register(langpacks);
	} catch {}

	var unreg = addon => {
		if (addon.type != "locale") return;
		var name = sourceName(addon.__AddonInternal__.startupData.langpackId);
		registry.hasSource(name) && registry.removeSources([name]);
	}
	var addonListener = Object.create(null);
	for(var p of ["onDisabled", "onUninstalling", "onUninstalled"]) addonListener[p] = unreg;
	AddonManager.addAddonListener(addonListener);

	var topicLng = "webextension-langpack-startup";
	var topicWin = "browser-delayed-startup-finished";
	var {addObserver: add, removeObserver: rem} = Services.obs;

	var reg = subj => register([subj.wrappedJSObject.langpack]);

	add(reg, topicLng);
	add(onBrowserWin, topicWin);
	add(function quit(s, topic) {
		rem(quit, topic);
		rem(reg, topicLng);
		rem(onBrowserWin, topicWin);
	}, "quit-application-granted");


	var addFtlLink = (doc, href) => {
		var link = doc.createElement("link");
		link.rel = "localization";
		link.href = href;
		doc.head.prepend(link);
	}
	var maybeInsertFTL = doc => !(
		doc.head.querySelector(`[href="${fluentFile}"]`) || addFtlLink(doc, fluentFile)
	);

	var impl;
	CustomizableUI.createWidget(impl = {
		l10nId: fluentButton,
		id: "browser-console-button",
		defaultArea: CustomizableUI.AREA_NAVBAR,
		onCreated(btn) {
			maybeInsertFTL(btn.ownerDocument);
			btn.image = img;
			for(var type of this.events)
				btn.addEventListener(type, this);
		},
		events: ["command", "auxclick", "contextmenu"],
		handleEvent(e) {
			this[e.type](e);
		},
		command(e) {
			e.view.SidebarController.toggle(id);
		},
		auxclick(e) {
			if (e.button != 1) return;
			var gb = e.view.gBrowser;
			if (!e.ctrlKey && !e.shiftKey) for(var tab of gb.visibleTabs)
				if (tab.linkedBrowser?.currentURI.spec == url)
					return gb.selectedTab = tab;

			gb.selectedTab = gb.addTrustedTab(url, {index: gb.selectedTab._tPos + 1});
		},
		contextmenu(e) {
			if (!e.ctrlKey && !e.shiftKey)
				e.preventDefault(),
				this.bcm.openBrowserConsoleOrFocus();
		},
		get bcm() {
			delete this.bcm;
			return this.bcm = ChromeUtils.importESModule(
				"resource://devtools/shared/loader/Loader.sys.mjs"
			).require(
				"devtools/client/webconsole/browser-console-manager"
			).BrowserConsoleManager;
		}
	});

	var revamp = true, pref = "sidebar.revamp";
	if (Services.prefs.prefHasDefaultValue(pref)) {
		var obs = () => revamp = Services.prefs.getBoolPref(pref);
		Services.prefs.addObserver(pref, obs);
		obs();
	}

	var id = "viewBrowserConsoleSidebar";
	var addSidebar = version >= 135
		? (bars, sb) => bars.set(id, sb)
		: (bars, sb) => {
			// viewCustomizeSidebar breaks iteration in SidebarController.selectMenuItem()
			var arr = Array.from(bars);
			bars.clear();
			bars.set(id, sb);
			for(var args of arr) bars.set(...args);
		}
	var maybeAddSidebar = (sc, sb) => {
		var bars = sc.sidebars;
		bars.has(id) || addSidebar(bars, sb);
	}
	var addFluentRes = sm => sm.fluentStrings.addResourceIds([fluentFile]);
	var ce = {configurable: true, enumerable: true};

	function onBrowserWin(win) {
		win.addEventListener("DOMContentLoaded", onDOMContentLoaded);

		var doc = win.document;
		maybeInsertFTL(doc);
		var sc = win.SidebarController;

		var sep = doc.querySelector("#sidebarMenu-popup > menuseparator");
		if (sep) {
			var switcher = doc.createXULElement("menuitem");
			switcher.dataset.l10nId = fluentLabel;
			switcher.id = "sidebar-switcher-browser-console";
			switcher.addEventListener("command", () => sc.toggle(id));
			sep.before(switcher);
		}
		var sb = sc.makeSidebar({
			url,
			iconUrl: img,
			elementId: switcher?.id,
			menuL10nId: fluentLabel,
			revampL10nId: fluentLabel,
			menuId: "menu_browserConsoleSidebar"
		});
		var popup = doc.getElementById("viewSidebarMenu");
		popup.insertBefore(
			sc.createMenuItem(id, sb),
			popup.querySelector(".webextension-menuitem")
		);
		var o = win.Object;
		var def = (obj, prop, desc) => o.defineProperty(obj, prop, o.assign(desc, ce));

		def(sb, "hasOwnProperty", {value(prop) {
			prop == "extensionId" && win.setTimeout(maybeAddSidebar, 0, sc, sb);
			return o.hasOwn(sb, prop);
		}});
		addSidebar(sc.sidebars, sb);

		var br = sc.browser;
		def(UcfPrefs.dbg.ref("defaultTools", sc.init, win), id, {get() {
			var doc = br.contentDocument;
			var cust = doc?.querySelector("sidebar-customize");
			cust && maybeInsertFTL(doc) &&
				UcfPrefs.dbg.ref("l10nMap", cust.constructor, 0).set(id, fluentLabel);
			return "browserconsole";
		}});
		sc._toolsAndExtensions = null;
		var sm = sc.sidebarMain;
		if (revamp) addFluentRes(sm), sm.requestUpdate();
		else win.customElements.whenDefined("sidebar-main").then(() => addFluentRes(sm));
	}

	var noop = () => {};

	var setSmall = hdr => hdr.shadowRoot
		.querySelector("moz-button").setAttribute("size", "small");

	var addCloseBtn = async (doc, win) => {
		var main = doc.body.querySelector("main");
		var {promise, resolve} = win.Promise.withResolvers();
		var mo = new win.MutationObserver(resolve);

		mo.observe(main, {childList: true});
		await promise;
		mo.disconnect();

		var header = main.querySelector("header");
		addFtlLink(doc, "browser/sidebar.ftl");

		var url = "chrome://browser/content/sidebar/sidebar-panel-header.mjs";
		UcfPrefs.dbg.makeGlobalObjectReference(win).executeInGlobal(
			`ChromeUtils.importESModule("${url}", {global: "current"});`
		);
		var sph = doc.createElement("sidebar-panel-header");
		header.append(sph);
		win.setTimeout(setSmall, 50, sph);

		var sheet = new win.CSSStyleSheet();
		sheet.replaceSync(`
			h4 {
				display: none !important
			}
			:host {
				flex-grow: 1 !important;
				display: flex !important;
				justify-content: end !important;
			}
			moz-button, moz-button::part(button) {
				display: flex !important;
				height: 100% !important;
				min-height: 100% !important;
			}
		`);
		sph.shadowRoot.adoptedStyleSheets.push(sheet);
	}

	var onDOMContentLoaded = e => {
		var doc = e.target;
		if (doc?.documentURI != url) return;
		var win = doc.ownerGlobal, ds = win.docShell;
		ds.name != "toolbox-panel-iframe-webconsole"
			&& onConsoleDOMContentLoaded(doc, win, ds);
	}
	var onConsoleDOMContentLoaded = async (doc, win, ds) => {
		try {Services.scriptloader.loadSubScript(
			"chrome://custombuttons/content/consoleOverlay.js", win
		)} catch {}

		var tabWin = !win.isChromeWindow;
		if (tabWin) {
			if (doc.visibilityState == "hidden") {
				var {focus} = win;
				win.focus = () => win.focus = focus;
			}
			win.browsingContext
				.currentWindowGlobal.windowGlobalChild
				.getActor("ContextMenu").handleEvent = noop;

			var br = ds.chromeEventHandler;
			var cmAttr = br.getAttribute("contextmenu");

			var gb = br.ownerGlobal.gBrowser;
			gb.setIcon(gb.getTabForBrowser(br), img);
		}
		else if (revamp) addCloseBtn(doc, win);

		var bc = await getConsole(win);
		if (tabWin) {
			if (cmAttr)
				bc.cmAttr = cmAttr,
				(bc.br = br).removeAttribute("contextmenu");
			bc.updateWindowTitle(win);
		}
	}


	var resolvers = [];
	var find = function(resolver) {
		return resolver.id == this;
	}
	var destroy = function() {
		this.resolve();
		resolvers.splice(resolvers.indexOf(this), 1);
	}
	var getConsole = async win => {
		var resolver = Promise.withResolvers();
		resolver.destroy = destroy;
		resolver.id = win.browsingContext.id;
		resolvers.unshift(resolver);
		resolvers.length > 1 && await resolvers[1].promise;

		var bc = await constructConsole(win);
		resolver.destroy();
		return bc;
	}
	var constructConsole = win => {
		var closer = {close: noop};
		var cc = class extends impl.bcm.constructor {
			constructor(win) {
				super();
				this.win = win;
			}
			openWindow() {
				var {win} = this;
				win.addEventListener("unload", this, {once: true});
				delete this.win;
				return win;
			}
			handleEvent(e) {
				resolvers.find(find, e.target.ownerGlobal.browsingContext.id)?.destroy();
				var bc = this.getBrowserConsole();
				bc.chromeWindow = closer;
				this.closeBrowserConsole();
				bc.br?.isConnected && bc.br.setAttribute("contextmenu", bc.cmAttr);
			}
		}
		return (constructConsole = win => new cc(win).toggleBrowserConsole())(win);
	}
})("chrome://devtools/content/webconsole/index.html");

Отредактировано Dumby (02-03-2025 19:15:11)

Отсутствует

 

№1724202-03-2025 20:35:25

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Великолепно! Благодарю.
А ты случайно не делал скрипт UserCSSLoader,
чето кнопка уменя не работает.

Отсутствует

 

№1724302-03-2025 21:55:38

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

Garalf пишет

А ты случайно не делал скрипт UserCSSLoader,
чето кнопка уменя не работает.

Нет. Я даже не знаю что это такое.
А не работает, скорее всего, из-за «on…» атрибутов
и/или C{u, hromeUtils}.import(). Это основной отвал на 136.

Отсутствует

 

№1724402-03-2025 22:12:51

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

"Ну и нашел еще одну кнопку, которая перестала работать в 136
Консоль браузера"

Прошу прощения, кнопка рабочая. Там был конфликт с одним раширением.

Отредактировано Garalf (02-03-2025 22:14:43)

Отсутствует

 

№1724503-03-2025 15:13:59

green25
Участник
 
Группа: Members
Зарегистрирован: 14-12-2024
Сообщений: 25
UA: unknown 0.0

Re: Custom Buttons

Dumby
toggleRestartlessAddons.js    В 115  не того,сдох ?
И Dom inspector  тоже...?

Отсутствует

 

№1724603-03-2025 23:58:41

Dumby
Участник
 
Группа: Members
Зарегистрирован: 12-08-2012
Сообщений: 2315
UA: Firefox 78.0

Re: Custom Buttons

green25 пишет

В 115

Думаешь я помню что там было в 115?

toggleRestartlessAddons.js    В 115  не того,сдох ?

Неизвестно.
Возможно где-то есть, надо бы поискать, но я не искун.

И Dom inspector  тоже...?

Хотелось бы думать, что подойдёт из этого поста.

Отсутствует

 

№1724704-03-2025 11:51:58

Farby
Участник
 
Группа: Members
Зарегистрирован: 21-11-2012
Сообщений: 331
UA: Google 2.1

Re: Custom Buttons

В 137 прикатил Bug 1946764, поэтому во всех CSS нужно заменить...

CSS 137+

Выделить код

Код:

// (-moz-bool-pref: "
-moz-pref("


Жизнь иногда такое выкидывает, что хочется подобрать...

Отсутствует

 

№1724804-03-2025 17:58:55

green25
Участник
 
Группа: Members
Зарегистрирован: 14-12-2024
Сообщений: 25
UA: unknown 0.0

Re: Custom Buttons

Может у кого кнопкой есть toggleRestartlessAddons ? Последняя.

Отсутствует

 

№1724904-03-2025 21:37:18

green25
Участник
 
Группа: Members
Зарегистрирован: 14-12-2024
Сообщений: 25
UA: Firefox 136.0

Re: Custom Buttons

Dumby
Это сдохло, а жаль..

скрытый текст

Выделить код

Код:

((id, g) => {
    addDestructor(r => r[5] == "e" && id in g && g[id].destroy());                                         
    if (g[id]) return;
    var topic = "quit-application-granted", {obs} = Services;
    obs.addObserver(g[id] = {
        // true - disable, false - enable
        states: {
            
            "jid1-s2tSKqH4h0BHUw@jetpack": false,
            "mozilla_cc3@internetdownloadmanager.com": false,
            "{74145f27-f039-47ce-a470-a662b129930a}": false,
            "{acf99872-d701-4863-adc2-cdda1163aa34}": true,
            "{8f4bbf79-5514-4d04-a901-d5fabfe91d73}": true, 
            
        },
        filter(addon) {
            var state = this.states[addon.id];
            if (
                state !== undefined && addon.userDisabled != state
                && addon.type.endsWith("extension")
                && addon.location.name != "app-builtin"
            ) {
                addon.active = addon.location.get(addon.id).enabled
                    = !(addon.userDisabled = state);
                return true;
            }
        },
        observe() {
            this.destroy();
            if (g.XPIDatabase.getAddons().filter(this.filter, this).length)
                g.XPIDatabase.saveChanges(), g.XPIStates.save();
        },
        destroy() {
            delete g[id];
            obs.removeObserver(this, topic);
        }
    }, topic, false);
})(
    "CBQuitApplicationExtensionsSwitcher",
    Cu.import("resource://gre/modules/addons/XPIDatabase.jsm", {})
);

Отредактировано green25 (05-03-2025 05:44:17)

Отсутствует

 

№1725005-03-2025 09:26:42

Garalf
Участник
 
Группа: Members
Зарегистрирован: 19-09-2017
Сообщений: 345
UA: Firefox 136.0

Re: Custom Buttons

green25

Кнопка CB toggleRestartlessAddons для 115

скрытый текст

Выделить код

Код:

// http://infocatcher.ucoz.net/js/cb/toggleRestartlessAddons.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57948
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Toggle_Restartless_Add-ons

// Toggle Restartless Add-ons button for Custom Buttons
// (code for "initialization" section)
// Also the code can be used from main window context (as Mouse Gestures code, for example)

// Also you can check for add-ons updates using right-click:
// copy all code from
// https://github.com/Infocatcher/Custom_Buttons/blob/master/Check_for_Addons_Updates/checkForAddonsUpdates.js
// after "//== Check for Addons Updates begin"

// See "var style = " to modify styles for specific add-ons

// (c) Infocatcher 2013-2019
// version 0.1.3pre4 - 2020-01-01

var options = {
	addonTypes: ["extension"],
	// Possible values: "extension", "plugin"
	// From extensions: "userstyle" (Stylish), "greasemonkey-user-script" (Greasemonkey), "userscript" (Scriptish)
	// (swap to reorder in the menu)
	showVersions: 1,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 0,
	// 0  - don't show hidden add-ons
	// -1 - show only enabled hidden add-ons (e.g. to track new items)
	// 1  - show all hidden add-ons
	sort: {
		enabled:     0,
		clickToPlay: 0,
		disabled:    1
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

var mp = document.createElementNS(xulns, "menupopup");
mp.setAttribute("onpopupshowing", "this.updateMenu();");
mp.setAttribute("oncommand", "this.handleEvent(event);");
mp.setAttribute("onmousedown", "if(event.button == 0) this.handleEvent(event);");
mp.setAttribute("onclick", "if(event.button > 0) this.handleEvent(event);");
mp.setAttribute("oncontextmenu", "return false;");
mp.setAttribute("onpopuphidden", "this.destroyMenu();");

var tb = this.parentNode;
if(tb && tb.getAttribute("orient") == "vertical") {
	// https://addons.mozilla.org/firefox/addon/vertical-toolbar/
	var isRight = tb.parentNode.getAttribute("placement") == "right";
	mp.setAttribute("position", isRight ? "start_before" : "end_before");
}

var cleanupTimer = 0;
mp.updateMenu = function() {
	clearTimeout(cleanupTimer);
	addStyle();
	getRestartlessAddons(options.addonTypes, function(addons) {
		var df = document.createDocumentFragment();
		var prevType;
		function sortPosition(addon) {
			if("STATE_ASK_TO_ACTIVATE" in AddonManager && addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE)
				return options.sort.clickToPlay;
			if(addon.isActive)
				return options.sort.enabled;
			return options.sort.disabled;
		}
		function key(addon) {
			return options.addonTypes.indexOf(addon.type)
				+ "\n" + sortPosition(addon)
				+ "\n" + addon.name.toLowerCase();
		}
		addons.sort(function(a, b) {
			var ka = key(a);
			var kb = key(b);
			return ka == kb ? 0 : ka < kb ? -1 : 1;
		}).forEach(function(addon) {
			var type = addon.type;
			if(prevType && type != prevType)
				df.appendChild(document.createElementNS(xulns, "menuseparator"));
			prevType = type;
			var icon = addon.iconURL || addon.icon64URL;
			var mi = document.createElementNS(xulns, "menuitem");
			mi.className = "menuitem-iconic";
			var label = addon.name;
			if(options.showVersions == 1)
				label += " " + addon.version;
			else if(options.showVersions == 2)
				mi.setAttribute("acceltext", addon.version);
			mi.setAttribute("label", label);
			mi.setAttribute("image", icon || mp.icons[type] || "");
			if(!icon && mp.icons.useSVG)
				mi.style.fill = "#15c";
			var tip = addon.description || "";
			var delay = "delayedStartupAddons" in Services
				&& Services.delayedStartupAddons[addon.id] || null;
			var isDelayed = delay !== null;
			mi.classList.toggle("toggleRestartlessAddons-isDelayed", isDelayed);
			if(isDelayed)
				tip = "[Delayed Startup: " + delay.toLocaleString() + "]" + (tip ? "\n" + tip : "");
			tip && mi.setAttribute("tooltiptext", tip);
			mi.classList.toggle("toggleRestartlessAddons-isHidden", addon.hidden || false);
			setDisabled(mi, addon.userDisabled);
			mi._cbAddon = addon;
			df.appendChild(mi);
		});
		mp.textContent = "";
		mp.appendChild(df);
	});
};
mp.handleEvent = function(e) {
	var mi = e.target;
	if(!("_cbAddon" in mi))
		return;
	var addon = mi._cbAddon;
	if(e.type == "mousedown") {
		var closeMenu = isAskToActivateAddon(addon)
			? options.closeMenuClickToPlay
			: options.closeMenu;
		if(e.shiftKey)
			closeMenu = !closeMenu;
		mi.setAttribute("closemenu", closeMenu ? "auto" : "none");
		return;
	}
	var hasMdf = hasModifier(e);
	if(e.type == "command" && (!hasMdf || e.shiftKey)) {
		let newDis = setNewDisabled(addon);
		setDisabled(mi, newDis);
	}
	else if(e.type == "command" && hasMdf || e.type == "click" && e.button == 1) {
		openAddonPage(addon);
		closeMenus(mi);
	}
	else if(e.type == "click" && e.button == 2) {
		if(openAddonOptions(addon))
			closeMenus(mi);
	}
};
mp.destroyMenu = function() {
	removeStyle();
	clearTimeout(cleanupTimer);
	cleanupTimer = setTimeout(function() {
		mp.textContent = "";
	}, 5000);
};
mp.icons = {
	get platformVersion() {
		delete this.platformVersion;
		return this.platformVersion = parseFloat(Services.appinfo.platformVersion);
	},
	get useSVG() {
		delete this.useSVG;
		return this.useSVG = Services.appinfo.name == "Firefox" && this.platformVersion >= 57;
	},
	get plugin() {
		delete this.plugin;
		return this.plugin = this.useSVG
			? this.platformVersion >= 65
				? "chrome://global/skin/plugins/pluginGeneric.svg"
				: "chrome://mozapps/skin/plugins/pluginGeneric.svg"
			: "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
	},
	get extension() {
		delete this.extension;
		return this.extension = this.useSVG
			? this.platformVersion >= 76
				? "chrome://mozapps/skin/extensions/extensionGeneric.svg" // Or chrome://mozapps/skin/extensions/extension.svg
				: "chrome://mozapps/skin/extensions/extensionGeneric-16.svg"
			: "chrome://mozapps/skin/extensions/extensionGeneric-16.png";
	}
};
function isAskToActivateAddon(addon) {
	return addon.type == "plugin"
		&& "STATE_ASK_TO_ACTIVATE" in AddonManager
		&& Services.prefs.getBoolPref("plugins.click_to_play", true);
}
function setNewDisabled(addon) {
	var newDis = getNewDisabled(addon);
	var oldDis = addon.userDisabled;
	try {
		addon.userDisabled = newDis;
	}
	catch(e) { // Error: Cannot disable hidden add-on firefox@getpocket.com
		_log("Can't set addon.userDisabled to " + newDis + ", error:\n" + e);
		if(addon.hidden)
			setNewDisabledRaw(addon, newDis);
	}
	var realDis = addon.userDisabled;
	if(realDis != newDis && addon.type == "extension") { // Firefox 62+? Weird things happens
		setNewDisabledRaw(addon, newDis);
		realDis = addon.userDisabled;
	}
	if(realDis != newDis) { // We can't enable vulnerable plugins
		let err = "Can't set addon.userDisabled to " + newDis + ", real value: " + realDis;
		if(newDis) {
			_log(err + "\nSTATE_ASK_TO_ACTIVATE not supported?");
			newDis = false;
		}
		else {
			_log(err + "\nVulnerable plugin?");
			if(oldDis == AddonManager.STATE_ASK_TO_ACTIVATE)
				newDis = true;
			else
				newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		}
		addon.userDisabled = newDis;
	}
	ensureSpecialDisabled(addon, newDis);
	return addon.userDisabled;
}
function getNewDisabled(addon) {
	// disabled -> STATE_ASK_TO_ACTIVATE -> enabled -> ...
	var curDis = addon.userDisabled;
	var newDis;
	if("STATE_ASK_TO_ACTIVATE" in AddonManager && curDis == AddonManager.STATE_ASK_TO_ACTIVATE)
		newDis = false;
	else if(!curDis)
		newDis = true;
	else {
		if(isAskToActivateAddon(addon))
			newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		else
			newDis = false;
	}
	return newDis;
}
function setNewDisabledRaw(addon, newDis) {
	_log("Let's try set addon.userDisabled using raw hack");
	let g = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	if("lazy" in g) g = g.lazy;

	if("XPIDatabase" in g && "updateAddonDisabledState" in g.XPIDatabase) { // Firefox 61+
		let rawAddon = g.XPIDatabase.getAddons().find(function(rawAddon) {
			return rawAddon.id == addon.id;
		});
		g.XPIDatabase.updateAddonDisabledState(
			rawAddon,
			g.XPIDatabase.updateAddonDisabledState.length == 1 // Firefox 74+
				? { userDisabled: newDis }
				: newDis
		);
	}
	else if("eval" in g) { // See "set userDisabled(val)"
		let addonFor = g.eval("addonFor");
		let rawAddon = addonFor(addon);
		//rawAddon.userDisabled = newDis;
		g.XPIProvider.updateAddonDisabledState(rawAddon, newDis);
	}
	else { // Firefox 57+? See https://forum.mozilla-russia.org/viewtopic.php?pid=745272#p745272
		updateAddonDisabledState(addon, newDis);
	}
}
function updateAddonDisabledState(addon, newDis) {
	var nsvo = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
	var key = "_cbToggleRestartlessAddonsData";
	var url = URL.createObjectURL(new Blob([
		"XPIProvider.updateAddonDisabledState(addonFor(this." + key + "[0]), this." + key + "[1]); delete this." + key + ";"
	]));
	addDestructor(function() {
		URL.revokeObjectURL(url);
	});
	(updateAddonDisabledState = function(addon, newDis) {
		nsvo[key] = [addon, newDis];
		Services.scriptloader.loadSubScript(url, nsvo);
	})(addon, newDis);
}
function setDisabled(mi, disabled) {
	var askToActivate = "STATE_ASK_TO_ACTIVATE" in AddonManager && disabled == AddonManager.STATE_ASK_TO_ACTIVATE;
	var cl = mi.classList;
	cl.toggle("toggleRestartlessAddons-askToActivate", askToActivate);
	cl.toggle("toggleRestartlessAddons-disabled", disabled && !askToActivate);
}
function ensureSpecialDisabled(addon, newDis) {
	if(addon.id == "screenshots@mozilla.org")
		Services.prefs.setBoolPref("extensions.screenshots.disabled", newDis);
}

if(
	this instanceof XULElement // Custom Buttons
	&& typeof event == "object"
	&& !("type" in event) && typeof _phase == "string" && _phase == "init" // Initialization
) {
	this.type = "menu";
	this.orient = "horizontal";
	this.appendChild(mp);

	this.onmouseover = function(e) {
		if(e.target != this)
			return;
		Array.prototype.some.call(
			this.parentNode.getElementsByTagName("*"),
			function(node) {
				if(
					node != this
					&& node.namespaceURI == xulns
					// See https://github.com/Infocatcher/Custom_Buttons/issues/28
					//&& node.boxObject
					//&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject
					&& "open" in node
					&& node.open
					&& node.getElementsByTagName("menupopup").length
				) {
					node.open = false;
					this.open = true;
					return true;
				}
				return false;
			},
			this
		);
	};
	this.onmousedown = function(e) {
		if(e.target == this && e.button == 0 && hasModifier(e))
			e.preventDefault();
	};
	this.oncontextmenu = function(e) {
		if(e.target == this && !hasModifier(e) && hasUpdater())
			e.preventDefault();
	};
	this.onclick = function(e) {
		if(e.target != this)
			return;
		if(e.button == 0 && hasModifier(e) || e.button == 1)
			openAddonsManager();
		else if(e.button == 2 && !hasModifier(e) && hasUpdater())
			checkForAddonsUpdates.call(this);
	};
}
else { // Mouse gestures or something other...
	let e;
	if(typeof event == "object" && event instanceof Event && "screenX" in event) // FireGestures
		e = event;
	else if(
		this instanceof Components.interfaces.nsIDOMChromeWindow
		&& "mgGestureState" in window && "endEvent" in mgGestureState // Mouse Gestures Redox
	)
		e = mgGestureState.endEvent;
	else {
		let anchor = this instanceof XULElement && this
			|| window.gBrowser && gBrowser.selectedBrowser
			|| document.documentElement;
		if("boxObject" in anchor) {
			let bo = anchor.boxObject;
			e = {
				screenX: bo.screenX,
				screenY: bo.screenY
			};
			if(this instanceof XULElement)
				e.screenY += bo.height;
		}
	}
	if(!e || !("screenX" in e))
		throw new Error("[Toggle Restartless Add-ons]: Can't get event object");
	document.documentElement.appendChild(mp);
	mp.addEventListener("popuphidden", function destroy(e) {
		mp.removeEventListener(e.type, destroy, false);
		setTimeout(function() {
			mp.destroyMenu();
			mp.parentNode.removeChild(mp);
		}, 0);
	}, false);
	mp.openPopupAtScreen(e.screenX, e.screenY);
}

function getRestartlessAddons(addonTypes, callback, context) {
	if(!("AddonManager" in window))
		Components.utils.import("resource://gre/modules/AddonManager.jsm");
	if(!("Services" in window))
		Components.utils.import("resource://gre/modules/Services.jsm");
	var then, promise = AddonManager.getAddonsByTypes(addonTypes, then = function(addons) {
		callback.call(context, addons.filter(function(addon) {
			var ops = addon.operationsRequiringRestart;
			return !addon.appDisabled
				&& !(ops & AddonManager.OP_NEEDS_RESTART_ENABLE || ops & AddonManager.OP_NEEDS_RESTART_DISABLE)
				&& (
					!addon.hidden
					|| options.showHidden > 0
					|| options.showHidden == -1 && !addon.userDisabled
				)
				&& (addon.iconURL || "").substr(0, 29) != "resource://search-extensions/";
		}));
	});
	promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+
}
function openAddonOptions(addon) {
	// Based on code from chrome://mozapps/content/extensions/extensions.js
	// Firefox 21.0a1 (2013-01-27)
	var optionsURL = addon.optionsURL;
	if(!addon.isActive || !optionsURL)
		return false;
	if(addon.type == "plugin") // No options for now!
		return false;
	if(
		addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_INFO || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_BROWSER || NaN)
	)
		openAddonPage(addon, true);
	else if(addon.optionsType == AddonManager.OPTIONS_TYPE_TAB && "switchToTabHavingURI" in window)
		switchToTabHavingURI(optionsURL, true);
	else {
		let windows = Services.wm.getEnumerator(null);
		while(windows.hasMoreElements()) {
			let win = windows.getNext();
			if(win.document.documentURI == optionsURL) {
				win.focus();
				return true;
			}
		}
		// Note: original code checks browser.preferences.instantApply and may open modal windows
		window.openDialog(optionsURL, "", "chrome,titlebar,toolbar,centerscreen,dialog=no");
	}
	return true;
}
function openAddonsManager(view) {
	var openAddonsMgr = window.BrowserOpenAddonsMgr // Firefox
		|| window.openAddonsMgr // Thunderbird
		|| window.toEM; // SeaMonkey
	openAddonsMgr(view);
}
function openAddonPage(addon, scrollToPreferences) {
	var platformVersion = parseFloat(
		Services.appinfo.name == "Pale Moon"
			? Services.appinfo.version
			: Services.appinfo.platformVersion
	);
	scrollToPreferences = scrollToPreferences && platformVersion >= 12
		? "/preferences"
		: "";
	openAddonsManager("addons://detail/" + encodeURIComponent(addon.id) + scrollToPreferences);
}

function hasModifier(e) {
	return e.ctrlKey || e.shiftKey || e.altKey || e.metaKey;
}

function addStyle() {
	if(addStyle.hasOwnProperty("_style"))
		return;
	var style = '\
		.toggleRestartlessAddons-isDelayed > .menu-iconic-text {\n\
			opacity: 0.75;\n\
			color: #070;\n\
		}\n\
		.toggleRestartlessAddons-isHidden > .menu-iconic-text {\n\
			color: #609;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-left {\n\
			opacity: 0.4;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-text,\n\
		.toggleRestartlessAddons-disabled > .menu-accel-container {\n\
			opacity: 0.5;\n\
		}\n\
		.toggleRestartlessAddons-askToActivate {\n\
			color: -moz-nativehyperlinktext;\n\
		}';
	addStyle._style = document.insertBefore(
		document.createProcessingInstruction(
			"xml-stylesheet",
			'href="' + "data:text/css,"
				+ encodeURIComponent(style) + '" type="text/css"'
		),
		document.documentElement
	);
}
function removeStyle() {
	if(!addStyle.hasOwnProperty("_style"))
		return;
	var s = addStyle._style;
	s.parentNode.removeChild(s);
	delete addStyle._style;
}
function closeMenus(node) {
	// Based on function closeMenus from chrome://browser/content/utilityOverlay.js
	for(; node && "tagName" in node; node = node.parentNode) {
		if(
			node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
			&& (node.localName == "menupopup" || node.localName == "popup")
		)
			node.hidePopup();
	}
}
function _log(s) {
	if(typeof LOG == "function") // Custom Buttons
		LOG(s);
	else // Or something else
		Services.console.logStringMessage("Toggle Restartless Add-ons: " + s);
}

function hasUpdater() {
	var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1;
	hasUpdater = function() {
		return has;
	};
	return has;
}
	
	
	
	
	
	
	
	
	
	function checkForAddonsUpdates() {
// http://infocatcher.ucoz.net/js/cb/checkForAddonsUpdates.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57958
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Check_for_Addons_Updates

// Check for Addons Updates button for Custom Buttons
// (code for "code" section)

// (c) Infocatcher 2012-2021
// version 0.1.6pre4 - 2021-03-28

// Button just open hidden tab with about:addons and trigger built-in "Check for Updates" function.
// And show tab, if found updates.

(function() {
var btn = this instanceof XULElement
	? this
	: { // Launched not from custom button
		image: "", // Base64-encoded icon (if empty, will be used "imgLoading")
		label: "Check for Addons Updates",
		tooltipText: ""
	};
if("_cb_disabled" in btn)
	return;
btn._cb_disabled = true;

if(!("Services" in window))
	Components.utils.import("resource://gre/modules/Services.jsm");
var app = Services.appinfo.name;
var pv = parseFloat(Services.appinfo.platformVersion);

var ADDONS_URL = "about:addons";

var progressIcon = new ProgressIcon(btn);
var image = btn.image || progressIcon.imgLoading;
var tip = btn.tooltipText;
btn.tooltipText = "Open " + ADDONS_URL + "…";

var tab, browser, gBrowser;
var tbTabInfo, tbTab;

var trgWindow = Services.wm.getMostRecentWindow("navigator:browser")
	|| app == "Thunderbird" && Services.wm.getMostRecentWindow("mail:3pane")
	|| window;
var trgDocument = trgWindow.document;
var tabmail = trgDocument.getElementById("tabmail");

if(tabmail && app == "Thunderbird") { // Note: SeaMonkey doesn't support content tabs in mail window
	let addonsWin;
	let receivePong = function(subject, topic, data) {
		addonsWin = subject;
	};
	Services.obs.addObserver(receivePong, "EM-pong", false);
	Services.obs.notifyObservers(null, "EM-ping", "");
	Services.obs.removeObserver(receivePong, "EM-pong");
	if(addonsWin) {
		let rootWindow = addonsWin
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIWebNavigation)
			.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
			.rootTreeItem
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIDOMWindow);
		tabmail = rootWindow.document.getElementById("tabmail");
		tbTabInfo = tabmail.getBrowserForDocument(addonsWin);
		tbTab = tab = tbTabInfo.tabNode;
		processAddonsTab(addonsWin);
	}
	else {
		Services.obs.addObserver(function observer(subject, topic, data) {
			Services.obs.removeObserver(observer, topic);
			if(subject.document.readyState == "complete")
				processAddonsTab(subject);
			else {
				subject.addEventListener("load", function onLoad(e) {
					subject.removeEventListener(e.type, onLoad, false);
					processAddonsTab(subject);
				}, false);
			}
		}, "EM-loaded", false);
		// See openAddonsMgr() -> openContentTab()
		tbTabInfo = tabmail.openTab("contentTab", {
			contentPage: ADDONS_URL,
			clickHandler: "specialTabs.siteClickHandler(event, /addons\.mozilla\.org/);",
			background: true
		});
		tbTab = tab = tbTabInfo.tabNode;
		tbTab.collapsed = true;
		// Note: dontSelectHiddenTab() not implemented
	}
}
else if("gBrowser" in trgWindow && trgWindow.gBrowser.tabs) {
	let isPending = false;
	let ws = Services.wm.getEnumerator("navigator:browser");
	windowsLoop:
	while(ws.hasMoreElements()) {
		let w = ws.getNext();
		let tabs = w.gBrowser.tabs;
		for(let i = 0, l = tabs.length; i < l; ++i) {
			let t = tabs[i];
			if(
				!t.closing
				&& t.linkedBrowser
				&& t.linkedBrowser.currentURI.spec == ADDONS_URL
			) {
				tab = t;
				break windowsLoop;
			}
		}
	}

	gBrowser = trgWindow.gBrowser;
	if(!tab) {
		tab = gBrowser.addTab(ADDONS_URL, {
			triggeringPrincipal: "Services" in window // Firefox 63+
				&& Services.scriptSecurityManager
				&& Services.scriptSecurityManager.getSystemPrincipal()
		});
		tab.collapsed = true;
		tab.closing = true; // See "visibleTabs" getter in chrome://browser/content/tabbrowser.xml
		trgWindow.addEventListener("TabSelect", dontSelectHiddenTab, false);
	}
	else if(
		tab.getAttribute("pending") == "true" // Gecko >= 9.0
		|| tab.linkedBrowser.contentDocument.readyState == "uninitialized"
		// || tab.linkedBrowser.__SS_restoreState == 1
	)
		isPending = true;

	browser = tab.linkedBrowser;
	if(
		isPending
		|| browser.webProgress.isLoadingDocument
		|| browser.currentURI.spec == "about:blank" // Firefox 79+
	) {
		browser.addEventListener("load", processAddonsTab, true);
		if(isPending) {
			if(pv >= 41) {
				// Workaround to correctly restore pending tab
				// See https://github.com/Infocatcher/Custom_Buttons/issues/39
				let selTab = gBrowser.selectedTab;
				gBrowser.selectedTab = tab;
				gBrowser.selectedTab = selTab;
			}
			else {
				browser.reload();
			}
		}
	}
	else {
		processAddonsTab();
	}
}
else {
	progressIcon.restore();
	btn.tooltipText = tip;
	delete btn._cb_disabled;
	Services.prompt.alert(window, btn.label, "Error: Can't find supported window!");
	return;
}

function processAddonsTab(e, again) {
	var doc;
	if(e && e instanceof Components.interfaces.nsIDOMWindow) {
		doc = e.document;
	}
	else if(e) {
		doc = e.target;
		if(doc.location != ADDONS_URL)
			return;
		browser.removeEventListener(e.type, processAddonsTab, true);
	}
	else {
		doc = browser.contentDocument;
	}

	btn.tooltipText = "Process " + ADDONS_URL + "…";
	progressIcon.loading();

	var origAttr = "_cb_checkForAddonsUpdates_origImage";
	if(!tab.hasAttribute(origAttr)) {
		var link = doc.querySelector('link[rel="shortcut icon"]'); // Not loaded yet?
		tab.setAttribute(origAttr, link && link.href || tab.image);
	}
	tab.image = image;

	var fu = $("cmd_findAllUpdates");
	if(!fu) { // Firefox 72+
		var win = doc.defaultView;
		var vb = doc.getElementById("html-view-browser");
		if(!vb) {
			if(!HTMLHtmlElement.isInstance(doc.documentElement)) { // Firefox 87+
				win.setTimeout(processAddonsTab, 20, win);
				return;
			}
			vb = browser;
		}
		if(!again) { // Strange errors happens
			// chrome://mozapps/content/extensions/aboutaddons.js
			// getTelemetryViewName() -> el.closest(...) is null
			win.setTimeout(processAddonsTab, 20, win, true);
			return;
		}
		var vbDoc = vb.contentDocument;
		fu = vbDoc.querySelector('[action="check-for-updates"]');
		var um = vbDoc.getElementById("updates-message");
	}

	var notFound = $("updates-noneFound") || {
		get hidden() { return um.getAttribute("state") != "none-found"; }
	};
	var updated = $("updates-installed") || {
		get hidden() { return um.getAttribute("state") != "installed"; }
	};
	// Avoid getting false results from the past update check (may not be required for "noneFound")
	if(um) { // Firefox 72+
		um.hidden = true;
		um.removeAttribute("state");
	}
	else {
		notFound.hidden = updated.hidden = true;
	}

	//fu.doCommand();
	fu.click();

	function localize(node, key, callback) {
		if(um) { // Firefox 72+
			doc.l10n.formatValue(key).then(function(s) {
				callback(s || key);
			}, Components.utils.reportError);
			return;
		}
		callback(node.getAttribute("value") || key);
	}

	var inProgress = $("updates-progress") || {
		get hidden() { return um.getAttribute("state") != "updating"; }
	};
	localize(inProgress, "addon-updates-updating", function(s) {
		btn.tooltipText = s;
	});

	var waitTimer = setInterval(function() {
		if(!doc.defaultView || doc.defaultView.closed) {
			stopWait();
			notify("Tab with add-ons manager was closed!");
			return;
		}
		if(!inProgress.hidden)
			return;
		var autoUpdate = $("utils-autoUpdateDefault")
			|| vbDoc.querySelector('[action="set-update-automatically"]');
		var autoUpdateChecked = autoUpdate.getAttribute("checked") == "true"
			|| autoUpdate.checked;

		var found = $("updates-manualUpdatesFound-btn") || {
			get hidden() { return um.getAttribute("state") != "manual-updates-found"; }
		};
		if(
			autoUpdateChecked
				? notFound.hidden && updated.hidden
				: notFound.hidden && found.hidden
		) // Too early?
			return;

		stopWait();
		if(!tbTab)
			tab.closing = false;
		function removeTab() {
			if(!tab.collapsed)
				return;
			if(tbTab) {
				tabmail.closeTab(tbTabInfo, true /*aNoUndo*/);
				return;
			}
			gBrowser.removeTab(tab);
			(function forgetClosedTab(isSecondTry) {
				var ss = "nsISessionStore" in Components.interfaces
					? (
						Components.classes["@mozilla.org/browser/sessionstore;1"]
						|| Components.classes["@mozilla.org/suite/sessionstore;1"]
					).getService(Components.interfaces.nsISessionStore)
					: trgWindow.SessionStore; // Firefox 61+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450559
				if(!("forgetClosedTab" in ss))
					return;
				var closedTabs = JSON.parse(ss.getClosedTabData(window));
				for(let i = 0, l = closedTabs.length; i < l; ++i) {
					let closedTab = closedTabs[i];
					let state = closedTab.state;
					if(state.entries[state.index - 1].url == ADDONS_URL) {
						ss.forgetClosedTab(window, i);
						return;
					}
				}
				if(!isSecondTry) // May be needed in SeaMonkey
					setTimeout(forgetClosedTab, 0, true);
			})();
		}

		if(!notFound.hidden) {
			removeTab();
			localize(notFound, "addon-updates-none-found", function(s) {
				notify(s);
			});
			return;
		}
		if(autoUpdateChecked) {
			removeTab();
			localize(updated, "addon-updates-installed", function(s) {
				notify(s);
			});
			return;
		}

		tab.collapsed = false;

		var cats = $("categories");
		var upds = $("category-availableUpdates");
		if(cats && upds) {
			if(vb && cats.selectedItem == upds) // Only for Firefox 72+
				cats.selectedItem = $("category-extension"); // Trick to force update
			cats.selectedItem = upds;
		}
		else { // Firefox 76+ ?
			vbDoc.querySelector('.category[name="available-updates"]').click();
		}

		var tabWin = tab.ownerDocument.defaultView;
		if(tbTab)
			tabmail.switchToTab(tbTabInfo);
		else
			tabWin.gBrowser.selectedTab = tab;
		setTimeout(function() {
			tabWin.focus();
			doc.defaultView.focus();
			var al = $("addon-list") || vb;
			al.focus();
		}, 0);
	}, 50);
	function $(id) {
		return doc.getElementById(id);
	}
	function stopWait() {
		clearInterval(waitTimer);
		progressIcon.restore();
		btn.tooltipText = tip;
		if(tab.image == image)
			tab.image = tab.getAttribute(origAttr);
		tab.removeAttribute(origAttr);
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() {
			delete btn._cb_disabled;
		}, 500);
	}
	function notify(msg) {
		Components.classes["@mozilla.org/alerts-service;1"]
			.getService(Components.interfaces.nsIAlertsService)
			.showAlertNotification(
				app == "Firefox" && pv >= 57
					? "chrome://mozapps/skin/extensions/extensionGeneric.svg"
					: "chrome://mozapps/skin/extensions/extensionGeneric.png",
				btn.label,
				msg, false, "", null
			);
	}
}
function dontSelectHiddenTab(e) {
	// <tab /><tab collapsed="true" />
	// Close first tab: collapsed tab becomes selected
	var trgTab = e.originalTarget || e.target;
	if(trgTab != tab)
		return;

	if(/\n(?:BrowserOpenAddonsMgr|toEM)@chrome:\/\//.test(new Error().stack)) {
		// User open Add-ons Manager, show tab
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() { // Hidden tab can't be selected, so select it manually...
			tab.collapsed = tab.closing = false;
			gBrowser.selectedTab = tab;
		}, 0);
	}

	function done(t) {
		if(!t.hidden && !t.closing) {
			e.preventDefault();
			e.stopPropagation();
			return gBrowser.selectedTab = t;
		}
		return false;
	}
	for(var t = tab.nextSibling; t; t = t.nextSibling)
		if(done(t))
			return;
	for(var t = tab.previousSibling; t; t = t.previousSibling)
		if(done(t))
			return;
}
function ProgressIcon(btn) {
	var app = Services.appinfo.name;
	var pv = parseFloat(Services.appinfo.platformVersion);
	if(app == "SeaMonkey")
		this.imgConnecting = this.imgLoading = "chrome://communicator/skin/icons/loading.gif";
	else if(app == "Thunderbird") {
		this.imgConnecting = "chrome://messenger/skin/icons/connecting.png";
		this.imgLoading = "chrome://messenger/skin/icons/loading.png";
	}
	else {
		this.imgConnecting = app == "Firefox" && pv >= 58
			? "chrome://browser/skin/tabbrowser/tab-connecting.png"
			: "chrome://browser/skin/tabbrowser/connecting.png";
		this.imgLoading = app == "Firefox" && pv >= 48
			? "chrome://global/skin/icons/loading.png"
			: "chrome://browser/skin/tabbrowser/loading.png";
	}
	if(!(btn instanceof XULElement)) {
		this.loading = this.restore = function() {};
		return;
	}
	var useAnimation = app == "Firefox" && pv >= 32 && pv < 48;
	var btnIcon = btn.icon
		|| btn.ownerDocument.getAnonymousElementByAttribute(btn, "class", "toolbarbutton-icon");
	var origIcon = btnIcon.src;
	btnIcon.src = this.imgConnecting;
	if(useAnimation) {
		let cs = btnIcon.ownerDocument.defaultView.getComputedStyle(btnIcon, null);
		let s = btnIcon.style;
		s.margin = [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft].join(" ");
		s.padding = [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].join(" ");
		s.width = cs.width;
		s.height = cs.height;
		s.boxShadow = "none";
		s.borderColor = s.background = "transparent";
		btnIcon.setAttribute("fadein", "true");
		btnIcon.setAttribute("busy", "true");
		btnIcon.classList.add("tab-throbber");
		btnIcon._restore = function() {
			delete btnIcon._restore;
			btnIcon.removeAttribute("busy");
			btnIcon.removeAttribute("progress");
			setTimeout(function() {
				btnIcon.classList.remove("tab-throbber");
				btnIcon.removeAttribute("style");
				btnIcon.removeAttribute("fadein");
			}, 0);
		};
	}
	this.loading = function() {
		btnIcon.src = this.imgLoading;
		if(useAnimation)
			btnIcon.setAttribute("progress", "true");
	};
	this.restore = function() {
		btnIcon.src = origIcon;
		if(useAnimation)
			btnIcon._restore();
	};
}
}).call(this);
//== Check for Addons Updates end
}


this.tooltipText = "Переключатель джетпаков" 
                   + "\n\nУправление:\nЛКМ – открыть меню" 
                   + "\nПКМ – проверить обновления"
                   + "\nСКМ – открыть страницу дополнений"
                   + "\nShift+ПКМ – меню кнопки"
                   + "\n\nВ меню: \nЛКМ – включить/выключить дополнение"
                   + "\nShift+ЛКМ – включить/выключить дополнение без закрытия меню"   
                   + "\nСКМ – открыть страницу дополнения в управлении дополнениями"                    
                   + "\nПКМ – открыть настройки дополнения (если есть)";

Кнопка CB для 136

скрытый текст

Выделить код

Код:

// http://infocatcher.ucoz.net/js/cb/toggleRestartlessAddons.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57948
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Toggle_Restartless_Add-ons

// Toggle Restartless Add-ons button for Custom Buttons
// (code for "initialization" section)
// Also the code can be used from main window context (as Mouse Gestures code, for example)

// Also you can check for add-ons updates using right-click:
// copy all code from
// https://github.com/Infocatcher/Custom_Buttons/blob/master/Check_for_Addons_Updates/checkForAddonsUpdates.js
// after "//== Check for Addons Updates begin"

// See "var style = " to modify styles for specific add-ons

// (c) Infocatcher 2013-2019
// version 0.1.3pre4 - 2020-01-01

var options = {
	addonTypes: ["extension", "plugin"],
	// Possible values: "extension", "plugin"
	// From extensions: "userstyle" (Stylish), "greasemonkey-user-script" (Greasemonkey), "userscript" (Scriptish)
	// (swap to reorder in the menu)
	showVersions: 1,
	// 0 - don't show versions
	// 1 - show after name: "Addon Name 1.2"
	// 2 - show as "acceltext" (in place for hotkey text)
	showHidden: 0,
	// 0  - don't show hidden add-ons
	// -1 - show only enabled hidden add-ons (e.g. to track new items)
	// 1  - show all hidden add-ons
	sort: {
		enabled:     0,
		clickToPlay: 0,
		disabled:    1
		// Sort order:
		// 0, 0, 0 - sort add-ons of each type alphabetically
		// 0, 0, 1 - show enabled add-ons (of each type) first
		// 0, 1, 2 - enabled add-ons, then click-to-play and then disabled
	},
	closeMenu: false, // Close menu after left-click
	closeMenuClickToPlay: false // Close menu after left-click, for click to play plugins
	// Use Shift+click to invert closeMenu* behavior
};

var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

var mp = document.createElementNS(xulns, "menupopup");
addEventListener("popupshowing", () => mp.updateMenu(), false, mp);
addEventListener("command", mp.onmousedown = mp.onclick = e => {
	if (!e.button || e.type.endsWith("k")) mp.handleEvent(e);
}, false, mp);
mp.toggleAttribute("context");
addEventListener("popuphidden", () => mp.destroyMenu(), false, mp);
var tb = this.parentNode;
if(tb && tb.getAttribute("orient") == "vertical") {
	// https://addons.mozilla.org/firefox/addon/vertical-toolbar/
	var isRight = tb.parentNode.getAttribute("placement") == "right";
	mp.setAttribute("position", isRight ? "start_before" : "end_before");
}

var cleanupTimer = 0;
mp.updateMenu = function() {
	clearTimeout(cleanupTimer);
	addStyle();
	getRestartlessAddons(options.addonTypes, function(addons) {
		var df = document.createDocumentFragment();
		var prevType;
		function sortPosition(addon) {
			if("STATE_ASK_TO_ACTIVATE" in AddonManager && addon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE)
				return options.sort.clickToPlay;
			if(addon.isActive)
				return options.sort.enabled;
			return options.sort.disabled;
		}
		function key(addon) {
			return options.addonTypes.indexOf(addon.type)
				+ "\n" + sortPosition(addon)
				+ "\n" + addon.name.toLowerCase();
		}
		addons.sort(function(a, b) {
			var ka = key(a);
			var kb = key(b);
			return ka == kb ? 0 : ka < kb ? -1 : 1;
		}).forEach(function(addon) {
			var type = addon.type;
			if(prevType && type != prevType)
				df.appendChild(document.createElementNS(xulns, "menuseparator"));
			prevType = type;
			var icon = addon.iconURL || addon.icon64URL;
			var mi = document.createElementNS(xulns, "menuitem");
			mi.className = "menuitem-iconic";
			var label = addon.name;
			if(options.showVersions == 1)
				label += " " + addon.version;
			else if(options.showVersions == 2)
				mi.setAttribute("acceltext", addon.version);
			mi.setAttribute("label", label);
			mi.setAttribute("image", icon || mp.icons[type] || "");
			if(!icon && mp.icons.useSVG)
				mi.style.fill = "#15c";
			var tip = addon.description || "";
			var delay = "delayedStartupAddons" in Services
				&& Services.delayedStartupAddons[addon.id] || null;
			var isDelayed = delay !== null;
			mi.classList.toggle("toggleRestartlessAddons-isDelayed", isDelayed);
			if(isDelayed)
				tip = "[Delayed Startup: " + delay.toLocaleString() + "]" + (tip ? "\n" + tip : "");
			tip && mi.setAttribute("tooltiptext", tip);
			mi.classList.toggle("toggleRestartlessAddons-isHidden", addon.hidden || false);
			setDisabled(mi, addon.userDisabled);
			mi._cbAddon = addon;
			df.appendChild(mi);
		});
		mp.textContent = "";
		mp.appendChild(df);
	});
};
mp.handleEvent = function(e) {
	var mi = e.target;
	if(!("_cbAddon" in mi))
		return;
	var addon = mi._cbAddon;
	if(e.type == "mousedown") {
		var closeMenu = isAskToActivateAddon(addon)
			? options.closeMenuClickToPlay
			: options.closeMenu;
		if(e.shiftKey)
			closeMenu = !closeMenu;
		mi.setAttribute("closemenu", closeMenu ? "auto" : "none");
		return;
	}
	var hasMdf = hasModifier(e);
	if(e.type == "command" && (!hasMdf || e.shiftKey)) {
		let newDis = setNewDisabled(addon);
		setDisabled(mi, newDis);
	}
	else if(e.type == "command" && hasMdf || e.type == "click" && e.button == 1) {
		openAddonPage(addon);
		closeMenus(mi);
	}
	else if(e.type == "click" && e.button == 2) {
		if(openAddonOptions(addon))
			closeMenus(mi);
	}
};
mp.destroyMenu = function() {
	removeStyle();
	clearTimeout(cleanupTimer);
	cleanupTimer = setTimeout(function() {
		mp.textContent = "";
	}, 5000);
};
mp.icons = {
	get platformVersion() {
		delete this.platformVersion;
		return this.platformVersion = parseFloat(Services.appinfo.platformVersion);
	},
	get useSVG() {
		delete this.useSVG;
		return this.useSVG = Services.appinfo.name == "Firefox" && this.platformVersion >= 57;
	},
	get plugin() {
		delete this.plugin;
		return this.plugin = this.useSVG
			? this.platformVersion >= 65
				? "chrome://global/skin/plugins/pluginGeneric.svg"
				: "chrome://mozapps/skin/plugins/pluginGeneric.svg"
			: "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
	},
	get extension() {
		delete this.extension;
		return this.extension = this.useSVG
			? this.platformVersion >= 76
				? "chrome://mozapps/skin/extensions/extensionGeneric.svg" // Or chrome://mozapps/skin/extensions/extension.svg
				: "chrome://mozapps/skin/extensions/extensionGeneric-16.svg"
			: "chrome://mozapps/skin/extensions/extensionGeneric-16.png";
	}
};
function isAskToActivateAddon(addon) {
	return addon.type == "plugin"
		&& "STATE_ASK_TO_ACTIVATE" in AddonManager
		&& Services.prefs.getBoolPref("plugins.click_to_play", true);
}
function setNewDisabled(addon) {
	var newDis = getNewDisabled(addon);
	var oldDis = addon.userDisabled;
	if(Components.interfaces.nsIWebTransportHash) { // random, Fx 123+
		var func = function() {
			func = false;
		}
		var thread = Services.tm.currentThread;
		var meth = newDis ? "disable" : "enable";
		addon[meth]({allowSystemAddons: true}).finally(func);
		while(func) thread.processNextEvent(true);
	}
	else try {
		addon.userDisabled = newDis;
	}
	catch(e) { // Error: Cannot disable hidden add-on firefox@getpocket.com
		_log("Can't set addon.userDisabled to " + newDis + ", error:\n" + e);
		if(addon.hidden)
			setNewDisabledRaw(addon, newDis);
	}
	var realDis = addon.userDisabled;
	if(realDis != newDis && addon.type == "extension") { // Firefox 62+? Weird things happens
		setNewDisabledRaw(addon, newDis);
		realDis = addon.userDisabled;
	}
	if(realDis != newDis) { // We can't enable vulnerable plugins
		let err = "Can't set addon.userDisabled to " + newDis + ", real value: " + realDis;
		if(newDis) {
			_log(err + "\nSTATE_ASK_TO_ACTIVATE not supported?");
			newDis = false;
		}
		else {
			_log(err + "\nVulnerable plugin?");
			if(oldDis == AddonManager.STATE_ASK_TO_ACTIVATE)
				newDis = true;
			else
				newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		}
		addon.userDisabled = newDis;
	}
	ensureSpecialDisabled(addon, newDis);
	return addon.userDisabled;
}
function getNewDisabled(addon) {
	// disabled -> STATE_ASK_TO_ACTIVATE -> enabled -> ...
	var curDis = addon.userDisabled;
	var newDis;
	if("STATE_ASK_TO_ACTIVATE" in AddonManager && curDis == AddonManager.STATE_ASK_TO_ACTIVATE)
		newDis = false;
	else if(!curDis)
		newDis = true;
	else {
		if(isAskToActivateAddon(addon))
			newDis = AddonManager.STATE_ASK_TO_ACTIVATE;
		else
			newDis = false;
	}
	return newDis;
}
function setNewDisabledRaw(addon, newDis) {
	_log("Let's try set addon.userDisabled using raw hack");
	if("lazy" in g) g = g.lazy;
	if("XPIDatabase" in g && "updateAddonDisabledState" in g.XPIDatabase) { // Firefox 61+
		let rawAddon = g.XPIDatabase.getAddons().find(function(rawAddon) {
			return rawAddon.id == addon.id;
		});
		g.XPIDatabase.updateAddonDisabledState(
			rawAddon,
			g.XPIDatabase.updateAddonDisabledState.length == 1 // Firefox 74+
				? { userDisabled: newDis }
				: newDis
		);
	}
	else if("eval" in g) { // See "set userDisabled(val)"
		let addonFor = g.eval("addonFor");
		let rawAddon = addonFor(addon);
		//rawAddon.userDisabled = newDis;
		g.XPIProvider.updateAddonDisabledState(rawAddon, newDis);
	}
	else { // Firefox 57+? See https://forum.mozilla-russia.org/viewtopic.php?pid=745272#p745272
		updateAddonDisabledState(addon, newDis);
	}
}
function updateAddonDisabledState(addon, newDis) {
	var key = "_cbToggleRestartlessAddonsData";
	var url = URL.createObjectURL(new Blob([
		"XPIProvider.updateAddonDisabledState(addonFor(this." + key + "[0]), this." + key + "[1]); delete this." + key + ";"
	]));
	addDestructor(function() {
		URL.revokeObjectURL(url);
	});
	(updateAddonDisabledState = function(addon, newDis) {
		nsvo[key] = [addon, newDis];
		Services.scriptloader.loadSubScript(url, nsvo);
	})(addon, newDis);
}
function setDisabled(mi, disabled) {
	var askToActivate = "STATE_ASK_TO_ACTIVATE" in AddonManager && disabled == AddonManager.STATE_ASK_TO_ACTIVATE;
	var cl = mi.classList;
	cl.toggle("toggleRestartlessAddons-askToActivate", askToActivate);
	cl.toggle("toggleRestartlessAddons-disabled", disabled && !askToActivate);
}
function ensureSpecialDisabled(addon, newDis) {
	if(addon.id == "screenshots@mozilla.org")
		Services.prefs.setBoolPref("extensions.screenshots.disabled", newDis);
}

if(
	this instanceof XULElement // Custom Buttons
	&& typeof event == "object"
	&& !("type" in event) && typeof _phase == "string" && _phase == "init" // Initialization
) {
	this.type = "menu";
	this.orient = "horizontal";
	this.appendChild(mp);

	this.onmouseover = function(e) {
		if(e.target != this)
			return;
		Array.prototype.some.call(
			this.parentNode.getElementsByTagName("*"),
			function(node) {
				if(
					node != this
					&& node.namespaceURI == xulns
					// See https://github.com/Infocatcher/Custom_Buttons/issues/28
					//&& node.boxObject
					//&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject
					&& "open" in node
					&& node.open
					&& node.getElementsByTagName("menupopup").length
				) {
					node.open = false;
					this.open = true;
					return true;
				}
				return false;
			},
			this
		);
	};
	this.onmousedown = function(e) {
		if(e.target == this && e.button == 0 && hasModifier(e))
			e.preventDefault();
	};
	this.oncontextmenu = function(e) {
		if(e.target == this && !hasModifier(e) && hasUpdater())
			e.preventDefault();
	};
	this.onclick = function(e) {
		if(e.target != this)
			return;
		if(e.button == 0 && hasModifier(e) || e.button == 1)
			openAddonsManager();
		else if(e.button == 2 && !hasModifier(e) && hasUpdater())
			checkForAddonsUpdates.call(this);
	};
}
else { // Mouse gestures or something other...
	let e;
	if(typeof event == "object" && event instanceof Event && "screenX" in event) // FireGestures
		e = event;
	else if(
		this instanceof Components.interfaces.nsIDOMChromeWindow
		&& "mgGestureState" in window && "endEvent" in mgGestureState // Mouse Gestures Redox
	)
		e = mgGestureState.endEvent;
	else {
		let anchor = this instanceof XULElement && this
			|| window.gBrowser && gBrowser.selectedBrowser
			|| document.documentElement;
		if("boxObject" in anchor) {
			let bo = anchor.boxObject;
			e = {
				screenX: bo.screenX,
				screenY: bo.screenY
			};
			if(this instanceof XULElement)
				e.screenY += bo.height;
		}
	}
	if(!e || !("screenX" in e))
		throw new Error("[Toggle Restartless Add-ons]: Can't get event object");
	document.documentElement.appendChild(mp);
	mp.addEventListener("popuphidden", function destroy(e) {
		mp.removeEventListener(e.type, destroy, false);
		setTimeout(function() {
			mp.destroyMenu();
			mp.parentNode.removeChild(mp);
		}, 0);
	}, false);
	mp.openPopupAtScreen(e.screenX, e.screenY);
}

function getRestartlessAddons(addonTypes, callback, context) {
	if(!("AddonManager" in window));
	var then, promise = AddonManager.getAddonsByTypes(addonTypes, then = function(addons) {
		callback.call(context, addons.filter(function(addon) {
			var ops = addon.operationsRequiringRestart;
			return !addon.appDisabled
				&& !(ops & AddonManager.OP_NEEDS_RESTART_ENABLE || ops & AddonManager.OP_NEEDS_RESTART_DISABLE)
				&& (
					!addon.hidden
					|| options.showHidden > 0
					|| options.showHidden == -1 && !addon.userDisabled
				)
				&& (addon.iconURL || "").substr(0, 29) != "resource://search-extensions/";
		}));
	});
	promise && typeof promise.then == "function" && promise.then(then, Components.utils.reportError); // Firefox 61+
}
function openAddonOptions(addon) {
	// Based on code from chrome://mozapps/content/extensions/extensions.js
	// Firefox 21.0a1 (2013-01-27)
	var optionsURL = addon.optionsURL;
	if(!addon.isActive || !optionsURL)
		return false;
	if(addon.type == "plugin") // No options for now!
		return false;
	if(
		addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_INFO || NaN)
		|| addon.optionsType == (AddonManager.OPTIONS_TYPE_INLINE_BROWSER || NaN)
	)
		openAddonPage(addon, true);
	else if(addon.optionsType == AddonManager.OPTIONS_TYPE_TAB && "switchToTabHavingURI" in window)
		switchToTabHavingURI(optionsURL, true);
	else {
		let windows = Services.wm.getEnumerator(null);
		while(windows.hasMoreElements()) {
			let win = windows.getNext();
			if(win.document.documentURI == optionsURL) {
				win.focus();
				return true;
			}
		}
		// Note: original code checks browser.preferences.instantApply and may open modal windows
		window.openDialog(optionsURL, "", "chrome,titlebar,toolbar,centerscreen,dialog=no");
	}
	return true;
}
function openAddonsManager(view) {
	var openAddonsMgr = window.BrowserOpenAddonsMgr || window.BrowserAddonUI.openAddonsMgr // Firefox
		|| window.openAddonsMgr // Thunderbird
		|| window.toEM; // SeaMonkey
	openAddonsMgr(view);
}
function openAddonPage(addon, scrollToPreferences) {
	var platformVersion = parseFloat(
		Services.appinfo.name == "Pale Moon"
			? Services.appinfo.version
			: Services.appinfo.platformVersion
	);
	scrollToPreferences = scrollToPreferences && platformVersion >= 12
		? "/preferences"
		: "";
	openAddonsManager("addons://detail/" + encodeURIComponent(addon.id) + scrollToPreferences);
}

function hasModifier(e) {
	return e.ctrlKey || e.shiftKey || e.altKey || e.metaKey;
}

function addStyle() {
	if(addStyle.hasOwnProperty("_style"))
		return;
	var style = '\
		.toggleRestartlessAddons-isDelayed > .menu-iconic-text {\n\
			opacity: 0.75;\n\
			color: #070;\n\
		}\n\
		.toggleRestartlessAddons-isHidden > .menu-iconic-text {\n\
			color: #609;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-left {\n\
			opacity: 0.4;\n\
		}\n\
		.toggleRestartlessAddons-disabled > .menu-iconic-text,\n\
		.toggleRestartlessAddons-disabled > .menu-accel-container {\n\
			opacity: 0.5;\n\
		}\n\
		.toggleRestartlessAddons-askToActivate {\n\
			color: -moz-nativehyperlinktext;\n\
		}';
	addStyle._style = document.insertBefore(
		document.createProcessingInstruction(
			"xml-stylesheet",
			'href="' + "data:text/css,"
				+ encodeURIComponent(style) + '" type="text/css"'
		),
		document.documentElement
	);
}
function removeStyle() {
	if(!addStyle.hasOwnProperty("_style"))
		return;
	var s = addStyle._style;
	s.parentNode.removeChild(s);
	delete addStyle._style;
}
function closeMenus(node) {
	// Based on function closeMenus from chrome://browser/content/utilityOverlay.js
	for(; node && "tagName" in node; node = node.parentNode) {
		if(
			node.namespaceURI == "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
			&& (node.localName == "menupopup" || node.localName == "popup")
		)
			node.hidePopup();
	}
}
function _log(s) {
	if(typeof LOG == "function") // Custom Buttons
		LOG(s);
	else // Or something else
		Services.console.logStringMessage("Toggle Restartless Add-ons: " + s);
}

function hasUpdater() {
	var has = checkForAddonsUpdates.toString().indexOf("about:addons") != -1;
	hasUpdater = function() {
		return has;
	};
	return has;
}
function checkForAddonsUpdates() {
//== Check for Addons Updates begin
// http://infocatcher.ucoz.net/js/cb/checkForAddonsUpdates.js
// https://forum.mozilla-russia.org/viewtopic.php?id=57958
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Check_for_Addons_Updates

// Check for Addons Updates button for Custom Buttons
// (code for "code" section)

// (c) Infocatcher 2012-2021
// version 0.1.6pre4 - 2021-03-28

// Button just open hidden tab with about:addons and trigger built-in "Check for Updates" function.
// And show tab, if found updates.

(function() {
var btn = this instanceof XULElement
	? this
	: { // Launched not from custom button
		image: "", // Base64-encoded icon (if empty, will be used "imgLoading")
		label: "Check for Addons Updates",
		tooltipText: ""
	};
if("_cb_disabled" in btn)
	return;
btn._cb_disabled = true;


var app = Services.appinfo.name;
var pv = parseFloat(Services.appinfo.platformVersion);

var ADDONS_URL = "about:addons";

var progressIcon = new ProgressIcon(btn);
var image = btn.image || progressIcon.imgLoading;
var tip = btn.tooltipText;
btn.tooltipText = "Open " + ADDONS_URL + "…";

var tab, browser, gBrowser;
var tbTabInfo, tbTab;

var trgWindow = Services.wm.getMostRecentWindow("navigator:browser")
	|| app == "Thunderbird" && Services.wm.getMostRecentWindow("mail:3pane")
	|| window;
var trgDocument = trgWindow.document;
var tabmail = trgDocument.getElementById("tabmail");

if(tabmail && app == "Thunderbird") { // Note: SeaMonkey doesn't support content tabs in mail window
	let addonsWin;
	let receivePong = function(subject, topic, data) {
		addonsWin = subject;
	};
	Services.obs.addObserver(receivePong, "EM-pong", false);
	Services.obs.notifyObservers(null, "EM-ping", "");
	Services.obs.removeObserver(receivePong, "EM-pong");
	if(addonsWin) {
		let rootWindow = addonsWin
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIWebNavigation)
			.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
			.rootTreeItem
			.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
			.getInterface(Components.interfaces.nsIDOMWindow);
		tabmail = rootWindow.document.getElementById("tabmail");
		tbTabInfo = tabmail.getBrowserForDocument(addonsWin);
		tbTab = tab = tbTabInfo.tabNode;
		processAddonsTab(addonsWin);
	}
	else {
		Services.obs.addObserver(function observer(subject, topic, data) {
			Services.obs.removeObserver(observer, topic);
			if(subject.document.readyState == "complete")
				processAddonsTab(subject);
			else {
				subject.addEventListener("load", function onLoad(e) {
					subject.removeEventListener(e.type, onLoad, false);
					processAddonsTab(subject);
				}, false);
			}
		}, "EM-loaded", false);
		// See openAddonsMgr() -> openContentTab()
		tbTabInfo = tabmail.openTab("contentTab", {
			contentPage: ADDONS_URL,
			clickHandler: "specialTabs.siteClickHandler(event, /addons\.mozilla\.org/);",
			background: true
		});
		tbTab = tab = tbTabInfo.tabNode;
		tbTab.collapsed = true;
		// Note: dontSelectHiddenTab() not implemented
	}
}
else if("gBrowser" in trgWindow && trgWindow.gBrowser.tabs) {
	let isPending = false;
	let ws = Services.wm.getEnumerator("navigator:browser");
	windowsLoop:
	while(ws.hasMoreElements()) {
		let w = ws.getNext();
		let tabs = w.gBrowser.tabs;
		for(let i = 0, l = tabs.length; i < l; ++i) {
			let t = tabs[i];
			if(
				!t.closing
				&& t.linkedBrowser
				&& t.linkedBrowser.currentURI.spec == ADDONS_URL
			) {
				tab = t;
				break windowsLoop;
			}
		}
	}

	gBrowser = trgWindow.gBrowser;
	if(!tab) {
		tab = gBrowser.addTab(ADDONS_URL, {
			triggeringPrincipal: "Services" in window // Firefox 63+
				&& Services.scriptSecurityManager
				&& Services.scriptSecurityManager.getSystemPrincipal()
		});
		tab.collapsed = true;
		tab.closing = true; // See "visibleTabs" getter in chrome://browser/content/tabbrowser.xml
		trgWindow.addEventListener("TabSelect", dontSelectHiddenTab, false);
	}
	else if(
		tab.getAttribute("pending") == "true" // Gecko >= 9.0
		|| tab.linkedBrowser.contentDocument.readyState == "uninitialized"
		// || tab.linkedBrowser.__SS_restoreState == 1
	)
		isPending = true;

	browser = tab.linkedBrowser;
	if(
		isPending
		|| browser.webProgress.isLoadingDocument
		|| browser.currentURI.spec == "about:blank" // Firefox 79+
	) {
		browser.addEventListener("load", processAddonsTab, true);
		if(isPending) {
			if(pv >= 41) {
				// Workaround to correctly restore pending tab
				// See https://github.com/Infocatcher/Custom_Buttons/issues/39
				let selTab = gBrowser.selectedTab;
				gBrowser.selectedTab = tab;
				gBrowser.selectedTab = selTab;
			}
			else {
				browser.reload();
			}
		}
	}
	else {
		processAddonsTab();
	}
}
else {
	progressIcon.restore();
	btn.tooltipText = tip;
	delete btn._cb_disabled;
	Services.prompt.alert(window, btn.label, "Error: Can't find supported window!");
	return;
}

function processAddonsTab(e, again) {
	var doc;
	if(e && e instanceof Components.interfaces.nsIDOMWindow) {
		doc = e.document;
	}
	else if(e) {
		doc = e.target;
		if(doc.location != ADDONS_URL)
			return;
		browser.removeEventListener(e.type, processAddonsTab, true);
	}
	else {
		doc = browser.contentDocument;
	}

	btn.tooltipText = "Process " + ADDONS_URL + "…";
	progressIcon.loading();

	var origAttr = "_cb_checkForAddonsUpdates_origImage";
	if(!tab.hasAttribute(origAttr)) {
		var link = doc.querySelector('link[rel="shortcut icon"]'); // Not loaded yet?
		tab.setAttribute(origAttr, link && link.href || tab.image);
	}
	tab.image = image;

	var fu = $("cmd_findAllUpdates");
	if(!fu) { // Firefox 72+
		var win = doc.defaultView;
		var vb = doc.getElementById("html-view-browser");
		if(!vb) {
			if(!HTMLHtmlElement.isInstance(doc.documentElement)) { // Firefox 87+
				win.setTimeout(processAddonsTab, 20, win);
				return;
			}
			vb = browser;
		}
		if(!again) { // Strange errors happens
			// chrome://mozapps/content/extensions/aboutaddons.js
			// getTelemetryViewName() -> el.closest(...) is null
			win.setTimeout(processAddonsTab, 20, win, true);
			return;
		}
		var vbDoc = vb.contentDocument;
		fu = vbDoc.querySelector('[action="check-for-updates"]');
		var um = vbDoc.getElementById("updates-message");
	}

	var notFound = $("updates-noneFound") || {
		get hidden() { return um.getAttribute("state") != "none-found"; }
	};
	var updated = $("updates-installed") || {
		get hidden() { return um.getAttribute("state") != "installed"; }
	};
	// Avoid getting false results from the past update check (may not be required for "noneFound")
	if(um) { // Firefox 72+
		um.hidden = true;
		um.removeAttribute("state");
	}
	else {
		notFound.hidden = updated.hidden = true;
	}

	//fu.doCommand();
	fu.click();

	function localize(node, key, callback) {
		if(um) { // Firefox 72+
			doc.l10n.formatValue(key).then(function(s) {
				callback(s || key);
			}, Components.utils.reportError);
			return;
		}
		callback(node.getAttribute("value") || key);
	}

	var inProgress = $("updates-progress") || {
		get hidden() { return um.getAttribute("state") != "updating"; }
	};
	localize(inProgress, "addon-updates-updating", function(s) {
		btn.tooltipText = s;
	});

	var waitTimer = setInterval(function() {
		if(!doc.defaultView || doc.defaultView.closed) {
			stopWait();
			notify("Tab with add-ons manager was closed!");
			return;
		}
		if(!inProgress.hidden)
			return;
		var autoUpdate = $("utils-autoUpdateDefault")
			|| vbDoc.querySelector('[action="set-update-automatically"]');
		var autoUpdateChecked = autoUpdate.getAttribute("checked") == "true"
			|| autoUpdate.checked;

		var found = $("updates-manualUpdatesFound-btn") || {
			get hidden() { return um.getAttribute("state") != "manual-updates-found"; }
		};
		if(
			autoUpdateChecked
				? notFound.hidden && updated.hidden
				: notFound.hidden && found.hidden
		) // Too early?
			return;

		stopWait();
		if(!tbTab)
			tab.closing = false;
		function removeTab() {
			if(!tab.collapsed)
				return;
			if(tbTab) {
				tabmail.closeTab(tbTabInfo, true /*aNoUndo*/);
				return;
			}
			gBrowser.removeTab(tab);
			(function forgetClosedTab(isSecondTry) {
				var ss = "nsISessionStore" in Components.interfaces
					? (
						Components.classes["@mozilla.org/browser/sessionstore;1"]
						|| Components.classes["@mozilla.org/suite/sessionstore;1"]
					).getService(Components.interfaces.nsISessionStore)
					: trgWindow.SessionStore; // Firefox 61+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450559
				if(!("forgetClosedTab" in ss))
					return;
				var closedTabs = (ss.getClosedTabDataForWindow(window));
				for(let i = 0, l = closedTabs.length; i < l; ++i) {
					let closedTab = closedTabs[i];
					let state = closedTab.state;
					if(state.entries[state.index - 1].url == ADDONS_URL) {
						ss.forgetClosedTab(window, i);
						return;
					}
				}
				if(!isSecondTry) // May be needed in SeaMonkey
					setTimeout(forgetClosedTab, 0, true);
			})();
		}

		if(!notFound.hidden) {
			removeTab();
			localize(notFound, "addon-updates-none-found", function(s) {
				notify(s);
			});
			return;
		}
		if(autoUpdateChecked) {
			removeTab();
			localize(updated, "addon-updates-installed", function(s) {
				notify(s);
			});
			return;
		}

		tab.collapsed = false;

		var cats = $("categories");
		var upds = $("category-availableUpdates");
		if(cats && upds) {
			if(vb && cats.selectedItem == upds) // Only for Firefox 72+
				cats.selectedItem = $("category-extension"); // Trick to force update
			cats.selectedItem = upds;
		}
		else { // Firefox 76+ ?
			vbDoc.querySelector('.category[name="available-updates"]').click();
		}

		var tabWin = tab.ownerDocument.defaultView;
		if(tbTab)
			tabmail.switchToTab(tbTabInfo);
		else
			tabWin.gBrowser.selectedTab = tab;
		setTimeout(function() {
			tabWin.focus();
			doc.defaultView.focus();
			var al = $("addon-list") || vb;
			al.focus();
		}, 0);
	}, 50);
	function $(id) {
		return doc.getElementById(id);
	}
	function stopWait() {
		clearInterval(waitTimer);
		progressIcon.restore();
		btn.tooltipText = tip;
		if(tab.image == image)
			tab.image = tab.getAttribute(origAttr);
		tab.removeAttribute(origAttr);
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() {
			delete btn._cb_disabled;
		}, 500);
	}
	function notify(msg) {
		Components.classes["@mozilla.org/alerts-service;1"]
			.getService(Components.interfaces.nsIAlertsService)
			.showAlertNotification(
				app == "Firefox" && pv >= 57
					? "chrome://mozapps/skin/extensions/extensionGeneric.svg"
					: "chrome://mozapps/skin/extensions/extensionGeneric.png",
				btn.label,
				msg, false, "", null
			);
	}
}
function dontSelectHiddenTab(e) {
	// <tab /><tab collapsed="true" />
	// Close first tab: collapsed tab becomes selected
	var trgTab = e.originalTarget || e.target;
	if(trgTab != tab)
		return;

	if(/\n(?:BrowserOpenAddonsMgr|toEM)@chrome:\/\//.test(new Error().stack)) {
		// User open Add-ons Manager, show tab
		trgWindow.removeEventListener("TabSelect", dontSelectHiddenTab, false);
		setTimeout(function() { // Hidden tab can't be selected, so select it manually...
			tab.collapsed = tab.closing = false;
			gBrowser.selectedTab = tab;
		}, 0);
	}

	function done(t) {
		if(!t.hidden && !t.closing) {
			e.preventDefault();
			e.stopPropagation();
			return gBrowser.selectedTab = t;
		}
		return false;
	}
	for(var t = tab.nextSibling; t; t = t.nextSibling)
		if(done(t))
			return;
	for(var t = tab.previousSibling; t; t = t.previousSibling)
		if(done(t))
			return;
}
function ProgressIcon(btn) {
	var app = Services.appinfo.name;
	var pv = parseFloat(Services.appinfo.platformVersion);
	if(app == "SeaMonkey")
		this.imgConnecting = this.imgLoading = "chrome://communicator/skin/icons/loading.gif";
	else if(app == "Thunderbird") {
		this.imgConnecting = "chrome://messenger/skin/icons/connecting.png";
		this.imgLoading = "chrome://messenger/skin/icons/loading.svg";
	}
	else {
		this.imgConnecting = app == "Firefox" && pv >= 58
			? "chrome://browser/skin/tabbrowser/tab-connecting.png"
			: "chrome://browser/skin/tabbrowser/connecting.png";
		this.imgLoading = app == "Firefox" && pv >= 48
			? "chrome://global/skin/icons/loading.png"
			: "chrome://browser/skin/tabbrowser/loading.png";
	}
	if(!(btn instanceof XULElement)) {
		this.loading = this.restore = function() {};
		return;
	}
	var useAnimation = app == "Firefox" && pv >= 32 && pv < 48;
	var btnIcon = btn.icon
		|| btn.ownerDocument.getAnonymousElementByAttribute(btn, "class", "toolbarbutton-icon");
	var origIcon = btnIcon.src;
	btnIcon.src = this.imgConnecting;
	if(useAnimation) {
		let cs = btnIcon.ownerDocument.defaultView.getComputedStyle(btnIcon, null);
		let s = btnIcon.style;
		s.margin = [cs.marginTop, cs.marginRight, cs.marginBottom, cs.marginLeft].join(" ");
		s.padding = [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].join(" ");
		s.width = cs.width;
		s.height = cs.height;
		s.boxShadow = "none";
		s.borderColor = s.background = "transparent";
		btnIcon.setAttribute("fadein", "true");
		btnIcon.setAttribute("busy", "true");
		btnIcon.classList.add("tab-throbber");
		btnIcon._restore = function() {
			delete btnIcon._restore;
			btnIcon.removeAttribute("busy");
			btnIcon.removeAttribute("progress");
			setTimeout(function() {
				btnIcon.classList.remove("tab-throbber");
				btnIcon.removeAttribute("style");
				btnIcon.removeAttribute("fadein");
			}, 0);
		};
	}
	this.loading = function() {
		btnIcon.src = this.imgLoading;
		if(useAnimation)
			btnIcon.setAttribute("progress", "true");
	};
	this.restore = function() {
		btnIcon.src = origIcon;
		if(useAnimation)
			btnIcon._restore();
	};
}
}).call(this);
//== Check for Addons Updates end
}              

this.tooltipText = "Переключатель джетпаков" 
                   + "\nПКМ – проверить обновления"
                   + "\nСКМ – открыть страницу дополнений"
                   + "\n\nВ меню: \nЛКМ – включить/выключить дополнение без закрытия меню"
                   + "\nShift+ЛКМ – включить/выключить дополнение"   
                   + "\nСКМ – открыть страницу дополнения в управлении дополнениями"                    
                   + "\nПКМ – открыть настройки дополнения (если есть)";     
// Autoopen/close feature
var openDelay = 200;
var closeDelay = 350;

var _openTimer = 0;
var _closeTimer = 0;
this.onmouseover = function(e) {
	clearTimeout(_closeTimer);
	if(e.target == this && closeOtherMenus()) {
		this.open = true;
		return;
	}
	_openTimer = setTimeout(function() {
		self.open = true;
	}, openDelay);
};
this.onmouseout = function(e) {
	clearTimeout(_openTimer);
	_closeTimer = setTimeout(function() {
			self.open = false;
	}, closeDelay);
};
function closeOtherMenus() {
	return Array.prototype.some.call(
		self.parentNode.getElementsByTagName("*"),
		function(node) {
			if(
				node != self
				&& node.namespaceURI == xulns
				// See https://github.com/Infocatcher/Custom_Buttons/issues/28
				//&& node.boxObject
				//&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject
				&& "open" in node
				&& node.open
				&& node.getElementsByTagName("menupopup").length
			) {
				node.open = false;
				return true;
			}
			return false;
		}
	);
}

Отсутствует

 

Board footer

Powered by PunBB
Modified by Mozilla Russia
Copyright © 2004–2020 Mozilla Russia GitHub mark
Язык отображения форума: [Русский] [English]