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

Хотите узнать больше о расширениях? Посмотрите ролики, рассказывающие о работе с расширениями Firefox.

№1692604-08-2023 13:01:30

negodnik
 
 
Группа: Members
Зарегистрирован: 14-03-2013
Сообщений: 608
UA: Seamonkey 2.38
Веб-сайт

Re: Custom Buttons

Win 7
115.1.0esr (64-разрядный), с portableapps.
Антиподписячий код от 2022-10-19 Это ведь последний?
Больше никакого кода в mozilla.cfg нет. Кодировка не менялась.
«Ошибка при чтении файла настроек. Обратитесь к системному администратору.»

Выделить код

Код:

SyntaxError: expected expression, got ':' mozilla.cfg:5:31

Что со мной не так, почему у остальных работает?
UPD: Вот я тормоз — это FoxReplace некстати сработал, код заменил!

Отредактировано negodnik (04-08-2023 16:25:07)

Отсутствует

 

№1692705-08-2023 05:31:16

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 475
UA: Firefox 97.0

Re: Custom Buttons

Подскажите какой-либо приём сократить несколько команд с одним идентификатором аналогично VBS ? (то есть, не писать gBrowser)
    With gBrowser
        .selectAllTabs() : .reloadMultiSelectedTabs() : .clearMultiSelectedTabs()
    End With

Выделить код

Код:

gBrowser.selectAllTabs();
gBrowser.reloadMultiSelectedTabs();
gBrowser.clearMultiSelectedTabs();

// этот вариант НЕ нужен:
var g = gBrowser; g.selectAllTabs(); g.reloadMultiSelectedTabs(); g.clearMultiSelectedTabs();

Отсутствует

 

№1692805-08-2023 05:47:30

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

Re: Custom Buttons

Dobrov пишет

With

with

Отсутствует

 

№1692905-08-2023 13:14:08

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 475
UA: Firefox 97.0

Re: Custom Buttons

Dumby - Спасибо, слона и не заметил!
Вопрос по закрытию вкладки под указателем мыши (т.е. не обязательно текущая вкладка).
Имеется trg = объект под мышью. Сделал перебором на уровень выше, до tabbrowser-tab. Посоветуйте способ попроще?

Выделить код

Код:

"#tabbrowser-tabs": {
		128(trg){ // СКМ
			for (let i = 0; i < 9; i++) // вкладка под курсором мыши
				if (trg.className == "tabbrowser-tab")
					{ gBrowser.removeTab(trg); break;}
				else trg = trg.parentNode; },
	}, // перебор tab-close-button, tab-text tab-label, tab-label-container и прочих…

Отсутствует

 

№1693005-08-2023 15:20:46

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

Re: Custom Buttons

Dobrov пишет

выше, до tabbrowser-tab

closest

Отсутствует

 

№1693111-08-2023 14:15:56

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 475
UA: Firefox 97.0

Re: Custom Buttons

Dumby - вопрос по твоему старому коду перехвата кликов мыши: я запутался в this.selectors.filter(…
В объекте кликов имена ключей начинаются с «#», этот символ мешает, если вместо имени использовать переменную: [variable]: свойство


Просьба убрать символ решётки из имён ключей, но учитывая, что кроме .id ещё нужно имя класса, например:
".titlebar-button.titlebar-close": { // так работает, а без решётки нет: "downloads-button": {………
    1(){ Help()},………
var trg = e.target, id = trg.id || trg.className || trg.tagName; // дополнил строку из твоего кода


Твой код при наведении мыши на объекты возвращает имена, не содержащие решётки или точки: .id .className .tagName
Удобнее вбить эти же имена в data ( …… чтобы не писать кучу повторных строк с # решёткой, а юзать переменные: [variable]: свойство

Лучше менять мой вариант кода из ucf_hookClicks.js на гитхабе, он почти тот же

Выделить код

Код:

// https://github.com/VicDobrov/UserChromeFiles/blob/main/profile_ucf_dobrov/chrome/user_chrome_files/custom_scripts/ucf_hookClicks.js
Tag = { // Hints
	"titlebar-button titlebar-close":
`Закрыть Firefox
◉ колёсико	вернуть вкладку
◧ держать	краткая Справка`,
};
Mouse = { // Clicks
	"#urlbar-input": { // здесь решётка, а в объекте mouse Over таких префиктов нет 
		2(trg, forward){ trg.value = ""}, // clear urlbar
	},
	".titlebar-button.titlebar-close": {
		1(){ Help()},
	},
};
Over = { // при наведении мыши обновить Hints и строку статуса
get "urlbar-input"() {
	glob.toStatus(Tag["#"].split('|')[0], 2500);
},
"titlebar-button titlebar-close": Tag["title-close"],
};

listener = { // Действия мыши и перехват существующих, если CapsLock выключен. без doubleclick
	find(sel) {
		return Mouse[sel][this] || Mouse[sel][this + 1];
	},
	filter(sel) { return this.closest(sel);
	},
	handleEvent(e) {
		if (this.skip || e.detail > 1 || e.getModifierState("CapsLock")) return;
		var trg = e.target, id = trg.id || trg.className || trg.tagName;
		if (e.type == "mouseenter") {
			var hint = Over[id] || Over[(trg = trg.parentNode).id];
			if (hint) trg.tooltipText = hint; return; //обновить подсказку
		}
		var sels = this.selectors.filter(this.filter, trg);
		var {length} = sels;
		if (!length) return;
		var wheel = e.type.startsWith("w");
		var num = e.metaKey*64 + e.ctrlKey*32 + e.shiftKey*16 + e.altKey*8 + (wheel ? 2 : e.button*128); //dbl*4
		var obj = Mouse[
			length > 1 && sels.find(this.find, num) || sels[0]
		];
		if (wheel) return obj[num]?.(trg, e.deltaY < 0);
// mousedown
		if (e.type.startsWith("m")) {
			obj.mousedownTarget && this.stop(e);
			this.longPress = false; //+ задержка при обычном клике
			if (++num in obj)
				this.mousedownTID = setTimeout(this.onLongPress, 640, trg, obj, num);
			if (e.button == 2)
				this.ctx = trg.getAttribute("context"), trg.setAttribute("context","");
			return;
		}
		obj.mousedownTarget || this.stop(e);	//click
		if (this.longPress) return this.longPress = false;
		this.mousedownTID &&= clearTimeout(this.mousedownTID);
		if (!obj[num]) {
			if (e.button == 1) return;
			if (e.button) {
				num = "context";
				for(var p in this.a) this.a[p] = e[p];
			} else
				num = "dispatch", this.mdt = obj.mousedownTarget;
			obj = this;
		}
		obj[num](trg); //run
	},
	get selectors() {
		this.onLongPress = (trg, obj, num) => {
			this.mousedownTID = null;
			this.longPress = true;
			obj[num](trg);
		}
		delete this.selectors;
		return this.selectors = Object.keys(Mouse);
	},
	get mdEvent() {
		delete this.mdEvent;
		return this.mdEvent = new MouseEvent("mousedown", {bubbles: true});
	},
	context(trg) {
		this.ctx ? trg.setAttribute("context", this.ctx) : trg.removeAttribute("context");
		trg.dispatchEvent(new MouseEvent("contextmenu", this.a));
	},
	dispatch(trg) {
		this.skip = true;
		this.mdt ? trg.dispatchEvent(this.mdEvent) : trg.click();
		this.skip = false;
	},
	stop: e => {
		e.preventDefault(); e.stopImmediatePropagation();
	},
	a: {__proto__: null, bubbles: true, screenX: 0, screenY: 0}
}

Отредактировано Dobrov (12-08-2023 08:21:54)

Отсутствует

 

№1693212-08-2023 21:17:35

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

Re: Custom Buttons

Dobrov пишет

чтобы не писать кучу повторных строк с # решёткой, а юзать переменные: [variable]: свойство

Ну, если это прям так принципиально, то решётку можно и дописать,
типа завести функцию, которая будет возвращать
trg.id && "#" + trg.id || trg.className && "." + [...trg.classList].join(".") || trg.tagName


и вызывать её внутри блока if (e.type == "mouseenter") {…}
Кстати, заканчиваться код блока должен инструкцией return;
ведь исполнять дальнейший код по "mouseenter" не нужно.

Отсутствует

 

№1693313-08-2023 04:51:51

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 475
UA: Firefox 97.0

Re: Custom Buttons

Dumby пишет

если это прям так принципиально, то решётку можно и дописать

Не пойдёт! Надо решётку убрать из всех ключей, а не дописывать!
Сделал иначе, и работает, НО не понял, когда выполняется функция find(sel) ?
Подскажи, как вызвать find(sel) чтоб её отладить ? Или можно лишь sels[0] оставить ?
Ещё в моём варианте прокрутка вкладок колёсиком над вкладками не пашет, а только на пустой области #tabbrowser-tabs. В прежнем коде работает везде!

виртуальное добавление # или .

Выделить код

Код:

// var sels = this.selectors.filter(this.filter, trg); ВМЕСТО этой строки 4 следующих:
var sels = this.selectors.filter(
	(sel, p) => { // виртуальное добавление # или .
		return trg.closest((trg.id && "#" || trg.className && "." || "") + sel);
	});

find(sel) { // при каких условиях выполняется ?
	return data[sel][this] || data[sel][this + 1];
},
handleEvent(e) {
……………
	var obj = data[ // length > 1 когда выполняется ???
		length > 1 && sels.find(this.find, num) || sels[0]];

Отредактировано Dobrov (13-08-2023 06:57:58)

Отсутствует

 

№1693413-08-2023 11:38:13

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

Re: Custom Buttons

Dobrov пишет

Надо решётку убрать из всех ключей, а не дописывать!

Тогда, наверно, вместо closest() надо будет использовать
что-нибудь самописное, которое так и будет проверять от trg вверх
безрешёточный id, затем бесточечное класс-добро, затем имя тэга.

когда выполняется функция find(sel) ?

this.selectors — это массив всех прописанных селекторов.
Через его метод filter() из него создаётся новый массив sels,
в котором остаются только те селекторы, под которые подпадает
сам trg или какой-либо из его родительских элементов.


Если таких селекторов в массиве sels получится больше одного,
тогда и выполняется функция find(sel)

Подскажи, как вызвать find(sel)

Как-то так: this.find.call(myThis, myArg);

только на пустой области #tabbrowser-tabs

Ну правильно,
trg.closest((trg.id && "#" || trg.className && "." || "") + sel)
это именно точное соответствие trg и псевдо-селектора sel,
а не поиск от trg вверх.


Не говоря уже о том, что просто прицепить точку к началу className
не прокатит, если классов несколько, а не один.

Отсутствует

 

№1693513-08-2023 13:10:37

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 475
UA: Firefox 97.0

Re: Custom Buttons

Dumby пишет

Тогда, наверно, вместо closest() надо будет использовать
что-нибудь самописное, которое так и будет проверять от trg вверх
безрешёточный id, затем бесточечное класс-добро, затем имя тэга.

А можешь что-то конкретное решение предложить ? Я изменил только filter(sel) {… и всё развалилось!
Убрал решётку на входе функции и в ней пробовал авто-подставить "#" и "." - по идее всё должно работать также, но не пашет…

Выделить код

Код:

var sels = this.selectors.filter((sel) => {
	var d = trg.id && "#" || trg.className && "." || sel; // здесь по твоим советам различные варианты пробовал… 
	return trg.closest(d + sel);
});

Ещё запасной вариант: берём твой код и из data {} удаляем решётки, затем к каждому ключу добавляем "#" и "."
Выгода в том, что много id-ключей берутся из массива подсказок, экономя на каждый несколько повторов строк.

Отредактировано Dobrov (13-08-2023 13:21:26)

Отсутствует

 

№1693613-08-2023 14:42:08

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

Re: Custom Buttons

Dobrov пишет

А можешь что-то конкретное решение предложить ?

Нет, не могу.
Я просто не понимаю конечный смысл.


А перевести на JS то, что процитировано, могу попробовать
Вот, например, изменил метод filter()

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

Выделить код

Код:

/*
	filter(sel) { return this.closest(sel);
	},
*/
	filter(sel) {
		var node = this;
		do {
			if (node.id == sel || node.className == sel)
				return true;
		}
		while(node = node.parentNode /*node.flattenedTreeParentNode*/);
	},


И, из некоторых имён свойств объекта Mouse убрал решётку,
а где селектор класса заменил на дословный className.
И вроде работает.

Отсутствует

 

№1693713-08-2023 15:16:03

Dobrov
Участник
 
Группа: Members
Зарегистрирован: 04-10-2011
Сообщений: 475
UA: Firefox 97.0

Re: Custom Buttons

Dumby - Спасибо! :beer:


Свой способ сделал - объём кода поменьше (как выше писал), но и твоя разработка пригодиться!
Mouse = {}; Object.keys(data).forEach((k) =>{Mouse["#"+ k] = Mouse["."+ k] = data[k]});

А c кнопкой "Закрыть" в заголовке окна titlebar-button.titlebar-close мой способ работает, твой нет… :(


Dumby пишет

Я просто не понимаю конечный смысл.

Имена кликабельных объектов используются без "#" и ".", поэтому можно сократить код!

Строки лишь в объекте подсказок, повторы имён меняем на ссылки:

Выделить код

Код:

Tag { "ToggleButton": "подсказка кнопки…"};
var Bt = Object.keys(Tag); // список часто используемых имён

(async (id) => CustomizableUI.createWidget({ label: id, id: id, tooltiptext: Tag[id],
……… создание кнопки
}))(Bt[0]);

Over = { //подсказка при наведении указателя мыши, инфа в строке статуса…
	get [Bt[0]]() {return Tag[Bt[0]]}

Mouse = { [Bt[0]]: { …… // обработка кликов

Отредактировано Dobrov (15-08-2023 13:22:25)

Отсутствует

 

№1693829-08-2023 22:54:29

Andrey_Krropotkin
Участник
 
Группа: Members
Зарегистрирован: 11-11-2011
Сообщений: 484
UA: Firefox 117.0

Re: Custom Buttons

Dumby Я прочитал про Services.jsm, но не пойму чем заменить в этой кнопке "global", так как она завязана на "storage"

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

Выделить код

Код:

// https://forum.mozilla-russia.org/viewtopic.php?id=56040
// http://infocatcher.ucoz.net/js/cb/cbEditorToggleOnTop.js
// https://github.com/Infocatcher/Custom_Buttons/tree/master/CB_Editor_Toggle_on_Top

// Custom Buttons Editor: Toggle on Top button for Custom Buttons
// (code for "initialization" section)

// (c) Infocatcher 2012-2015
// version 0.1.11 - 2015-06-04

// Hotkey: Ctrl+T

const watcherId = "customButtonsToggleOnTop_" + this.id;
var {Components} = window; // Prevent garbage collection in Firefox 3.6 and older
var storage = (function() {
    if(!("Services" in window)) // Firefox 3.6 and older
        return Application.storage;
    // Simple replacement for Application.storage
    // See https://bugzilla.mozilla.org/show_bug.cgi?id=1090880
    //var global = Components.utils.getGlobalForObject(Services);
    // Ensure, that we have global object (because window.Services may be overwritten)
    var global = Components.utils.import("resource://gre/modules/Services.jsm", {});
    var ns = "_cbEditorToggleOnTopStorage";
    // Note: Firefox 57+ returns NonSyntacticVariablesObject w/o .Object property
    var storage = global[ns] || (global[ns] = Components.utils.getGlobalForObject(global).Object.create(null));
    return {
        get: function(key, defaultVal) {
            if(key in storage)
                return storage[key];
            return defaultVal;
        },
        set: function(key, val) {
            if(key === null)
                delete storage[key];
            else
                storage[key] = val;
        }
    };
})();
var watcher = storage.get(watcherId, null);
if(!watcher) {
    watcher = {
        btnPos: 0, // 0 - at top right window corner, 1 - at end of tabs, 2 - before dialog buttons spacer
        btnStyle: "button", // "button" or "toolbarbutton"
        btnChecked: true, // use "checked" style: true or false

        // Fogue icons, http://p.yusukekamiyamane.com/
        // http://www.iconfinder.com/icondetails/12276/16/gps_location_pin_icon
        icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjAx4SevkwAAAjxJREFUKM+FUk1oE0EU/mZnRU0U6cFLIT1KKkkNpIcSWwUvVty2l142hZpDoQcPgYQKQkUEz6V6sHgR/KGQQwleQg/xEKRRsIEmga4NoZa2oRGKpJaWmp2Z52UX12Dxg4838+D73ps3jzUYQycIgDc7l0oNAkgqpZ4n5uc/NgHsEEHDKZB/X5OJRGJcSplemJ6+6SZPFStvJ0Ta4eEhTNMcE0I8fDsxcQsAdPwHc6nUoBRCOz4+Rr1eR39//+18Pn/h/cjIGea+eS6VegLgKoBfRPRdKbXGOf8GIBmPx8cty0Kz2YTf74ff70cmkynpNSIAwP7+/t2ZmZnowcGBPDo6+rm1tdWo1Wo7pmnesSwLtm2jWCx+EkJwKSUnoln2wWnv9eTkMBE9TafT0b29PbTbbXDO0Wq1YNs2lpaWVpVSj8ZyuWXhaPg953CtXK6v9fVtr6ys9A4NDXU3Gg24JoVC4fPJycnjsVxu2TtMzgCUAVQAUKVSrwDtzc3NQCQS6d7d3XU7EK1W62twY+ML84i1N4zB5TvgYrVaPTs1NRXNZDKlrq4u+Hw+BAKBy1LK6zYA2xE+6/gqzhgbXl9fnw+FQrPBYPCHlBKjo6NRIjpPRD3kEdbcJWGMgTF2Y3Fx8UE4HE4yxl70WtYCEc1ms9kS51wDcM4r/FOS87BhGK90XTc0TeNuPgggFosNG4axOjAw8PJKxwIxAD26rt9XShWVUtvOWosO2h1RAJAMwCUAPseInGG6UTlm6l/8DZ+nFuAjSdH0AAAAAElFTkSuQmCC",
        iconPinned: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjIpwH1VRwAAAgdJREFUKM+FkkFoU0EQhr/dtxFSquLBS8CAICISQci1oBWLKaaINL300psXDxH0ItSD4L21UKgUxV4aWnKyiIGaQwio2IQ0PQQpBaEhoaEk0dSQNsnb9WAepg+LPwz/7DD/7O7MiJIQuGEAV3QIiNowV4f0HlA0BskJsI8fo75YLHIEjw3ccIInivVxX1IqcXFu7l4Lnhq4BaD4P4Y6INsHB+zlcpwfH7+zG48PavCIvj8/B64CRxoqXdg8Bd+BqG9xMVJNpaiUy5zxehkcGCC2uppV28Y44rs3V1aCFIt2p15v/MzlSj/S6eKlmZnRairFoW2zmUx+boHVBsvAtEj+fV5Iw4vbS0tBe2uLw04H2+ulUy5zaNu8X17OdOGZgkTX3TADCRumP0xNZY3fz6/9fSr5PNVGg6/x+BfdE/Y3wxJAHtj6wzt5aHsSiQv+4WFfbWcHu9VCeDzdRq32DdgQfZMQom9JjDGngcn19fWF2shI9vrYWNBqNjFCND8mk+8MTDq5s65RWUKIUKFQmA0EAtP3pawdra0xGokE0dqrwe8kvgS2e1cjpURKORyLxTaUUg+UUuciUvIWQq8hU5uYMPOQmQcuu7fAsqxr4XD4jVIqLKW0nPgV4AmEFiDzCF65hQLwK6Ueaq0/aa13e2vddVnHxV3AFsBZYKBXyPSa6bDuFdP/st/BfOD54p3eIQAAAABJRU5ErkJggg==",

        boxId: "cbToggleOnTopBox",
        btnId: "cbToggleOnTopButton",
        onTopAttr: "cbOnTop",
        naAttr: "cbOnTopNA",
        styleId: "cbToggleOnTopStyle",
        get btnTip() {
            var locale = (function() {
                if("Services" in window && "locale" in Services) {
                    var locales = Services.locale.requestedLocales // Firefox 64+
                        || Services.locale.getRequestedLocales && Services.locale.getRequestedLocales();
                    if(locales)
                        return locales[0];
                }
                var prefs = "Services" in window && Services.prefs
                    || Components.classes["@mozilla.org/preferences-service;1"]
                        .getService(Components.interfaces.nsIPrefBranch);
                function pref(name, type) {
                    return prefs.getPrefType(name) != prefs.PREF_INVALID ? prefs["get" + type + "Pref"](name) : undefined;
                }
                if(!pref("intl.locale.matchOS", "Bool")) { // Also see https://bugzilla.mozilla.org/show_bug.cgi?id=1414390
                    var locale = pref("general.useragent.locale", "Char");
                    if(locale && locale.substr(0, 9) != "chrome://")
                        return locale;
                }
                return Components.classes["@mozilla.org/chrome/chrome-registry;1"]
                    .getService(Components.interfaces.nsIXULChromeRegistry)
                    .getSelectedLocale("global");
            })().match(/^[a-z]*/)[0];
            if(locale == "ru")
                return "Поверх всех окон (Ctrl+T)";
            return "Always on top (Ctrl+T)";
        },

        REASON_STARTUP: 1,
        REASON_SHUTDOWN: 2,
        REASON_WINDOW_LOADED: 3,
        REASON_WINDOW_CLOSED: 4,

        get obs() {
            delete this.obs;
            return this.obs = Components.classes["@mozilla.org/observer-service;1"]
                .getService(Components.interfaces.nsIObserverService);
        },
        get ww() {
            delete this.ww;
            return this.ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                .getService(Components.interfaces.nsIWindowWatcher);
        },
        get wm() {
            delete this.wm;
            return this.wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                .getService(Components.interfaces.nsIWindowMediator);
        },
        init: function(reason) {
            this.obs.addObserver(this, "quit-application-granted", false);
            var ws = this.wm.getEnumerator(null);
            while(ws.hasMoreElements())
                this.initWindow(ws.getNext(), reason);
            this.ww.registerNotification(this);
        },
        destroy: function(reason) {
            this.obs.removeObserver(this, "quit-application-granted");
            var ws = this.wm.getEnumerator(null);
            while(ws.hasMoreElements())
                this.destroyWindow(ws.getNext(), reason);
            this.ww.unregisterNotification(this);
        },
        checkOnTopAttr: function(window) {
            if(this.version < 71)
                return this.checkOnTopAttr = function() {}
            var attr = this.onTopAttr;
            var id = window.document.documentElement.id;
            var xs = Cu.import("resource://gre/modules/Services.jsm", {})
                .Services.xulStore;
            (this.checkOnTopAttr = function(window) {
                var de = window.document.documentElement;
                if(de.hasAttribute(attr))
                    return;
                var url = window.location.href;
                if(xs.hasValue(url, id, attr))
                    de.setAttribute(attr, xs.getValue(url, id, attr));
            })(window);
        },
        initWindow: function(window, reason) {
            if(!this.isTargetWindow(window))
                return;
            window.addEventListener("keypress", this, true);
            if(this.hasSizeModeChangeEvent)
                window.addEventListener("sizemodechange", this, false);
            else {
                window.addEventListener("resize", this, false); // Can detect only maximize/restore
                this.legacySizeModeChange(window);
            }
            this.checkOnTopAttr(window);

            var document = window.document;
            this.removeStyle(document);
            this.addStyle(document);
            var box = document.getElementById(this.boxId);
            box && box.parentNode.removeChild(box);
            box = document.createElementNS(xulns, "hbox");
            box.id = this.boxId;
            var btn = document.createElementNS(xulns, this.btnStyle);
            btn.id = this.btnId;
            if(this.btnChecked) {
                btn.setAttribute("type", "checkbox");
                btn.setAttribute("autoCheck", "false");
            }
            btn.tooltipText = this.btnTip;
            btn.addEventListener("command", this, false);
            box.appendChild(btn);
            switch(this.btnPos) {
                default:
                    box.setAttribute("cbOnTopFloat", "true");
                    document.documentElement.appendChild(box);
                break;
                case 1:
                    if(this.platformVersion >= 72)
                        box.setAttribute("pack", "end");
                    else
                        box.setAttribute("align", "right");
                    let tabbox = document.getElementById("custombuttons-editbutton-tabbox");
                    let tabs = tabbox.getElementsByTagName("tabs")[0];
                    tabs.parentNode.insertBefore(box, tabs);
                    box.style.marginBottom = -(btn.boxObject || btn.getBoundingClientRect()).height + "px";

                break;
                case 2:
                    box.setAttribute("align", "center");
                    let btnBox = document.documentElement.getButton("accept").parentNode;
                    let insPos = btnBox.firstChild;
                    for(let node = insPos; node; node = node.nextSibling) {
                        if(node.localName == "spacer") {
                            insPos = node;
                            break;
                        }
                    }
                    btnBox.insertBefore(box, insPos);
            }
            this.checkWindowStatus(window, box);
            //this.setOnTop(btn);
        },
        destroyWindow: function(window, reason) {
            if(reason == this.REASON_WINDOW_CLOSED)
                window.removeEventListener("DOMContentLoaded", this, false); // Window can be closed before DOMContentLoaded
            if(!this.isTargetWindow(window))
                return;
            window.removeEventListener("keypress", this, true);
            if(this.hasSizeModeChangeEvent)
                window.removeEventListener("sizemodechange", this, false);
            else
                window.removeEventListener("resize", this, false);
            var document = window.document;
            var btn = this.shadow(document).getElementById(this.btnId);

            btn.removeEventListener("command", this, false);
            if(reason == this.REASON_SHUTDOWN) {
                let box = btn.parentNode;
                box.parentNode.removeChild(box);
                this.removeStyle(document);
                let xulWin = this.getXulWin(window);
                xulWin.zLevel = xulWin.normalZ;
            }
        },
        isTargetWindow: function(window) {
            return window.location.href.substr(0, 41) == "chrome://custombuttons/content/editor.xul";
        },
        observe: function(subject, topic, data) {
            if(topic == "quit-application-granted")
                this.destroy();
            else if(topic == "domwindowopened")
                subject.addEventListener("DOMContentLoaded", this, false);
            else if(topic == "domwindowclosed")
                this.destroyWindow(subject, this.REASON_WINDOW_CLOSED);
        },
        handleEvent: function(e) {
            var trg = e.originalTarget || e.target;
            var window;
            switch(e.type) {
                case "DOMContentLoaded":
                    window = trg.defaultView;
                    window.removeEventListener("DOMContentLoaded", this, false);
                    this.initWindow(window, this.REASON_WINDOW_LOADED);
                break;
                case "keypress":
                    if(
                        !(
                            (e.ctrlKey || e.metaKey) && !e.altKey && !e.shiftKey
                            && String.fromCharCode(e.charCode).toLowerCase() == "t"
                        )
                    )
                        break;
                    e.preventDefault();
                    e.stopPropagation();
                case "command":
                    window = trg.ownerDocument.defaultView.top;
                    this.toggleOnTop(window);
                break;
                case "sizemodechange":
                case "resize":
                    window = trg;
                    this.checkWindowStatus(window);
            }
        },
        version: 0,
        get hasSizeModeChangeEvent() {
            var appinfo = "Services" in window && Services.appinfo;
            delete this.hasSizeModeChangeEvent;
            return this.hasSizeModeChangeEvent = appinfo && (
                appinfo.name == "Pale Moon"
                || (this.version = parseFloat(appinfo.platformVersion)) >= 8
            );
        },
        legacySizeModeChange: function(window) {
            var lastState = window.windowState;
            window.setInterval(function(window, _this) {
                var state = window.windowState;
                if(state != lastState)
                    _this.checkWindowStatus(window);
                lastState = state;
            }, 150, window, this);
        },
        checkWindowStatus: function(window, box) {
            if(!box)
                box = this.shadow(window.document).getElementById(this.boxId);
            var na = String(window.windowState != window.STATE_NORMAL);
            if(box.getAttribute(this.naAttr) == na)
                return;
            box.setAttribute(this.naAttr, na);
            //LOG("Set n/a: " + na);
            this.setOnTop(box.firstChild);
        },
        addStyle: function(document) {
            var style = '\
                %box%[cbOnTopFloat] {\n\
                    display: block !important;\n\
                    position: fixed !important;\n\
                    top: 0 !important;\n\
                    right: 0 !important;\n\
                }\n\
                %btn%, %btn% * {\n\
                    margin: 0 !important;\n\
                    padding: 0 !important;\n\
                }\n\
                %btn% > .button-box > .button-icon {\n\
                    margin: -3px -1px !important;\n\
                }\n\
                toolbarbutton%btn% {\n\
                    padding: 0 2px !important;\n\
                }\n\
                %btn% {\n\
                    position: relative !important;\n\
                    z-index: 2147483647 !important;\n\
                    list-style-image: url("%icon%") !important;\n\
                    -moz-user-focus: ignore !important;\n\
                    min-height: 0 !important;\n\
                    min-width: 0 !important;\n\
                }\n\
                %btn%[%onTopAttr%="true"] {\n\
                    list-style-image: url("%iconPinned%") !important;\n\
                }\n\
                %box%[%naAttr%="true"] image {\n\
                    opacity: 0.75 !important;\n\
                }'
                .replace(/%box%/g, "#" + this.boxId)
                .replace(/%btn%/g, "#" + this.btnId)
                .replace(/%onTopAttr%/g, this.onTopAttr)
                .replace(/%icon%/g, this.icon)
                .replace(/%iconPinned%/g, this.iconPinned)
                .replace(/%naAttr%/g, this.naAttr);

            if(this.shadow(document, style))
                return;
            document.insertBefore(document.createProcessingInstruction(
                "xml-stylesheet",
                'id="' + this.styleId + '" href="' + "data:text/css,"
                    + encodeURIComponent(style) + '" type="text/css"'
            ), document.documentElement);
        },
        removeStyle: function(document) {
            if(this.shadow(document, false))
                return;
            var mark = 'id="' + this.styleId + '"';
            for(var child = document.documentElement; child = child.previousSibling; ) {
                if(
                    child.nodeType == child.PROCESSING_INSTRUCTION_NODE
                    && child.data.substr(0, mark.length) == mark
                ) {
                    document.removeChild(child);
                    break;
                }
            }
        },
        shadow: function(document, arg) {
            var sr = document.documentElement.shadowRoot;
            if(this.btnPos != 2 || !sr)
                return (this.shadow = function(document, arg) {
                    return arg === undefined ? document : false;
                })(document, arg);

            if(arg === undefined)
                return sr;
            var st = sr.querySelector("style"), id = this.styleId;
            if(arg) {
                st.append(arg);
                st.lastChild[id] = true;
            }
            else {
                var tn = Array.from(st.childNodes).find(function(node) {
                    return id in node;
                });
                tn && tn.remove();
            }
            return true;
        },
        getXulWin: function(window) {
            return (
                    window.docShell && window.docShell instanceof Components.interfaces.nsIDocShell
                        ? window.docShell
                        : window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                            .getInterface(Components.interfaces.nsIWebNavigation)
                            .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                )
                    .treeOwner
                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIXULWindow || Ci.nsIAppWindow);
        },
        setOnTop: function(btn, toggle) {
            var document = btn.ownerDocument;
            var root = document.documentElement;
            var onTop = root.getAttribute(this.onTopAttr) == "true";
            if(toggle) {
                onTop = !onTop;
                root.setAttribute(this.onTopAttr, onTop);
                if(root.id) {
                    if("persist" in document)
                        document.persist(root.id, this.onTopAttr);
                    else // Firefox 63+
                        Services.xulStore.persist(root, this.onTopAttr);
                }
            }
            else if(!onTop) // Just opened or restored window always have zLevel == normalZ
                return;
            var window = document.defaultView;
            var state = window.windowState;
            // Strange glitches with minimized "raisedZ" window...
            var restore = onTop && state == window.STATE_MINIMIZED;
            if(restore || state == window.STATE_NORMAL) {
                if(restore)
                    onTop = false;
                let xulWin = this.getXulWin(window);
                xulWin.zLevel = onTop ? xulWin.raisedZ : xulWin.normalZ;
                //LOG("Set on top: " + onTop);
            }
            this.checkButton(btn, onTop);
        },
        toggleOnTop: function(window) {
            this.setOnTop(this.shadow(window.document).getElementById(this.btnId), true);
        },
        checkButton: function(btn, onTop) {
            btn.setAttribute(this.onTopAttr, onTop);
            this.btnChecked && btn.setAttribute("checked", onTop);
        }
    };
    storage.set(watcherId, watcher);
    watcher.init(watcher.REASON_STARTUP);
}
function destructor(reason) {
    if(reason == "update" || reason == "delete") {
        watcher.destroy(watcher.REASON_SHUTDOWN);
        storage.set(watcherId, null);
    }
}
if(
    typeof addDestructor == "function" // Custom Buttons 0.0.5.6pre4+
    && addDestructor != ("addDestructor" in window && window.addDestructor)
)
    addDestructor(destructor, this);
else
    this.onDestroy = destructor;

Отсутствует

 

№1693930-08-2023 11:05:57

Black_Monk
Участник
 
Группа: Members
Зарегистрирован: 18-02-2012
Сообщений: 195
UA: Firefox 117.0

Re: Custom Buttons

После перехода на 117 перестали работать расширения custom_buttons и add_toolbar_buttons. Подскажите, что делать? Или я не в ту тему попал?


Windows 10 LTSC

Отсутствует

 

№1694030-08-2023 11:10:23

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

Re: Custom Buttons

про Custom Buttons 0.0.7.0.0.32 надо прочитать.


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

Отсутствует

 

№1694130-08-2023 11:35:09

Black_Monk
Участник
 
Группа: Members
Зарегистрирован: 18-02-2012
Сообщений: 195
UA: Firefox 117.0

Re: Custom Buttons

Farby пишет

про Custom Buttons 0.0.7.0.0.32 надо прочитать.

Дык оно не устанавливается так как оно "не может быть проверено". Опять config.js надо редактировать? Он у меня вот отсюда, но с правками из четырех стёртых строчек.


Windows 10 LTSC

Отсутствует

 

№1694230-08-2023 11:45:23

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

Re: Custom Buttons

Black_Monk пишет

"не может быть проверено"

Антиподписячий код надо брать здесь

Добавлено 30-08-2023 11:47:39
И запускатор здесь, для paxmod если что.

Добавлено 30-08-2023 11:56:40
ну или

bootstrap-loader

Выделить код

Код:

try {(ios => {
	var subst = "bootstrap-loader-config-script";
	ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(subst, ios.newURI(String.raw`
		data:,/%2A%2A%2A RDFDataSource.jsm %2A%2A%2A/%0A%0Aconst NS_XML = "http://www.w3.org/XML/1998/namespace";%0Aconst NS_XMLNS = "http://www.w3.org/2000/xmlns/";%0Aconst NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns%23";%0Aconst NS_NC = "http://home.netscape.com/NC-rdf%23";%0A%0Afunction isElement(obj) {%0A  return Element.isInstance(obj);%0A}%0Afunction isText(obj) {%0A  return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text";%0A}%0A%0A/%2A%2A%0A %2A Returns either an rdf namespaced attribute or an un-namespaced attribute%0A %2A value. Returns null if neither exists,%0A %2A/%0Afunction getRDFAttribute(element, name) {%0A  if (element.hasAttributeNS(NS_RDF, name))%0A    return element.getAttributeNS(NS_RDF, name);%0A  if (element.hasAttribute(name))%0A    return element.getAttribute(name);%0A  return undefined;%0A}%0A%0A/%2A%2A%0A %2A Represents an assertion in the datasource%0A %2A/%0Aclass RDFAssertion {%0A  constructor(subject, predicate, object) {%0A    // The subject on this assertion, an RDFSubject%0A    this._subject = subject;%0A    // The predicate, a string%0A    this._predicate = predicate;%0A    // The object, an RDFNode%0A    this._object = object;%0A    // The datasource this assertion exists in%0A    this._ds = this._subject._ds;%0A    // Marks that _DOMnode is the subject's element%0A    this._isSubjectElement = false;%0A    // The DOM node that represents this assertion. Could be a property element,%0A    // a property attribute or the subject's element for rdf:type%0A    this._DOMNode = null;%0A  }%0A%0A  getPredicate() {%0A    return this._predicate;%0A  }%0A%0A  getObject() {%0A    return this._object;%0A  }%0A}%0A%0Aclass RDFNode {%0A  equals(rdfnode) {%0A    return (rdfnode.constructor === this.constructor &&%0A            rdfnode._value == this._value);%0A  }%0A}%0A%0A/%2A%2A%0A %2A A simple literal value%0A %2A/%0Aclass RDFLiteral extends RDFNode {%0A  constructor(value) {%0A    super();%0A    this._value = value;%0A  }%0A%0A  getValue() {%0A    return this._value;%0A  }%0A}%0A%0A/%2A%2A%0A %2A This is an RDF node that can be a subject so a resource or a blank node%0A %2A/%0Aclass RDFSubject extends RDFNode {%0A  constructor(ds) {%0A    super();%0A    // A lookup of the assertions with this as the subject. Keyed on predicate%0A    this._assertions = {};%0A    // A lookup of the assertions with this as the object. Keyed on predicate%0A    this._backwards = {};%0A    // The datasource this subject belongs to%0A    this._ds = ds;%0A    // The DOM elements in the document that represent this subject. Array of Element%0A    this._elements = [];%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the given Element from the DOM document%0A   %2A/%0A  /%2A eslint-disable complexity %2A/%0A  _parseElement(element) {%0A    this._elements.push(element);%0A%0A    // There might be an inferred rdf:type assertion in the element name%0A    if (element.namespaceURI != NS_RDF ||%0A        element.localName != "Description") {%0A      var assertion = new RDFAssertion(this, RDF_R("type"),%0A                                       this._ds.getResource(element.namespaceURI + element.localName));%0A      assertion._DOMnode = element;%0A      assertion._isSubjectElement = true;%0A      this._addAssertion(assertion);%0A    }%0A%0A    // Certain attributes can be literal properties%0A    for (let attr of element.attributes) {%0A      if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS ||%0A          attr.nodeName == "xmlns")%0A        continue;%0A      if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) &&%0A          (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName)))%0A        continue;%0A      var object = null;%0A      if (attr.namespaceURI == NS_RDF) {%0A        if (attr.localName == "type")%0A          object = this._ds.getResource(attr.nodeValue);%0A      }%0A      if (!object)%0A        object = new RDFLiteral(attr.nodeValue);%0A      assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object);%0A      assertion._DOMnode = attr;%0A      this._addAssertion(assertion);%0A    }%0A%0A    var child = element.firstChild;%0A    element.listCounter = 1;%0A    while (child) {%0A      if (isElement(child)) {%0A        object = null;%0A        var predicate = child.namespaceURI + child.localName;%0A        if (child.namespaceURI == NS_RDF) {%0A          if (child.localName == "li") {%0A            predicate = RDF_R(%60_%24{element.listCounter}%60);%0A            element.listCounter++;%0A          }%0A        }%0A%0A        // Check for and bail out on unknown attributes on the property element%0A        for (let attr of child.attributes) {%0A          // Ignore XML namespaced attributes%0A          if (attr.namespaceURI == NS_XML)%0A            continue;%0A          // These are reserved by XML for future use%0A          if (attr.localName.substring(0, 3).toLowerCase() == "xml")%0A            continue;%0A          // We can handle these RDF attributes%0A          if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) &&%0A              ["resource", "nodeID"].includes(attr.localName))%0A            continue;%0A          // This is a special attribute we handle for compatibility with Mozilla RDF%0A          if (attr.namespaceURI == NS_NC &&%0A              attr.localName == "parseType")%0A            continue;%0A        }%0A%0A        var parseType = child.getAttributeNS(NS_NC, "parseType");%0A%0A        var resource = getRDFAttribute(child, "resource");%0A        var nodeID = getRDFAttribute(child, "nodeID");%0A%0A        if (resource !== undefined) {%0A          var base = Services.io.newURI(element.baseURI);%0A          object = this._ds.getResource(base.resolve(resource));%0A        } else if (nodeID !== undefined) {%0A          object = this._ds.getBlankNode(nodeID);%0A        } else {%0A          var hasText = false;%0A          var childElement = null;%0A          var subchild = child.firstChild;%0A          while (subchild) {%0A            if (isText(subchild) && /\S/.test(subchild.nodeValue)) {%0A              hasText = true;%0A            } else if (isElement(subchild)) {%0A              childElement = subchild;%0A            }%0A            subchild = subchild.nextSibling;%0A          }%0A%0A          if (childElement) {%0A            object = this._ds._getSubjectForElement(childElement);%0A            object._parseElement(childElement);%0A          } else%0A            object = new RDFLiteral(child.textContent);%0A        }%0A%0A        assertion = new RDFAssertion(this, predicate, object);%0A        this._addAssertion(assertion);%0A        assertion._DOMnode = child;%0A      }%0A      child = child.nextSibling;%0A    }%0A  }%0A  /%2A eslint-enable complexity %2A/%0A%0A  /%2A%2A%0A   %2A Adds a new assertion to the internal hashes. Should be called for every%0A   %2A new assertion parsed or created programmatically.%0A   %2A/%0A  _addAssertion(assertion) {%0A    var predicate = assertion.getPredicate();%0A    if (predicate in this._assertions)%0A      this._assertions[predicate].push(assertion);%0A    else%0A      this._assertions[predicate] = [ assertion ];%0A%0A    var object = assertion.getObject();%0A    if (object instanceof RDFSubject) {%0A      // Create reverse assertion%0A      if (predicate in object._backwards)%0A        object._backwards[predicate].push(assertion);%0A      else%0A        object._backwards[predicate] = [ assertion ];%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Returns all objects in assertions with this subject and the given predicate.%0A   %2A/%0A  getObjects(predicate) {%0A    if (predicate in this._assertions)%0A      return Array.from(this._assertions[predicate],%0A                        i => i.getObject());%0A%0A    return [];%0A  }%0A%0A  /%2A%2A%0A   %2A Retrieves the first property value for the given predicate.%0A   %2A/%0A  getProperty(predicate) {%0A    if (predicate in this._assertions)%0A      return this._assertions[predicate][0].getObject();%0A    return null;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFResource for the datasource. Private.%0A %2A/%0Aclass RDFResource extends RDFSubject {%0A  constructor(ds, uri) {%0A    super(ds);%0A    // This is the uri that the resource represents.%0A    this._uri = uri;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new blank node. Private.%0A %2A/%0Aclass RDFBlankNode extends RDFSubject {%0A  constructor(ds, nodeID) {%0A    super(ds);%0A    // The nodeID of this node. May be null if there is no ID.%0A    this._nodeID = nodeID;%0A  }%0A%0A  /%2A%2A%0A   %2A Sets attributes on the DOM element to mark it as representing this node%0A   %2A/%0A  _applyToElement(element) {%0A    if (!this._nodeID)%0A      return;%0A    if (USE_RDFNS_ATTR) {%0A      var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A      element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A    } else {%0A      element.setAttribute("nodeID", this._nodeID);%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Creates a new Element in the document for holding assertions about this%0A   %2A subject. The URI controls what tagname to use.%0A   %2A/%0A  _createNewElement(uri) {%0A    // If there are already nodes representing this in the document then we need%0A    // a nodeID to match them%0A    if (!this._nodeID && this._elements.length > 0) {%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    return super._createNewElement.call(uri);%0A  }%0A%0A  /%2A%2A%0A   %2A Adds a reference to this node to the given property Element.%0A   %2A/%0A  _addReferenceToElement(element) {%0A    if (this._elements.length > 0 && !this._nodeID) {%0A      // In document elsewhere already%0A      // Create a node ID and update the other nodes referencing%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    if (this._nodeID) {%0A      if (USE_RDFNS_ATTR) {%0A        let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A        element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A      } else {%0A        element.setAttribute("nodeID", this._nodeID);%0A      }%0A    } else {%0A      // Add the empty blank node, this is generally right since further%0A      // assertions will be added to fill this out%0A      var newelement = this._ds._addElement(element, RDF_R("Description"));%0A      newelement.listCounter = 1;%0A      this._elements.push(newelement);%0A    }%0A  }%0A%0A    /%2A%2A%0A     %2A Removes any reference to this node from the given property Element.%0A     %2A/%0A    _removeReferenceFromElement(element) {%0A      if (element.hasAttributeNS(NS_RDF, "nodeID"))%0A        element.removeAttributeNS(NS_RDF, "nodeID");%0A      if (element.hasAttribute("nodeID"))%0A        element.removeAttribute("nodeID");%0A    }%0A%0A  getNodeID() {%0A    return this._nodeID;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFDataSource from the given document. The document will be%0A %2A changed as assertions are added and removed to the RDF. Pass a null document%0A %2A to start with an empty graph.%0A %2A/%0Aclass RDFDataSource {%0A  constructor(document) {%0A    // All known resources, indexed on URI%0A    this._resources = {};%0A    // All blank nodes%0A    this._allBlankNodes = [];%0A%0A    // The underlying DOM document for this datasource%0A    this._document = document;%0A    this._parseDocument();%0A  }%0A%0A  static loadFromString(text) {%0A    let parser = new DOMParser();%0A    let document = parser.parseFromString(text, "application/xml");%0A%0A    return new this(document);%0A  }%0A%0A  /%2A%2A%0A   %2A Returns an rdf subject for the given DOM Element. If the subject has not%0A   %2A been seen before a new one is created.%0A   %2A/%0A  _getSubjectForElement(element) {%0A    var about = getRDFAttribute(element, "about");%0A%0A    if (about !== undefined) {%0A      let base = Services.io.newURI(element.baseURI);%0A      return this.getResource(base.resolve(about));%0A    }%0A    return this.getBlankNode(null);%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the document for subjects at the top level.%0A   %2A/%0A  _parseDocument() {%0A    var domnode = this._document.documentElement.firstChild;%0A    while (domnode) {%0A      if (isElement(domnode)) {%0A        var subject = this._getSubjectForElement(domnode);%0A        subject._parseElement(domnode);%0A      }%0A      domnode = domnode.nextSibling;%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Gets a blank node. nodeID may be null and if so a new blank node is created.%0A   %2A If a nodeID is given then the blank node with that ID is returned or created.%0A   %2A/%0A  getBlankNode(nodeID) {%0A    var rdfnode = new RDFBlankNode(this, nodeID);%0A    this._allBlankNodes.push(rdfnode);%0A    return rdfnode;%0A  }%0A%0A  /%2A%2A%0A   %2A Gets the resource for the URI. The resource is created if it has not been%0A   %2A used already.%0A   %2A/%0A  getResource(uri) {%0A    if (uri in this._resources)%0A      return this._resources[uri];%0A%0A    var resource = new RDFResource(this, uri);%0A    this._resources[uri] = resource;%0A    return resource;%0A  }%0A}%0A%0A%0A/%2A%2A%2A RDFManifestConverter.jsm %2A%2A%2A/%0A%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A  return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0A%0Afunction getValue(literal) {%0A  return literal && literal.getValue();%0A}%0A%0Afunction getProperty(resource, property) {%0A  return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A  constructor(ds) {%0A    this.ds = ds;%0A  }%0A%0A  static loadFromString(text) {%0A    return new this(RDFDataSource.loadFromString(text));%0A  }%0A}%0A%0Aclass InstallRDF extends Manifest {%0A  _readProps(source, obj, props) {%0A    for (let prop of props) {%0A      let val = getProperty(source, prop);%0A      if (val != null) {%0A        obj[prop] = val;%0A      }%0A    }%0A  }%0A%0A  _readArrayProp(source, obj, prop, target, decode = getValue) {%0A    let result = Array.from(source.getObjects(EM_R(prop)),%0A                            target => decode(target));%0A    if (result.length) {%0A      obj[target] = result;%0A    }%0A  }%0A%0A  _readArrayProps(source, obj, props, decode = getValue) {%0A    for (let [prop, target] of Object.entries(props)) {%0A      this._readArrayProp(source, obj, prop, target, decode);%0A    }%0A  }%0A%0A  _readLocaleStrings(source, obj) {%0A    this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A    this._readArrayProps(source, obj, {%0A      locale: "locales",%0A      developer: "developers",%0A      translator: "translators",%0A      contributor: "contributors",%0A    });%0A  }%0A%0A  decode() {%0A    let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A    let result = {};%0A%0A    let props = ["id", "version", "type", "updateURL", "optionsURL",%0A                 "optionsType", "aboutURL", "iconURL",%0A                 "bootstrap", "unpack", "strictCompatibility"];%0A    this._readProps(root, result, props);%0A%0A    let decodeTargetApplication = source => {%0A      let app = {};%0A      this._readProps(source, app, ["id", "minVersion", "maxVersion"]);%0A      return app;%0A    };%0A%0A    let decodeLocale = source => {%0A      let localized = {};%0A      this._readLocaleStrings(source, localized);%0A      return localized;%0A    };%0A%0A    this._readLocaleStrings(root, result);%0A%0A    this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"});%0A    this._readArrayProps(root, result, {"targetApplication": "targetApplications"},%0A                         decodeTargetApplication);%0A    this._readArrayProps(root, result, {"localized": "localized"},%0A                         decodeLocale);%0A    this._readArrayProps(root, result, {"dependency": "dependencies"},%0A                         source => getProperty(source, "id"));%0A%0A    return result;%0A  }%0A}%0A%0A%0A/%2A%2A%2A BootstrapLoader.jsm %2A%2A%2A/%0A%0Avar Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;%0Avar XPCOMUtils = globalThis.XPCOMUtils || ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;%0A%0AXPCOMUtils.defineLazyModuleGetters(this, {%0A  ConsoleAPI: "resource://gre/modules/Console.jsm",%0A  Blocklist: "resource://gre/modules/Blocklist.jsm",%0A  XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm"%0A});%0A%0Avar OPTIONS_TYPE_DIALOG = 1;%0A%0AServices.obs.addObserver(doc => {%0A  if (doc.location.protocol + doc.location.pathname === 'about:addons' ||%0A      doc.location.protocol + doc.location.pathname === 'chrome://mozapps/content/extensions/aboutaddons.html') {%0A    const win = doc.defaultView;%0A    let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent;%0A    win.customElements.get('addon-card').prototype.handleEvent = function (e) {%0A      if (e.type === 'click' &&%0A          e.target.getAttribute('action') === 'preferences' &&%0A          this.addon.optionsType == OPTIONS_TYPE_DIALOG) {%0A        var windows = Services.wm.getEnumerator(null);%0A        while (windows.hasMoreElements()) {%0A          var win2 = windows.getNext();%0A          if (win2.closed) {%0A            continue;%0A          }%0A          if (win2.document.documentURI == this.addon.optionsURL) {%0A            win2.focus();%0A            return;%0A          }%0A        }%0A        var features = 'chrome,titlebar,toolbar,centerscreen';%0A        win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features); %0A      } else {%0A        handleEvent_orig.apply(this, arguments);%0A      }%0A    }%0A    let update_orig = win.customElements.get('addon-options').prototype.update;%0A    win.customElements.get('addon-options').prototype.update = function (card, addon) {%0A      update_orig.apply(this, arguments);%0A      if (addon.optionsType == OPTIONS_TYPE_DIALOG)%0A        this.querySelector('panel-item[data-l10n-id="preferences-addon-button"]').hidden = false;%0A    }%0A  }%0A}, 'chrome-document-loaded');%0A%0Aconst {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0Aconst {XPIDatabase, AddonInternal} = ChromeUtils.import("resource://gre/modules/addons/XPIDatabase.jsm");%0A%0ACu.import("resource://gre/modules/addons/XPIDatabase.jsm", {}).defineAddonWrapperProperty("optionsType", function optionsType() {%0A  if (!this.isActive) {%0A    return null;%0A  }%0A%0A  let addon = this.__AddonInternal__;%0A  let hasOptionsURL = !!this.optionsURL;%0A%0A  if (addon.optionsType) {%0A    switch (parseInt(addon.optionsType, 10)) {%0A      case OPTIONS_TYPE_DIALOG:%0A      case AddonManager.OPTIONS_TYPE_TAB:%0A      case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:%0A        return hasOptionsURL ? addon.optionsType : null;%0A    }%0A    return null;%0A  }%0A%0A  return null;%0A});%0A%0AXPIDatabase.isDisabledLegacy = () => false;%0A%0AXPCOMUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => XPIProvider.BOOTSTRAP_REASONS);%0A%0Aconst {Log} = ChromeUtils.import('resource://gre/modules/Log.jsm');%0Avar logger = Log.repository.getLogger('addons.bootstrap');%0A%0A/%2A%2A%0A %2A Valid IDs fit this pattern.%0A %2A/%0Avar gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]%2A\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA      = ['id', 'version', 'type', 'internalName', 'updateURL',%0A                            'optionsURL', 'optionsType', 'aboutURL', 'iconURL'];%0Aconst PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL'];%0Aconst PROP_LOCALE_MULTI  = ['developers', 'translators', 'contributors'];%0A%0A// Map new string type identifiers to old style nsIUpdateItem types.%0A// Retired values:%0A// 32 = multipackage xpi file%0A// 8 = locale%0A// 256 = apiextension%0A// 128 = experiment%0A// theme = 4%0Aconst TYPES = {%0A  extension: 2,%0A  dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A  extension: true,%0A  dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A  let ext = filename.slice(-4).toLowerCase();%0A  return ext === '.xpi' || ext === '.zip';%0A}%0A%0A/%2A%2A%0A %2A Gets an nsIURI for a file within another file, either a directory or an XPI%0A %2A file. If aFile is a directory then this will return a file: URI, if it is an%0A %2A XPI file then it will return a jar: URI.%0A %2A%0A %2A @param {nsIFile} aFile%0A %2A        The file containing the resources, must be either a directory or an%0A %2A        XPI file%0A %2A @param {string} aPath%0A %2A        The path to find the resource at, '/' separated. If aPath is empty%0A %2A        then the uri to the root of the contained files will be returned%0A %2A @returns {nsIURI}%0A %2A        An nsIURI pointing at the resource%0A %2A/%0Afunction getURIForResourceInFile(aFile, aPath) {%0A  if (!isXPI(aFile.leafName)) {%0A    let resource = aFile.clone();%0A    if (aPath)%0A      aPath.split('/').forEach(part => resource.append(part));%0A%0A    return Services.io.newFileURI(resource);%0A  }%0A%0A  return buildJarURI(aFile, aPath);%0A}%0A%0A/%2A%2A%0A %2A Creates a jar: URI for a file inside a ZIP file.%0A %2A%0A %2A @param {nsIFile} aJarfile%0A %2A        The ZIP file as an nsIFile%0A %2A @param {string} aPath%0A %2A        The path inside the ZIP file%0A %2A @returns {nsIURI}%0A %2A        An nsIURI for the file%0A %2A/%0Afunction buildJarURI(aJarfile, aPath) {%0A  let uri = Services.io.newFileURI(aJarfile);%0A  uri = 'jar:' + uri.spec + '!/' + aPath;%0A  return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A  name: 'bootstrap',%0A  manifestFile: 'install.rdf',%0A  async loadManifest(pkg) {%0A    /%2A%2A%0A     %2A Reads locale properties from either the main install manifest root or%0A     %2A an em:localized section in the install manifest.%0A     %2A%0A     %2A @param {Object} aSource%0A     %2A        The resource to read the properties from.%0A     %2A @param {boolean} isDefault%0A     %2A        True if the locale is to be read from the main install manifest%0A     %2A        root%0A     %2A @param {string[]} aSeenLocales%0A     %2A        An array of locale names already seen for this install manifest.%0A     %2A        Any locale names seen as a part of this function will be added to%0A     %2A        this array%0A     %2A @returns {Object}%0A     %2A        an object containing the locale properties%0A     %2A/%0A    function readLocale(aSource, isDefault, aSeenLocales) {%0A      let locale = {};%0A      if (!isDefault) {%0A        locale.locales = [];%0A        for (let localeName of aSource.locales || []) {%0A          if (!localeName) {%0A            logger.warn('Ignoring empty locale in localized properties');%0A            continue;%0A          }%0A          if (aSeenLocales.includes(localeName)) {%0A            logger.warn('Ignoring duplicate locale in localized properties');%0A            continue;%0A          }%0A          aSeenLocales.push(localeName);%0A          locale.locales.push(localeName);%0A        }%0A%0A        if (locale.locales.length == 0) {%0A          logger.warn('Ignoring localized properties with no listed locales');%0A          return null;%0A        }%0A      }%0A%0A      for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A        if (hasOwnProperty(aSource, prop)) {%0A          locale[prop] = aSource[prop];%0A        }%0A      }%0A%0A      return locale;%0A    }%0A%0A    let manifestData = await pkg.readString('install.rdf');%0A    let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A    let addon = new AddonInternal();%0A    for (let prop of PROP_METADATA) {%0A      if (hasOwnProperty(manifest, prop)) {%0A        addon[prop] = manifest[prop];%0A      }%0A    }%0A%0A    if (!addon.type) {%0A      addon.type = 'extension';%0A    } else {%0A      let type = addon.type;%0A      addon.type = null;%0A      for (let name in TYPES) {%0A        if (TYPES[name] == type) {%0A          addon.type = name;%0A          break;%0A        }%0A      }%0A    }%0A%0A    if (!(addon.type in TYPES))%0A      throw new Error('Install manifest specifies unknown type: ' + addon.type);%0A%0A    if (!addon.id)%0A      throw new Error('No ID in install manifest');%0A    if (!gIDTest.test(addon.id))%0A      throw new Error('Illegal add-on ID ' + addon.id);%0A    if (!addon.version)%0A      throw new Error('No version in install manifest');%0A%0A    addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A                                 manifest.strictCompatibility == 'true');%0A%0A    // Only read these properties for extensions.%0A    if (addon.type == 'extension') {%0A      if (manifest.bootstrap != 'true') {%0A        throw new Error('Non-restartless extensions no longer supported');%0A      }%0A%0A      if (addon.optionsType &&%0A          addon.optionsType != OPTIONS_TYPE_DIALOG &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) {%0A            throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType);%0A      }%0A%0A      if (addon.optionsType)%0A        addon.optionsType = parseInt(addon.optionsType);%0A    }%0A%0A    addon.defaultLocale = readLocale(manifest, true);%0A%0A    let seenLocales = [];%0A    addon.locales = [];%0A    for (let localeData of manifest.localized || []) {%0A      let locale = readLocale(localeData, false, seenLocales);%0A      if (locale)%0A        addon.locales.push(locale);%0A    }%0A%0A    let dependencies = new Set(manifest.dependencies);%0A    addon.dependencies = Object.freeze(Array.from(dependencies));%0A%0A    let seenApplications = [];%0A    addon.targetApplications = [];%0A    for (let targetApp of manifest.targetApplications || []) {%0A      if (!targetApp.id || !targetApp.minVersion ||%0A          !targetApp.maxVersion) {%0A            logger.warn('Ignoring invalid targetApplication entry in install manifest');%0A            continue;%0A      }%0A      if (seenApplications.includes(targetApp.id)) {%0A        logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id +%0A                    ' in install manifest');%0A        continue;%0A      }%0A      seenApplications.push(targetApp.id);%0A      addon.targetApplications.push(targetApp);%0A    }%0A%0A    // Note that we don't need to check for duplicate targetPlatform entries since%0A    // the RDF service coalesces them for us.%0A    addon.targetPlatforms = [];%0A    for (let targetPlatform of manifest.targetPlatforms || []) {%0A      let platform = {%0A        os: null,%0A        abi: null,%0A      };%0A%0A      let pos = targetPlatform.indexOf('_');%0A      if (pos != -1) {%0A        platform.os = targetPlatform.substring(0, pos);%0A        platform.abi = targetPlatform.substring(pos + 1);%0A      } else {%0A        platform.os = targetPlatform;%0A      }%0A%0A      addon.targetPlatforms.push(platform);%0A    }%0A%0A    addon.userDisabled = false;%0A    addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A    addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A    addon.userPermissions = null;%0A%0A    addon.icons = {};%0A    if (await pkg.hasResource('icon.png')) {%0A      addon.icons[32] = 'icon.png';%0A      addon.icons[48] = 'icon.png';%0A    }%0A%0A    if (await pkg.hasResource('icon64.png')) {%0A      addon.icons[64] = 'icon64.png';%0A    }%0A%0A    return addon;%0A  },%0A%0A  loadScope(addon) {%0A    let file = addon.file || addon._sourceBundle;%0A    let uri = getURIForResourceInFile(file, 'bootstrap.js').spec;%0A    let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A    let sandbox = new Cu.Sandbox(principal, {%0A      sandboxName: uri,%0A      addonId: addon.id,%0A      wantGlobalProperties: ['ChromeUtils'],%0A      metadata: { addonID: addon.id, URI: uri },%0A    });%0A%0A    try {%0A      Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A      XPCOMUtils.defineLazyGetter(sandbox, 'console', () =>%0A        new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 }));%0A%0A      Services.scriptloader.loadSubScript(uri, sandbox);%0A    } catch (e) {%0A      logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A    }%0A%0A    function findMethod(name) {%0A      if (sandbox[name]) {%0A        return sandbox[name];%0A      }%0A%0A      try {%0A        let method = Cu.evalInSandbox(name, sandbox);%0A        return method;%0A      } catch (err) { }%0A%0A      return () => {%0A        logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A      };%0A    }%0A%0A    let install = findMethod('install');%0A    let uninstall = findMethod('uninstall');%0A    let startup = findMethod('startup');%0A    let shutdown = findMethod('shutdown');%0A%0A    return {%0A      install(...args) {%0A        install(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      uninstall(...args) {%0A        uninstall(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      startup(...args) {%0A        if (addon.type == 'extension') {%0A          logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A          Components.manager.addBootstrappedManifestLocation(file);%0A        }%0A        return startup(...args);%0A      },%0A%0A      shutdown(data, reason) {%0A        try {%0A          return shutdown(data, reason);%0A        } catch (err) {%0A          throw err;%0A        } finally {%0A          if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A            logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A            Components.manager.removeBootstrappedManifestLocation(file);%0A          }%0A        }%0A      },%0A    };%0A  },%0A};%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0A%0Aif (AddonManager.isReady) {%0A  AddonManager.getAllAddons().then(addons => {%0A    addons.forEach(addon => {%0A      if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) {%0A        addon.reload();%0A      };%0A    });%0A  });%0A};%0A%0AObject.defineProperty(%0A  AddonInternal.prototype,%0A  "providesUpdatesSecurely",%0A  {enumerable: true, value: true}%0A);%0A
	`.trim()));
	Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
		.loadSubScript("resource://" + subst, new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils", "DOMParser", "Element", "fetch"]}));
})(Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService));} catch(ex) {Cu.reportError(ex);}

Отредактировано Farby (30-08-2023 11:56:40)


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

Отсутствует

 

№1694330-08-2023 12:05:33

Black_Monk
Участник
 
Группа: Members
Зарегистрирован: 18-02-2012
Сообщений: 195
UA: Firefox 117.0

Re: Custom Buttons

Farby пишет

Антиподписячий код надо брать здесь

Добавлено Сегодня 13:47:39
И запускатор здесь, для paxmod если что.

Ага, custom_buttons у меня заработал, спасибо. И  add_toolbar_buttons я вернул, но его дополнительных кнопок почему-то нигде нет. :(


Windows 10 LTSC

Отсутствует

 

№1694430-08-2023 12:12:22

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

Re: Custom Buttons

Andrey_Krropotkin пишет

не пойму чем заменить в этой кнопке "global"

Чем-нибудь глобальным, в смысле независимым от окон,
куда можно добавить ссылку на storage и она там останется, пока сам не удалишь.
Например, js-объект рождённый в модуле, только нормальный,
а не какой-нибудь неподходящий, типа замороженный как AppConstants или ещё что.


Ну или, собственно, сам SystemGlobal. Вот там же две строки с var global = …
Можно первую раскомментировать, а вторую закомментировать,
если конечно window.Services у тебя не «overwritten».


Black_Monk пишет

add_toolbar_buttons я вернул, но его дополнительных кнопок почему-то нигде нет.

А зачистка Services.jsm в нём проведена?

Отсутствует

 

№1694530-08-2023 12:35:25

Black_Monk
Участник
 
Группа: Members
Зарегистрирован: 18-02-2012
Сообщений: 195
UA: Firefox 117.0

Re: Custom Buttons

Dumby пишет

А зачистка Services.jsm в нём проведена?

config.js я конечно чистил, а вот где это делать для add_toolbar_buttons я не знаю.

Отредактировано Black_Monk (30-08-2023 12:39:48)


Windows 10 LTSC

Отсутствует

 

№1694630-08-2023 12:52:04

xrun1
Участник
 
Группа: Members
Зарегистрирован: 12-12-2013
Сообщений: 1224
UA: Firefox 117.0

Re: Custom Buttons

Dumby пишет

А зачистка Services.jsm в нём проведена?

Там два файла с этим Services.jsm
parent.js со строкой

Выделить код

Код:

var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

Эту можно закомментировать, как я понимаю, или удалить. А что делать со второй в файле ATBWinActorChild.jsm?

Выделить код

Код:

ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");

Отсутствует

 

№1694730-08-2023 13:11:53

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

Re: Custom Buttons

xrun1 пишет

Farby
Ещё я здесь спросил.
Спасибо.

да всё так же, var const let на выбор в зависимости от случая, правда с этим сам ещё до конца не понял...

Выделить код

Код:

// ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
var Services = globalThis.Services || ChromeUtils.import("resource://gre/modules/Services.jsm").Services;

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

Отсутствует

 

№1694831-08-2023 23:23:02

vitalii201
Участник
 
Группа: Members
Зарегистрирован: 24-03-2011
Сообщений: 679
UA: Firefox 117.0

Re: Custom Buttons

CB (custom_buttons-0.0.7.0.0.32-fx-bootstrap) отключился, при попытке переустановить: "не может быть проверено".

config-prefs
// https://forum.mozilla-russia.org/viewtopic.php?pid=720043#p720043
pref("general.config.obscure_value", 0);
pref("general.config.filename", "config.js");
pref("general.config.sandbox_enabled", false);

config

Выделить код

Код:

//
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 lexp = () => lockPref("extensions.experiments.enabled", true);
	var MRS = "MOZ_REQUIRE_SIGNING", AC = "AppConstants", uac = `resource://gre/modules/${AC}.`;

	if (o.isFrozen(o)) { // Fx 102.0b7+
		lexp(); disleg = true; genv();

		dbg.onEnterFrame = frame => {
			var {script} = frame;
			try {if (!script.url.startsWith(uac)) return;} catch {return;}
			dbg.onEnterFrame = undefined;

			if (script.isModule) { // ESM, Fx 108+
				var env = frame.environment;
				frame.onPop = () => env.setVariable(AC, gref.makeDebuggeeValue(freeze(
					o.assign(new o(), env.getVariable(AC).unsafeDereference(), {[MRS]: false})
				)));
			}
			else { // JSM
				var nsvo = frame.this.unsafeDereference();
				nsvo.Object = {freeze(ac) {
					ac[MRS] = false;
					delete nsvo.Object;
					return freeze(ac);
				}};
			}
		}
	}
	else o.freeze = obj => {
		if (!Components.stack.caller.filename.startsWith(uac)) return freeze(obj);
		obj[MRS] = 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("xpinstall.signatures.required", false);
	lockPref("extensions.langpacks.signatures.required", false);

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

		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);}

// bootstrap-loader.js https://forum.mozilla-russia.org/viewtopic.php?pid=795196#p795196
try {(ios => {
	var subst = "bootstrap-loader-config-script";
	ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(subst, ios.newURI(String.raw`
		data:,/%2A%2A%2A RDFDataSource.jsm %2A%2A%2A/%0A%0Aconst NS_XML = "http://www.w3.org/XML/1998/namespace";%0Aconst NS_XMLNS = "http://www.w3.org/2000/xmlns/";%0Aconst NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns%23";%0Aconst NS_NC = "http://home.netscape.com/NC-rdf%23";%0A%0Afunction isElement(obj) {%0A  return Element.isInstance(obj);%0A}%0Afunction isText(obj) {%0A  return obj && typeof obj == "object" && ChromeUtils.getClassName(obj) == "Text";%0A}%0A%0A/%2A%2A%0A %2A Returns either an rdf namespaced attribute or an un-namespaced attribute%0A %2A value. Returns null if neither exists,%0A %2A/%0Afunction getRDFAttribute(element, name) {%0A  if (element.hasAttributeNS(NS_RDF, name))%0A    return element.getAttributeNS(NS_RDF, name);%0A  if (element.hasAttribute(name))%0A    return element.getAttribute(name);%0A  return undefined;%0A}%0A%0A/%2A%2A%0A %2A Represents an assertion in the datasource%0A %2A/%0Aclass RDFAssertion {%0A  constructor(subject, predicate, object) {%0A    // The subject on this assertion, an RDFSubject%0A    this._subject = subject;%0A    // The predicate, a string%0A    this._predicate = predicate;%0A    // The object, an RDFNode%0A    this._object = object;%0A    // The datasource this assertion exists in%0A    this._ds = this._subject._ds;%0A    // Marks that _DOMnode is the subject's element%0A    this._isSubjectElement = false;%0A    // The DOM node that represents this assertion. Could be a property element,%0A    // a property attribute or the subject's element for rdf:type%0A    this._DOMNode = null;%0A  }%0A%0A  getPredicate() {%0A    return this._predicate;%0A  }%0A%0A  getObject() {%0A    return this._object;%0A  }%0A}%0A%0Aclass RDFNode {%0A  equals(rdfnode) {%0A    return (rdfnode.constructor === this.constructor &&%0A            rdfnode._value == this._value);%0A  }%0A}%0A%0A/%2A%2A%0A %2A A simple literal value%0A %2A/%0Aclass RDFLiteral extends RDFNode {%0A  constructor(value) {%0A    super();%0A    this._value = value;%0A  }%0A%0A  getValue() {%0A    return this._value;%0A  }%0A}%0A%0A/%2A%2A%0A %2A This is an RDF node that can be a subject so a resource or a blank node%0A %2A/%0Aclass RDFSubject extends RDFNode {%0A  constructor(ds) {%0A    super();%0A    // A lookup of the assertions with this as the subject. Keyed on predicate%0A    this._assertions = {};%0A    // A lookup of the assertions with this as the object. Keyed on predicate%0A    this._backwards = {};%0A    // The datasource this subject belongs to%0A    this._ds = ds;%0A    // The DOM elements in the document that represent this subject. Array of Element%0A    this._elements = [];%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the given Element from the DOM document%0A   %2A/%0A  /%2A eslint-disable complexity %2A/%0A  _parseElement(element) {%0A    this._elements.push(element);%0A%0A    // There might be an inferred rdf:type assertion in the element name%0A    if (element.namespaceURI != NS_RDF ||%0A        element.localName != "Description") {%0A      var assertion = new RDFAssertion(this, RDF_R("type"),%0A                                       this._ds.getResource(element.namespaceURI + element.localName));%0A      assertion._DOMnode = element;%0A      assertion._isSubjectElement = true;%0A      this._addAssertion(assertion);%0A    }%0A%0A    // Certain attributes can be literal properties%0A    for (let attr of element.attributes) {%0A      if (attr.namespaceURI == NS_XML || attr.namespaceURI == NS_XMLNS ||%0A          attr.nodeName == "xmlns")%0A        continue;%0A      if ((attr.namespaceURI == NS_RDF || !attr.namespaceURI) &&%0A          (["nodeID", "about", "resource", "ID", "parseType"].includes(attr.localName)))%0A        continue;%0A      var object = null;%0A      if (attr.namespaceURI == NS_RDF) {%0A        if (attr.localName == "type")%0A          object = this._ds.getResource(attr.nodeValue);%0A      }%0A      if (!object)%0A        object = new RDFLiteral(attr.nodeValue);%0A      assertion = new RDFAssertion(this, attr.namespaceURI + attr.localName, object);%0A      assertion._DOMnode = attr;%0A      this._addAssertion(assertion);%0A    }%0A%0A    var child = element.firstChild;%0A    element.listCounter = 1;%0A    while (child) {%0A      if (isElement(child)) {%0A        object = null;%0A        var predicate = child.namespaceURI + child.localName;%0A        if (child.namespaceURI == NS_RDF) {%0A          if (child.localName == "li") {%0A            predicate = RDF_R(%60_%24{element.listCounter}%60);%0A            element.listCounter++;%0A          }%0A        }%0A%0A        // Check for and bail out on unknown attributes on the property element%0A        for (let attr of child.attributes) {%0A          // Ignore XML namespaced attributes%0A          if (attr.namespaceURI == NS_XML)%0A            continue;%0A          // These are reserved by XML for future use%0A          if (attr.localName.substring(0, 3).toLowerCase() == "xml")%0A            continue;%0A          // We can handle these RDF attributes%0A          if ((!attr.namespaceURI || attr.namespaceURI == NS_RDF) &&%0A              ["resource", "nodeID"].includes(attr.localName))%0A            continue;%0A          // This is a special attribute we handle for compatibility with Mozilla RDF%0A          if (attr.namespaceURI == NS_NC &&%0A              attr.localName == "parseType")%0A            continue;%0A        }%0A%0A        var parseType = child.getAttributeNS(NS_NC, "parseType");%0A%0A        var resource = getRDFAttribute(child, "resource");%0A        var nodeID = getRDFAttribute(child, "nodeID");%0A%0A        if (resource !== undefined) {%0A          var base = Services.io.newURI(element.baseURI);%0A          object = this._ds.getResource(base.resolve(resource));%0A        } else if (nodeID !== undefined) {%0A          object = this._ds.getBlankNode(nodeID);%0A        } else {%0A          var hasText = false;%0A          var childElement = null;%0A          var subchild = child.firstChild;%0A          while (subchild) {%0A            if (isText(subchild) && /\S/.test(subchild.nodeValue)) {%0A              hasText = true;%0A            } else if (isElement(subchild)) {%0A              childElement = subchild;%0A            }%0A            subchild = subchild.nextSibling;%0A          }%0A%0A          if (childElement) {%0A            object = this._ds._getSubjectForElement(childElement);%0A            object._parseElement(childElement);%0A          } else%0A            object = new RDFLiteral(child.textContent);%0A        }%0A%0A        assertion = new RDFAssertion(this, predicate, object);%0A        this._addAssertion(assertion);%0A        assertion._DOMnode = child;%0A      }%0A      child = child.nextSibling;%0A    }%0A  }%0A  /%2A eslint-enable complexity %2A/%0A%0A  /%2A%2A%0A   %2A Adds a new assertion to the internal hashes. Should be called for every%0A   %2A new assertion parsed or created programmatically.%0A   %2A/%0A  _addAssertion(assertion) {%0A    var predicate = assertion.getPredicate();%0A    if (predicate in this._assertions)%0A      this._assertions[predicate].push(assertion);%0A    else%0A      this._assertions[predicate] = [ assertion ];%0A%0A    var object = assertion.getObject();%0A    if (object instanceof RDFSubject) {%0A      // Create reverse assertion%0A      if (predicate in object._backwards)%0A        object._backwards[predicate].push(assertion);%0A      else%0A        object._backwards[predicate] = [ assertion ];%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Returns all objects in assertions with this subject and the given predicate.%0A   %2A/%0A  getObjects(predicate) {%0A    if (predicate in this._assertions)%0A      return Array.from(this._assertions[predicate],%0A                        i => i.getObject());%0A%0A    return [];%0A  }%0A%0A  /%2A%2A%0A   %2A Retrieves the first property value for the given predicate.%0A   %2A/%0A  getProperty(predicate) {%0A    if (predicate in this._assertions)%0A      return this._assertions[predicate][0].getObject();%0A    return null;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFResource for the datasource. Private.%0A %2A/%0Aclass RDFResource extends RDFSubject {%0A  constructor(ds, uri) {%0A    super(ds);%0A    // This is the uri that the resource represents.%0A    this._uri = uri;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new blank node. Private.%0A %2A/%0Aclass RDFBlankNode extends RDFSubject {%0A  constructor(ds, nodeID) {%0A    super(ds);%0A    // The nodeID of this node. May be null if there is no ID.%0A    this._nodeID = nodeID;%0A  }%0A%0A  /%2A%2A%0A   %2A Sets attributes on the DOM element to mark it as representing this node%0A   %2A/%0A  _applyToElement(element) {%0A    if (!this._nodeID)%0A      return;%0A    if (USE_RDFNS_ATTR) {%0A      var prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A      element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A    } else {%0A      element.setAttribute("nodeID", this._nodeID);%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Creates a new Element in the document for holding assertions about this%0A   %2A subject. The URI controls what tagname to use.%0A   %2A/%0A  _createNewElement(uri) {%0A    // If there are already nodes representing this in the document then we need%0A    // a nodeID to match them%0A    if (!this._nodeID && this._elements.length > 0) {%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    return super._createNewElement.call(uri);%0A  }%0A%0A  /%2A%2A%0A   %2A Adds a reference to this node to the given property Element.%0A   %2A/%0A  _addReferenceToElement(element) {%0A    if (this._elements.length > 0 && !this._nodeID) {%0A      // In document elsewhere already%0A      // Create a node ID and update the other nodes referencing%0A      this._ds._createNodeID(this);%0A      for (let element of this._elements)%0A        this._applyToElement(element);%0A    }%0A%0A    if (this._nodeID) {%0A      if (USE_RDFNS_ATTR) {%0A        let prefix = this._ds._resolvePrefix(element, RDF_R("nodeID"));%0A        element.setAttributeNS(prefix.namespaceURI, prefix.qname, this._nodeID);%0A      } else {%0A        element.setAttribute("nodeID", this._nodeID);%0A      }%0A    } else {%0A      // Add the empty blank node, this is generally right since further%0A      // assertions will be added to fill this out%0A      var newelement = this._ds._addElement(element, RDF_R("Description"));%0A      newelement.listCounter = 1;%0A      this._elements.push(newelement);%0A    }%0A  }%0A%0A    /%2A%2A%0A     %2A Removes any reference to this node from the given property Element.%0A     %2A/%0A    _removeReferenceFromElement(element) {%0A      if (element.hasAttributeNS(NS_RDF, "nodeID"))%0A        element.removeAttributeNS(NS_RDF, "nodeID");%0A      if (element.hasAttribute("nodeID"))%0A        element.removeAttribute("nodeID");%0A    }%0A%0A  getNodeID() {%0A    return this._nodeID;%0A  }%0A}%0A%0A/%2A%2A%0A %2A Creates a new RDFDataSource from the given document. The document will be%0A %2A changed as assertions are added and removed to the RDF. Pass a null document%0A %2A to start with an empty graph.%0A %2A/%0Aclass RDFDataSource {%0A  constructor(document) {%0A    // All known resources, indexed on URI%0A    this._resources = {};%0A    // All blank nodes%0A    this._allBlankNodes = [];%0A%0A    // The underlying DOM document for this datasource%0A    this._document = document;%0A    this._parseDocument();%0A  }%0A%0A  static loadFromString(text) {%0A    let parser = new DOMParser();%0A    let document = parser.parseFromString(text, "application/xml");%0A%0A    return new this(document);%0A  }%0A%0A  /%2A%2A%0A   %2A Returns an rdf subject for the given DOM Element. If the subject has not%0A   %2A been seen before a new one is created.%0A   %2A/%0A  _getSubjectForElement(element) {%0A    var about = getRDFAttribute(element, "about");%0A%0A    if (about !== undefined) {%0A      let base = Services.io.newURI(element.baseURI);%0A      return this.getResource(base.resolve(about));%0A    }%0A    return this.getBlankNode(null);%0A  }%0A%0A  /%2A%2A%0A   %2A Parses the document for subjects at the top level.%0A   %2A/%0A  _parseDocument() {%0A    var domnode = this._document.documentElement.firstChild;%0A    while (domnode) {%0A      if (isElement(domnode)) {%0A        var subject = this._getSubjectForElement(domnode);%0A        subject._parseElement(domnode);%0A      }%0A      domnode = domnode.nextSibling;%0A    }%0A  }%0A%0A  /%2A%2A%0A   %2A Gets a blank node. nodeID may be null and if so a new blank node is created.%0A   %2A If a nodeID is given then the blank node with that ID is returned or created.%0A   %2A/%0A  getBlankNode(nodeID) {%0A    var rdfnode = new RDFBlankNode(this, nodeID);%0A    this._allBlankNodes.push(rdfnode);%0A    return rdfnode;%0A  }%0A%0A  /%2A%2A%0A   %2A Gets the resource for the URI. The resource is created if it has not been%0A   %2A used already.%0A   %2A/%0A  getResource(uri) {%0A    if (uri in this._resources)%0A      return this._resources[uri];%0A%0A    var resource = new RDFResource(this, uri);%0A    this._resources[uri] = resource;%0A    return resource;%0A  }%0A}%0A%0A%0A/%2A%2A%2A RDFManifestConverter.jsm %2A%2A%2A/%0A%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A  return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0A%0Afunction getValue(literal) {%0A  return literal && literal.getValue();%0A}%0A%0Afunction getProperty(resource, property) {%0A  return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A  constructor(ds) {%0A    this.ds = ds;%0A  }%0A%0A  static loadFromString(text) {%0A    return new this(RDFDataSource.loadFromString(text));%0A  }%0A}%0A%0Aclass InstallRDF extends Manifest {%0A  _readProps(source, obj, props) {%0A    for (let prop of props) {%0A      let val = getProperty(source, prop);%0A      if (val != null) {%0A        obj[prop] = val;%0A      }%0A    }%0A  }%0A%0A  _readArrayProp(source, obj, prop, target, decode = getValue) {%0A    let result = Array.from(source.getObjects(EM_R(prop)),%0A                            target => decode(target));%0A    if (result.length) {%0A      obj[target] = result;%0A    }%0A  }%0A%0A  _readArrayProps(source, obj, props, decode = getValue) {%0A    for (let [prop, target] of Object.entries(props)) {%0A      this._readArrayProp(source, obj, prop, target, decode);%0A    }%0A  }%0A%0A  _readLocaleStrings(source, obj) {%0A    this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A    this._readArrayProps(source, obj, {%0A      locale: "locales",%0A      developer: "developers",%0A      translator: "translators",%0A      contributor: "contributors",%0A    });%0A  }%0A%0A  decode() {%0A    let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A    let result = {};%0A%0A    let props = ["id", "version", "type", "updateURL", "optionsURL",%0A                 "optionsType", "aboutURL", "iconURL",%0A                 "bootstrap", "unpack", "strictCompatibility"];%0A    this._readProps(root, result, props);%0A%0A    let decodeTargetApplication = source => {%0A      let app = {};%0A      this._readProps(source, app, ["id", "minVersion", "maxVersion"]);%0A      return app;%0A    };%0A%0A    let decodeLocale = source => {%0A      let localized = {};%0A      this._readLocaleStrings(source, localized);%0A      return localized;%0A    };%0A%0A    this._readLocaleStrings(root, result);%0A%0A    this._readArrayProps(root, result, {"targetPlatform": "targetPlatforms"});%0A    this._readArrayProps(root, result, {"targetApplication": "targetApplications"},%0A                         decodeTargetApplication);%0A    this._readArrayProps(root, result, {"localized": "localized"},%0A                         decodeLocale);%0A    this._readArrayProps(root, result, {"dependency": "dependencies"},%0A                         source => getProperty(source, "id"));%0A%0A    return result;%0A  }%0A}%0A%0A%0A/%2A%2A%2A BootstrapLoader.jsm %2A%2A%2A/%0A%0Avar {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");%0Avar {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");%0A%0AXPCOMUtils.defineLazyModuleGetters(this, {%0A  ConsoleAPI: "resource://gre/modules/Console.jsm",%0A  Blocklist: "resource://gre/modules/Blocklist.jsm",%0A  XPIProvider: "resource://gre/modules/addons/XPIProvider.jsm"%0A});%0A%0Avar OPTIONS_TYPE_DIALOG = 1;%0A%0AServices.obs.addObserver(doc => {%0A  if (doc.location.protocol + doc.location.pathname === 'about:addons' ||%0A      doc.location.protocol + doc.location.pathname === 'chrome:/content/extensions/aboutaddons.html') {%0A    const win = doc.defaultView;%0A    let handleEvent_orig = win.customElements.get('addon-card').prototype.handleEvent;%0A    win.customElements.get('addon-card').prototype.handleEvent = function (e) {%0A      if (e.type === 'click' &&%0A          e.target.getAttribute('action') === 'preferences' &&%0A          this.addon.optionsType == OPTIONS_TYPE_DIALOG) {%0A        var windows = Services.wm.getEnumerator(null);%0A        while (windows.hasMoreElements()) {%0A          var win2 = windows.getNext();%0A          if (win2.closed) {%0A            continue;%0A          }%0A          if (win2.document.documentURI == this.addon.optionsURL) {%0A            win2.focus();%0A            return;%0A          }%0A        }%0A        var features = 'chrome,titlebar,toolbar,centerscreen';%0A        var instantApply = Services.prefs.getBoolPref('browser.preferences.instantApply');%0A        features += instantApply ? ',dialog=no' : '';%0A        win.docShell.rootTreeItem.domWindow.openDialog(this.addon.optionsURL, this.addon.id, features); %0A      } else {%0A        handleEvent_orig.apply(this, arguments);%0A      }%0A    }%0A    let update_orig = win.customElements.get('addon-options').prototype.update;%0A    win.customElements.get('addon-options').prototype.update = function (card, addon) {%0A      update_orig.apply(this, arguments);%0A      if (addon.optionsType == OPTIONS_TYPE_DIALOG)%0A        this.querySelector('panel-item[data-l10n-id="preferences-addon-button"]').hidden = false;%0A    }%0A  }%0A}, 'chrome-document-loaded');%0A%0Aconst {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0Aconst {XPIDatabase, AddonInternal} = ChromeUtils.import("resource://gre/modules/addons/XPIDatabase.jsm");%0A%0ACu.import("resource://gre/modules/addons/XPIDatabase.jsm", {}).defineAddonWrapperProperty("optionsType", function optionsType() {%0A  if (!this.isActive) {%0A    return null;%0A  }%0A%0A  let addon = this.__AddonInternal__;%0A  let hasOptionsURL = !!this.optionsURL;%0A%0A  if (addon.optionsType) {%0A    switch (parseInt(addon.optionsType, 10)) {%0A      case OPTIONS_TYPE_DIALOG:%0A      case AddonManager.OPTIONS_TYPE_TAB:%0A      case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:%0A        return hasOptionsURL ? addon.optionsType : null;%0A    }%0A    return null;%0A  }%0A%0A  return null;%0A});%0A%0AXPIDatabase.isDisabledLegacy = () => false;%0A%0AXPCOMUtils.defineLazyGetter(this, 'BOOTSTRAP_REASONS', () => XPIProvider.BOOTSTRAP_REASONS);%0A%0Aconst {Log} = ChromeUtils.import('resource://gre/modules/Log.jsm');%0Avar logger = Log.repository.getLogger('addons.bootstrap');%0A%0A/%2A%2A%0A %2A Valid IDs fit this pattern.%0A %2A/%0Avar gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]%2A\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA      = ['id', 'version', 'type', 'internalName', 'updateURL',%0A                            'optionsURL', 'optionsType', 'aboutURL', 'iconURL'];%0Aconst PROP_LOCALE_SINGLE = ['name', 'description', 'creator', 'homepageURL'];%0Aconst PROP_LOCALE_MULTI  = ['developers', 'translators', 'contributors'];%0A%0A// Map new string type identifiers to old style nsIUpdateItem types.%0A// Retired values:%0A// 32 = multipackage xpi file%0A// 8 = locale%0A// 256 = apiextension%0A// 128 = experiment%0A// theme = 4%0Aconst TYPES = {%0A  extension: 2,%0A  dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A  extension: true,%0A  dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A  let ext = filename.slice(-4).toLowerCase();%0A  return ext === '.xpi' || ext === '.zip';%0A}%0A%0A/%2A%2A%0A %2A Gets an nsIURI for a file within another file, either a directory or an XPI%0A %2A file. If aFile is a directory then this will return a file: URI, if it is an%0A %2A XPI file then it will return a jar: URI.%0A %2A%0A %2A @param {nsIFile} aFile%0A %2A        The file containing the resources, must be either a directory or an%0A %2A        XPI file%0A %2A @param {string} aPath%0A %2A        The path to find the resource at, '/' separated. If aPath is empty%0A %2A        then the uri to the root of the contained files will be returned%0A %2A @returns {nsIURI}%0A %2A        An nsIURI pointing at the resource%0A %2A/%0Afunction getURIForResourceInFile(aFile, aPath) {%0A  if (!isXPI(aFile.leafName)) {%0A    let resource = aFile.clone();%0A    if (aPath)%0A      aPath.split('/').forEach(part => resource.append(part));%0A%0A    return Services.io.newFileURI(resource);%0A  }%0A%0A  return buildJarURI(aFile, aPath);%0A}%0A%0A/%2A%2A%0A %2A Creates a jar: URI for a file inside a ZIP file.%0A %2A%0A %2A @param {nsIFile} aJarfile%0A %2A        The ZIP file as an nsIFile%0A %2A @param {string} aPath%0A %2A        The path inside the ZIP file%0A %2A @returns {nsIURI}%0A %2A        An nsIURI for the file%0A %2A/%0Afunction buildJarURI(aJarfile, aPath) {%0A  let uri = Services.io.newFileURI(aJarfile);%0A  uri = 'jar:' + uri.spec + '!/' + aPath;%0A  return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A  name: 'bootstrap',%0A  manifestFile: 'install.rdf',%0A  async loadManifest(pkg) {%0A    /%2A%2A%0A     %2A Reads locale properties from either the main install manifest root or%0A     %2A an em:localized section in the install manifest.%0A     %2A%0A     %2A @param {Object} aSource%0A     %2A        The resource to read the properties from.%0A     %2A @param {boolean} isDefault%0A     %2A        True if the locale is to be read from the main install manifest%0A     %2A        root%0A     %2A @param {string[]} aSeenLocales%0A     %2A        An array of locale names already seen for this install manifest.%0A     %2A        Any locale names seen as a part of this function will be added to%0A     %2A        this array%0A     %2A @returns {Object}%0A     %2A        an object containing the locale properties%0A     %2A/%0A    function readLocale(aSource, isDefault, aSeenLocales) {%0A      let locale = {};%0A      if (!isDefault) {%0A        locale.locales = [];%0A        for (let localeName of aSource.locales || []) {%0A          if (!localeName) {%0A            logger.warn('Ignoring empty locale in localized properties');%0A            continue;%0A          }%0A          if (aSeenLocales.includes(localeName)) {%0A            logger.warn('Ignoring duplicate locale in localized properties');%0A            continue;%0A          }%0A          aSeenLocales.push(localeName);%0A          locale.locales.push(localeName);%0A        }%0A%0A        if (locale.locales.length == 0) {%0A          logger.warn('Ignoring localized properties with no listed locales');%0A          return null;%0A        }%0A      }%0A%0A      for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A        if (hasOwnProperty(aSource, prop)) {%0A          locale[prop] = aSource[prop];%0A        }%0A      }%0A%0A      return locale;%0A    }%0A%0A    let manifestData = await pkg.readString('install.rdf');%0A    let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A    let addon = new AddonInternal();%0A    for (let prop of PROP_METADATA) {%0A      if (hasOwnProperty(manifest, prop)) {%0A        addon[prop] = manifest[prop];%0A      }%0A    }%0A%0A    if (!addon.type) {%0A      addon.type = 'extension';%0A    } else {%0A      let type = addon.type;%0A      addon.type = null;%0A      for (let name in TYPES) {%0A        if (TYPES[name] == type) {%0A          addon.type = name;%0A          break;%0A        }%0A      }%0A    }%0A%0A    if (!(addon.type in TYPES))%0A      throw new Error('Install manifest specifies unknown type: ' + addon.type);%0A%0A    if (!addon.id)%0A      throw new Error('No ID in install manifest');%0A    if (!gIDTest.test(addon.id))%0A      throw new Error('Illegal add-on ID ' + addon.id);%0A    if (!addon.version)%0A      throw new Error('No version in install manifest');%0A%0A    addon.strictCompatibility = (!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A                                 manifest.strictCompatibility == 'true');%0A%0A    // Only read these properties for extensions.%0A    if (addon.type == 'extension') {%0A      if (manifest.bootstrap != 'true') {%0A        throw new Error('Non-restartless extensions no longer supported');%0A      }%0A%0A      if (addon.optionsType &&%0A          addon.optionsType != OPTIONS_TYPE_DIALOG &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A          addon.optionsType != AddonManager.OPTIONS_TYPE_TAB) {%0A            throw new Error('Install manifest specifies unknown optionsType: ' + addon.optionsType);%0A      }%0A%0A      if (addon.optionsType)%0A        addon.optionsType = parseInt(addon.optionsType);%0A    }%0A%0A    addon.defaultLocale = readLocale(manifest, true);%0A%0A    let seenLocales = [];%0A    addon.locales = [];%0A    for (let localeData of manifest.localized || []) {%0A      let locale = readLocale(localeData, false, seenLocales);%0A      if (locale)%0A        addon.locales.push(locale);%0A    }%0A%0A    let dependencies = new Set(manifest.dependencies);%0A    addon.dependencies = Object.freeze(Array.from(dependencies));%0A%0A    let seenApplications = [];%0A    addon.targetApplications = [];%0A    for (let targetApp of manifest.targetApplications || []) {%0A      if (!targetApp.id || !targetApp.minVersion ||%0A          !targetApp.maxVersion) {%0A            logger.warn('Ignoring invalid targetApplication entry in install manifest');%0A            continue;%0A      }%0A      if (seenApplications.includes(targetApp.id)) {%0A        logger.warn('Ignoring duplicate targetApplication entry for ' + targetApp.id +%0A                    ' in install manifest');%0A        continue;%0A      }%0A      seenApplications.push(targetApp.id);%0A      addon.targetApplications.push(targetApp);%0A    }%0A%0A    // Note that we don't need to check for duplicate targetPlatform entries since%0A    // the RDF service coalesces them for us.%0A    addon.targetPlatforms = [];%0A    for (let targetPlatform of manifest.targetPlatforms || []) {%0A      let platform = {%0A        os: null,%0A        abi: null,%0A      };%0A%0A      let pos = targetPlatform.indexOf('_');%0A      if (pos != -1) {%0A        platform.os = targetPlatform.substring(0, pos);%0A        platform.abi = targetPlatform.substring(pos + 1);%0A      } else {%0A        platform.os = targetPlatform;%0A      }%0A%0A      addon.targetPlatforms.push(platform);%0A    }%0A%0A    addon.userDisabled = false;%0A    addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A    addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A    addon.userPermissions = null;%0A%0A    addon.icons = {};%0A    if (await pkg.hasResource('icon.png')) {%0A      addon.icons[32] = 'icon.png';%0A      addon.icons[48] = 'icon.png';%0A    }%0A%0A    if (await pkg.hasResource('icon64.png')) {%0A      addon.icons[64] = 'icon64.png';%0A    }%0A%0A    return addon;%0A  },%0A%0A  loadScope(addon) {%0A    let file = addon.file || addon._sourceBundle;%0A    let uri = getURIForResourceInFile(file, 'bootstrap.js').spec;%0A    let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A    let sandbox = new Cu.Sandbox(principal, {%0A      sandboxName: uri,%0A      addonId: addon.id,%0A      wantGlobalProperties: ['ChromeUtils'],%0A      metadata: { addonID: addon.id, URI: uri },%0A    });%0A%0A    try {%0A      Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A      XPCOMUtils.defineLazyGetter(sandbox, 'console', () =>%0A        new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 }));%0A%0A      Services.scriptloader.loadSubScript(uri, sandbox);%0A    } catch (e) {%0A      logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A    }%0A%0A    function findMethod(name) {%0A      if (sandbox[name]) {%0A        return sandbox[name];%0A      }%0A%0A      try {%0A        let method = Cu.evalInSandbox(name, sandbox);%0A        return method;%0A      } catch (err) { }%0A%0A      return () => {%0A        logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A      };%0A    }%0A%0A    let install = findMethod('install');%0A    let uninstall = findMethod('uninstall');%0A    let startup = findMethod('startup');%0A    let shutdown = findMethod('shutdown');%0A%0A    return {%0A      install(...args) {%0A        install(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      uninstall(...args) {%0A        uninstall(...args);%0A        // Forget any cached files we might've had from this extension.%0A        Services.obs.notifyObservers(null, 'startupcache-invalidate');%0A      },%0A%0A      startup(...args) {%0A        if (addon.type == 'extension') {%0A          logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A          Components.manager.addBootstrappedManifestLocation(file);%0A        }%0A        return startup(...args);%0A      },%0A%0A      shutdown(data, reason) {%0A        try {%0A          return shutdown(data, reason);%0A        } catch (err) {%0A          throw err;%0A        } finally {%0A          if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A            logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A            Components.manager.removeBootstrappedManifestLocation(file);%0A          }%0A        }%0A      },%0A    };%0A  },%0A};%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0A%0Aif (AddonManager.isReady) {%0A  AddonManager.getAllAddons().then(addons => {%0A    addons.forEach(addon => {%0A      if (addon.type == 'extension' && !addon.isWebExtension && !addon.userDisabled) {%0A        addon.reload();%0A      };%0A    });%0A  });%0A}%0A%0AObject.defineProperty(%0A  AddonInternal.prototype,%0A  "providesUpdatesSecurely",%0A  {enumerable: true, value: true}%0A);%0A
	`.trim()));
	Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
		.loadSubScript("resource://" + subst, new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils", "DOMParser", "Element", "fetch"]}));
})(Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService));} catch(ex) {Cu.reportError(ex);}

// https://forum.mozilla-russia.org/viewtopic.php?pid=780458#p780458
//(async xp => {
//	var imprt, 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} = (imprt = url => Cu.import(url, {}))(xp);

//	else { // Fx 101+
//		var g = Cu.getGlobalForObject(Cu), te = new g.TextEncoder();
//		var imp = g.ChromeUtils.import, {XPIInternal} = imp(xp);
//		var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
//		var rph = ios.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
//		imprt = (url, id) => {
//			var subst = te.encode(id).join("");
//			rph.setSubstitution(subst, ios.newURI(url));
//			return imp(`resource://${subst}/`);
//		}
//	}
//	var load = async (file, id) => {
//		var rootURI = XPIInternal.getURIForResourceInFile(file, "");
//		imprt(rootURI.resolve("startup.jsm"), 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.jsm");//


Как исправить?

Отсутствует

 

№1694901-09-2023 08:56:42

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

Re: Custom Buttons

vitalii201 пишет

Как исправить?

можно так...

Выделить код

Код:

https://forum.mozilla-russia.org/viewtopic.php?pid=806719#p806719


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

Отсутствует

 

№1695001-09-2023 09:46:05

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

Re: Custom Buttons

vitalii201 пишет

bootstrap

bootstrap-loader.js не зачищен от Services.jsm
Но он и не слишком-то дружественнен к правке по своей природе.
Лучше разворачивать что-то гитхабское.


Или вот, на пробу (замену), shitty-simple

bootstrap-loader-mini.js

Выделить код

Код:

// bootstrap-loader-mini.js
Cu.evalInSandbox(
	'(async () => {\n\tvar {console, DOMParser} = Cu.getGlobalForObject(Cu);\n\n\tvar imp = sub => {\n\t\tvar url = `resource://gre/modules/${sub}.`;\n\t\ttry {return ChromeUtils.importESModule(url + "sys.mjs");}\n\t\tcatch {return ChromeUtils.import(url + "jsm");}\n\t}\n\tvar {AddonManager} = imp("AddonManager");\n\tvar {XPIProvider} = imp("addons/XPIProvider");\n\tvar {XPIDatabase, AddonInternal} = imp("addons/XPIDatabase");\n\n\tXPIDatabase.isDisabledLegacy = () => false;\n\n\tvar OPTIONS_TYPE_DIALOG = 1; // AddonManager.OPTIONS_TYPE_DIALOG\n\tvar aboutaddons = "chrome://mozapps/content/extensions/aboutaddons.js";\n\n\tvar optionsTypeDescriptor = {\n\t\tenumerable: true,\n\t\tconfigurable: true, // extensions may has its own def\n\t\tget() {\n\t\t\tif (!this.isActive) return null;\n\n\t\t\tvar {caller} = Components.stack.caller;\n\t\t\tif (caller?.filename.startsWith(aboutaddons)) switch (caller.name) {\n\t\t\t\tcase "setElementState": return AddonManager.OPTIONS_TYPE_TAB; // show pref button\n\t\t\t\tcase "handleEvent": this.lastOpenOptionsCall -\n\t\t\t\t\t(this.lastOpenOptionsCall = Cu.now()) < -500 && openOptionsDialog(this);\n\t\t\t\tdefault: return null;\n\t\t\t}\n\t\t\treturn OPTIONS_TYPE_DIALOG;\n\t\t}\n\t};\n\n\tvar openOptionsDialog = async addon => {\n\t\tvar {optionsURL} = addon;\n\t\tfor(var win of Services.wm.getEnumerator(null))\n\t\t\tif (win.document?.documentURI == optionsURL && !win.closed)\n\t\t\t\treturn win.focus();\n\n\t\tServices.wm.getMostRecentWindow(null).openDialog(\n\t\t\toptionsURL, addon.id, "chrome,titlebar,toolbar,centerscreen"\n\t\t); \n\t}\n\n\tvar BootstrapLoader = {\n\t\tname: "bootstrap",\n\t\tmanifestFile: "install.rdf",\n\t\tasync loadManifest(pkg) {\n\n\t\t\tvar addon = new AddonInternal();\n\t\t\tvar xml = await pkg.readString("install.rdf");\n\t\t\tvar doc = new DOMParser().parseFromString(xml, "application/xml");\n\n\t\t\tfor(var prop of [\n\t\t\t\t"id", "version", "name", "description", "creator",\n\t\t\t\t"homepageURL", "updateURL", "optionsURL", "optionsType", "iconURL"\n\t\t\t])\n\t\t\t\taddon[prop] = doc.querySelector(":root > Description > " + prop)?.textContent;\n\n\t\t\tif (addon.optionsType) addon.optionsType = +addon.optionsType;\n\n\t\t\taddon.defaultLocale = {};\n\t\t\tvar locProps = ["name", "description", "creator"];\n\t\t\tfor(var p of locProps) addon.defaultLocale[p] = addon[p];\n\n\t\t\taddon.locales = [];\n\t\t\tfor(var desc of doc.querySelectorAll("localized > Description")) {\n\n\t\t\t\tvar locales = desc.querySelectorAll(":scope > locale");\n\t\t\t\tvar locale = {locales: Array.from(locales, n => n.textContent)};\n\t\t\t\t\n\t\t\t\tfor(var p of locProps)\n\t\t\t\t\tlocale[p] = desc.querySelector(":scope > " + p)?.textContent || addon[p];\n\n\t\t\t\taddon.locales.push(locale);\n\t\t\t}\n\t\t\taddon.type = "extension";\n\t\t\taddon.targetPlatforms = [];\n\t\t\taddon.targetApplications = [{id: "toolkit@mozilla.org"}];\n\n\t\t\taddon.icons = {};\n\t\t\tif (await pkg.hasResource("icon.png"))\n\t\t\t\taddon.icons[32] = addon.icons[48] = "icon.png";\n\t\t\tif (await pkg.hasResource("icon64.png"))\n\t\t\t\taddon.icons[64] = "icon64.png";\n\n\t\t\treturn addon;\n\t\t},\n\n\t\tpatch(wrapper) {\n\t\t\tvar addon = wrapper.__AddonInternal__;\n\t\t\tif (\n\t\t\t\taddon.optionsType == OPTIONS_TYPE_DIALOG\n\t\t\t\t&& addon.optionsURL && !wrapper.lastOpenOptionsCall\n\t\t\t)\n\t\t\t\twrapper.lastOpenOptionsCall = 1,\n\t\t\t\tObject.defineProperty(wrapper, "optionsType", optionsTypeDescriptor);\n\n\t\t\taddon.providesUpdatesSecurely ||\n\t\t\t\tObject.defineProperty(addon, "providesUpdatesSecurely", {enumerable: true, value: true});\n\t\t},\n\n\t\tloadScope(addon) {\n\t\t\tAddonManager.getAddonByID(addon.id).then(this.patch);\n\n\t\t\tvar file = addon.file || addon._sourceBundle;\n\n\t\t\tvar uri = Services.io.newFileURI(file).spec;\n\t\t\tif (file.isFile()) uri = `jar:${uri}!/`;\n\t\t\turi += "bootstrap.js";\n\n\t\t\tvar principal = Services.scriptSecurityManager.getSystemPrincipal();\n\n\t\t\tvar sandbox = new Cu.Sandbox(principal, {\n\t\t\t\tsandboxName: uri,\n\t\t\t\taddonId: addon.id,\n\t\t\t\twantGlobalProperties: ["ChromeUtils"],\n\t\t\t\tmetadata: {addonID: addon.id, URI: uri},\n\t\t\t});\n\n\t\t\ttry {\n\t\t\t\tObject.assign(sandbox, XPIProvider.BOOTSTRAP_REASONS);\n\t\t\t\tObject.defineProperty(sandbox, "console", {\n\t\t\t\t\tconfigurable: true, enumerable: true, get() {\n\t\t\t\t\t\tvar exp = imp("Console");\n\t\t\t\t\t\tdelete sandbox.console;\n\t\t\t\t\t\treturn sandbox.console = new exp.ConsoleAPI({consoleID: `addon/${addon.id}`});\n\t\t\t\t}});\n\t\t\t\tServices.scriptloader.loadSubScript(uri, sandbox);\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(`Error loading bootstrap.js for ${addon.id}`, e);\n\t\t\t}\n\n\t\t\tfunction findMethod(name) {\n\t\t\t\tif (sandbox[name]) return sandbox[name];\n\t\t\t\ttry {return Cu.evalInSandbox(name, sandbox);} catch {}\n\t\t\t\treturn () => console.warn(`Add-on ${addon.id} is missing bootstrap method ${name}`);\n\t\t\t}\n\n\t\t\tvar install = findMethod("install");\n\t\t\tvar uninstall = findMethod("uninstall");\n\t\t\tvar startup = findMethod("startup");\n\t\t\tvar shutdown = findMethod("shutdown");\n\n\t\t\treturn {\n\t\t\t\tinstall(...args) {\n\t\t\t\t\tinstall(...args);\n\t\t\t\t\t// Forget any cached files we might\'ve had from this extension.\n\t\t\t\t\tServices.obs.notifyObservers(null, "startupcache-invalidate");\n\t\t\t\t},\n\t\t\t\tuninstall(...args) {\n\t\t\t\t\tuninstall(...args);\n\t\t\t\t\t// Forget any cached files we might\'ve had from this extension.\n\t\t\t\t\tServices.obs.notifyObservers(null, "startupcache-invalidate");\n\t\t\t\t},\n\t\t\t\tstartup(...args) {\n\t\t\t\t\tComponents.manager.addBootstrappedManifestLocation(file);\n\t\t\t\t\treturn startup(...args);\n\t\t\t\t},\n\t\t\t\tshutdown(data, reason) {\n\t\t\t\t\ttry {return shutdown(data, reason);}\n\t\t\t\t\tcatch (ex) {throw ex;}\n\t\t\t\t\tfinally {\n\t\t\t\t\t\tif (reason != XPIProvider.BOOTSTRAP_REASONS.APP_SHUTDOWN)\n\t\t\t\t\t\t\tComponents.manager.removeBootstrappedManifestLocation(file);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t};\n\tAddonManager.addExternalExtensionLoader(BootstrapLoader);\n})();'
, this);


Посмотреть код во вкладке — запустить с консоли браузера:
gBrowser.selectedTab = gBrowser.addTrustedTab("data:text/plain;charset=utf-8," + encodeURIComponent(
    // здесь длинная строка из кода
));

Отсутствует

 

Board footer

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