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

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

№187608-02-2025 15:25:47

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

Re: UCF - ваши кнопки, скрипты…

unter_officer
Во навалился!
Проще browser.xhtml заоверрайдить без csp-меты (шучу, конечно :)).

Восстановление удалённых закладок или папок

Вроде этот код был признан непригодным к использованию,
из-за загадочного глюка, возникающего после вызова PlacesTransactions.undo()


Но, если абстрагироваться от этого, то что тут сложного?
Там, где устанавливаются атрибуты, изымаем oncommand


А вместо этого, далее, пишем строку, добавляющую листенер
menuitem.addEventListener("command", () => PlacesTransactions.undo().catch(Cu.reportError));
И все дела.


Остальное, — вот,
заменил четырёх-пробелья на табуляцию,
плюс попытка удаления «on…» атрибутов.
Ну, import() ещё, в этом (кем-то испорченном) SSM.
Только это, на большее рассчитывать не следует.

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

Выделить код

Код:

//
(async (pid, mp, self) => CustomizableUI.createWidget((self = {
	id: "ucf_SimpleSessionManager",
	label: "Simple Session Manager",
	tooltiptext: "Менеджер сессий",
	localized: false,
	init() {
		this.handleEvent = e => this[e.type](e);
		this.onTimeout = async () => await this.saveSession() || this.save();
		Services.obs.addObserver(this, "quit-application");
		var {openMenu} = this;
		this.render = function() {
			this.openMenu = openMenu;
			this.constructor.prototype.render.call(this);
		}
		delete this.init;
		return this;
	},
	image: "data:image/png;base64,.....",
	onCreated(btn) {
		btn.type = "menu";
		btn.phTimestamp = 0;
		btn.render = this.render;
		btn.onclick = this.click;
		btn.setAttribute("image", this.image);
		var popup = btn.ownerDocument.createXULElement("menupopup");
		popup.filler = this;
		popup.id = pid;

		btn.prepend(popup);
		popup.shadowRoot;
		popup.ensureInitialized = this.handlePopupshowing;

		btn.addEventListener("mousedown", this);
		popup.addEventListener("command", this);
	},
	handlePopupshowing() {
		var e = this.ownerGlobal.event;
		e.defaultPrevented || this.filler.fill(e);
	},
	openMenu(arg) {
		var pos;
		if (this.matches(".widget-overflow-list > :scope"))
			pos = "after_start";
		else var win = this.ownerGlobal, {width, height, top, bottom, left, right} =
			this.closest("toolbar").getBoundingClientRect(), pos = width > height
				? `${win.innerHeight - bottom > top ? "after" : "before"}_start`
				: `${win.innerWidth - right > left ? "end" : "start"}_before`;
		this.firstChild.setAttribute("position", pos);
		delete this.openMenu;
		this.openMenu(arg);
	},
	mousedown(e) {
		if (e.button) return;
		var trg = e.target;
		if (trg.nodeName.startsWith("t"))
			trg.mdTimestamp = Cu.now(),
			trg.tid = e.view.setTimeout(this.onTimeout, 500),
			e.preventDefault();
	},
	boot(trg) {
		var popup = trg.parentNode;
		var old = popup.querySelector("[boot]");
		if (old != trg) old?.removeAttribute("boot");
		trg.toggleAttribute("boot");
		popup.bootChanged = true;
	},
	muTimestamp: 0,
	click(e) {
		var trg = e.target;
		if (trg.nodeName == "menu") {
			if (e.button > 1) self.boot(trg);
			else if (Cu.now() - self.muTimestamp > 50)
				e.view.closeMenus(trg.menupopup),
				self.restoreSession(trg.label, (e.button || e.ctrlKey) && e.view);
		}
		else if (trg != this || e.button) return;
		e.view.clearTimeout(this.tid);
		if (this.mdTimestamp - this.phTimestamp > 50) this.open = true;
	},
	async command(e) {
		var arg, trg = e.target, cmd = trg.value;
		if (cmd.startsWith("r"))
			arg = trg.parentNode.parentNode.label;
		await this[cmd](arg) || this.save();
	},
	dragstart(e) {
		var trg = e.target;
		if (trg.nodeName != "menu") return;

		var popup = trg.parentNode;
		this.dragData = {trg, mouse: true};
		trg.menupopup.hidePopup();

		var win = trg.ownerGlobal;
		win.setCursor("grabbing");
		var {width} = trg.getBoundingClientRect();
		trg.setAttribute("maxwidth", width);

		win.addEventListener("mouseup", this);
		popup.addEventListener("mousemove", this);
	},
	mousemove(e) {
		var trg = e.target, dtrg = this.dragData.trg;
		if (trg == dtrg || trg.nodeName != "menu") return;

		e.movementY > 0
			? trg.nextSibling != dtrg && trg.after(dtrg)
			: trg.previousSibling != dtrg && trg.before(dtrg);
	},
	mouseup(arg) {
		if (arg.constructor.isInstance?.(arg)) {
			arg.preventDefault();
			var {trg} = this.dragData;
			this.dragData.mouse = false;
			this.muTimestamp = Cu.now();
		}
		else var trg = arg;

		trg.removeAttribute("maxwidth");
		trg.ownerGlobal.setCursor("auto");
		trg.ownerGlobal.removeEventListener("mouseup", this);
		trg.parentNode.removeEventListener("mousemove", this);
	},
	popuphidden(e) {
		if (!e.target.id) return;
		e.view.removeEventListener("keydown", this, true);
		var popup = e.target;
		popup.parentNode.phTimestamp = Cu.now();

		var save;
		if (this.dragData) {
			var {trg, mouse} = this.dragData;
			mouse && this.mouseup(trg);

			delete this.dragData;
			var order = Array.from(popup.getElementsByTagName("menu"), m => m.label);
			if (order.toString() != this.meta.order.toString()) {
				var hasBoot = this.meta.boot != null;
				if (hasBoot) var bootName = this.meta.order[this.meta.boot];
				this.meta.order = order;
				if (hasBoot) this.meta.boot = this.meta.order.indexOf(bootName);
				save = true;
			}
		}
		if (popup.bootChanged) {
			delete popup.bootChanged;
			var {boot} = this.meta;
			var bootNode = e.target.querySelector("[boot]");
			var ind = bootNode && this.meta.order.indexOf(bootNode.label);
			if (ind != boot)
				this.meta.boot = ind,
				save = true;
		}
		save && this.save(e.view);
	},

	sku: `#${pid} > menu[maxwidth]`,
	skd: `#${pid} > menu:is([maxwidth],[_moz-menuactive]):not([open])`,
	keydown(e) {
		if (e.repeat && e.key == "Shift" || !e.shiftKey && e.key != " ") return;
		var func = this.keyHandlers[e.key];
		if (!func) return;

		var menu = e.view.windowRoot
			.ownerGlobal.document.querySelector(this.skd);
		if (menu)
			e.stopImmediatePropagation(),
			func.call(this, menu, e);
	},
	keyup(e) {
		if (e.key != "Shift") return;
		var win = e.view.windowRoot.ownerGlobal;
		win.removeEventListener("keyup", this, true);
		win.document.querySelector(this.skd)?.removeAttribute("maxwidth");
	},
	keyHandlers: {
		Enter(menu) {
			this.boot(menu);
		}
	},
	arrow(menu) {
		if (menu.hasAttribute("maxwidth")) return;
		menu.setAttribute("maxwidth", menu.getBoundingClientRect().width);
		menu.ownerGlobal.addEventListener("keyup", this, true);
		this.dragData = {trg: menu};
	},
	fill(e) {
		var mxe = e.view.MozXULElement;

		var initFrag = mxe.parseXULToFragment(`
			<menuitem value="saveSession" class="menuitem-iconic" label="Сохранить сессию"/>
			<menuseparator/>
		`);
		this.menuFrag = mxe.parseXULToFragment(`<menu class="menu-iconic"><menupopup>
			<menuitem label="Переименовать"
				class="menuitem-iconic" value="renameSession"/>
			<menuitem label="Удалить"
				class="menuitem-iconic" value="removeSession"/>
		</menupopup></menu>`);

		this.regStyle();

		var filler = { fill: e => e.target.id
			? e.view.addEventListener("keydown", this, true)
				|| e.target.fillFlag || this.fillSessions(e.target)
				: this.dragData?.mouse && e.preventDefault()
		};

		(this.fill = e => {
			var trg = e.target;
			trg.setAttribute("context", "");
			trg.append(trg.ownerDocument.importNode(initFrag, true));
			(trg.filler = filler).fill(e);
			// trg.addEventListener("dragstart", this);
			trg.addEventListener("popuphidden", this);
		})(e);
	},
	fillSessions(popup) {
		while(popup.lastChild.nodeName == "menu") popup.lastChild.remove();
		var ind = 0, {boot} = this.meta;
		for(var name of this.meta.order) {
			var df = popup.ownerDocument.importNode(this.menuFrag, true);
			df.firstChild.setAttribute("label", name);
			if (ind++ == boot) df.firstChild.toggleAttribute("boot");
			popup.append(df);
		}
		popup.fillFlag = true;
	},
	regStyle() {
		delete this.regStyle;
		var subst = "ucf-ssm-style-resurl";
		Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler).setSubstitution(
			subst, Services.io.newURI("data:text/css;charset=utf-8," + encodeURIComponent(`
				@-moz-document url-prefix(chrome://browser/content/browser.xhtml) {
					#${pid} > menu {
						// list-style-image: url("${this.image}");
						list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEnQAABJ0BfDRroQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAGxSURBVDiNnZE/T1NRGMZ/7zn3lmKLkIKpaUxZFINEJZ0YHXRwcnBxMQ6aOJi4+FH8Dm5OOjA4+AEYGCTRook1JlVIVUrbW/rnPg63UtReIr7bOXmf5/ye59jDlanHS3O+wn/Mu+/DF8HFeVe5f+vaPTdfPpF4UNvk6frmhwAvXOEsvrR0IoN47xM4CHCADmDQBkAHbdTrTBRZdgYLs6NTD/MiMC+gC8MO6ke8ffWsXv3S3JpksHouv7Z4/UEeM8z6IwKfEGjYRt0G1d3m1p3n0Y1JBi/vTtfKw1Yec2CHBDFmXYjboAgL4tTc5pXsmcNcDxIDUFQHuqgfYT69OAuE9qqAoX4jIcAL7b9H+0deOYYg3tkYXxx2EAo7dQYEBF/TEULD5oow7KHut18RhGVD3GwxMZ36YWn6IJd37vQCSMSNBpYQCMs43PQMAJXlwtr6k7CGfo9iznN1ebGY7Ak1NSJwYERYJgM+Q+nClVzp/OXc3wWMwdT+jAVKCMwLC2PUeoMrrEI4m94BQq0a6mxjoca/YKFgUCfeqR8jPhoHcIAHe3ST2+UFLv2T8o/5uMvrnx9Wn65p5nMEAAAAAElFTkSuQmCC");
					}
					#${pid} > [value=saveSession] {
						list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKVSURBVHjajFNNTBNREJ6+7na73bLbQn8I0BKNCYWKGr0oYoiJPxeNh6oHozcOxgSVi4le1JNHQRPigRsewURNPKiJMQicSISUACelRVJ2tz/bdktpy9adBzT8xTjJ5L35ezNv5hsLHEAv7noEntguChx7EuV8oTydKRhfno8kCnt9LTuFZ7f87mAz/8rnl86GOoOBBq+LQX1S1ioLs0txWdEmsulS38Phlcy+BwZ6W0L+JsfolcjpsFDnBKNcgQ193bRUwSrwQFgr6Dkd3o9NzilqMdI/FF+sPYCZ245I4zdunw9DtQr6LxlK6bVdpdrcPAiHfGBYLDD69ltUXsmfw0oIGoPNjkHMjMHa3J9asDyRpoyEOrQR0+dq5MxR0c29Rj3Bhvn8YjeWjZmN9cpWcApSkzpkptYg8T1FdWjTfysg1Ang9YtdT+80OogTbJc7OlsD+OftzOqsBrMflqHe6QKXIEL00zLV0UpSBbM/G9DeEQy6OXKJsfPsCbdXYjb0Yu2/nmMS9LSGYGlYpXLPkxCwkq1mR98Gn8Q4XexxYhhgYKf3TPTfRDaPqklkvViaSSpaxSpw/x1v5e0UG8W10k+iW8qfF2ZiccIydFQ7ScsXKO8ep4NiYj4ai6k5+Eoev1F1Wcn8QJDgnAnHbGYRWTh800cZ77Ryzmr6eCGXzYGiZCcQ2vQ32XT5PiIMTJBI4Waw1ZtZzLu7XaSMd1s9b9paKJA+vpuKqols3y4ov7wXaPN67GPXIl1hnDOOansyVicHhGHMzHkzeDKqqPnr/UOJxX3LNNDb5BJd3KC3Uexu7wgEGxq3limRqcxH47FVWRtPreYePBpJagdu4zYhwjx1cMFus59CuVgqTmPDDlrnvwIMAGS8IFCLagHpAAAAAElFTkSuQmCC");
					}
					#${pid} [value=restoreSession] {
						list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAACzElEQVQ4jX2QPWwbZRjHf+/5vfPdObZjO3Ga2pVpGtxCP4QIDKgqDCDUNQwgJCS2qFI3hBCRCvXSCcTAAu0GY0Fi4kuq+IjUCpWAgCTq4IpS3Di2G8c+x/bZ57t7mSJFMfBf/3p+z+95BPvy3pufxqKmcVY3ZFFKmQtFGIReuNnrDze6lv5TqfSyx4EIgA/e+fzkZML6MDmVnD80mz1kxW3DMAxQCs/z6Hb6g3q1Xt1ttdfa7dbFN668vrkHkADJhPntufNnc0LTDi7AjFkkUknzcGF2LvCDuZVvfiwCj+/1GoAmhL3x6x2cZhsVqjFIGIZs15ts/LJOJCLt/Z0EiE3Y9eLpR1ONaoMH9zbHAGiC9FSKkwunWF1Z3RoDABhRg/zRPPmj+XHA/0QDaLectFLj6gcT+AHdTm9mzMC27e766lrWtCwy2Qz2hIUeNVDAaDCku9ujWW/ij3wsO9oaMzCiunf66TMU5gt4wyGbf1Upr5W5u15mq1JDhYqZQo6HIuqtueLw4vLXzwC89NZXeQFw/ePv7zx5buHEf6nf3qgMVipOGE0ko6EKhoQi3NnZ+c51BwsaQLOxM9N1uv86vNtz+aHSDn0pH9S2tl6r/Pn3s/VG/WYunzs/NZ2OSID0dKpRrzZS98v3kYbEsi1004BQcbtc9Q07bdYb229/dvmFL5aWVvVts9mq1Wo/e54vJIAQqGOPzQHgj3zcvos39BAIBshRqJQYjYZ/AFy79tQIeHVx+ctiIDRTA+g43Xud9q4PIHVJPBknk82QzqY5VZg2UUrEYhOX9s5aXL5RnMykb8Rj9kcC4OrSVZ1Hpt+PJ5PPz+SzR5KpyYRpGgRKMei7fHLr7lBmZ1XHcX4LgsCxLPt4JBLRm82Hr4j9DyuVSprdO/ZELGafkXpkXtPA94OK03Vv/R4/csEw5XOGbuieN6y6bv/S9XdfvPkPTgcoDlpQJpwAAAAASUVORK5CYII=");
					}
					#${pid} [value=renameSession] {
						list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPFJREFUeNrEU0sOgkAMbc1IIHoQT+KeY7hkBW5ccwLdsOICxI2X8peIGhggI21gggpCwsImM01f+tpOO0WlFIwR0efgeR5nsG0boihizPd91A5UwZDjuq5qwycwUkQYhttSr4Y4l76fDdtBEAQqz3OVZdnPQ09o2sQhrpBSQgnAen/qzLxZzljHcawxy7KAuCJNUyiKAm53SS0FBGQNlUW3lFO2iVCLYRhAXJEkCQPn65NGAswpL/4fWAebt1ZGXB3geHlw9maXsKqjS94CHNxF7xRM0/wKgI7jDBojvbnZAz3Gv/9E7NvGvmXCsev8EmAAWocA9ofpaRIAAAAASUVORK5CYII=");
					}
					#${pid} [value=removeSession] {
						list-style-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAABt0lEQVQ4ja2RT2sTURTFz8tk0nntFAQrVLBQxIUg/tkkMoLyIEVECHZhNrrxI+hOP5Mb6eBCcEipYIjZ2LpvhFasUtBSkwzOve+6mE6YSScuxLt67753fudyLvA/a7T+qCHmiTfrfWCMd3j9diPfq2SHX+uPnzJx71gP33w3bX9avBcE2j+SkK30Di7ffJ71VebMxD1hC2GGEEfH41prpftynIm92A0t2aYQQYgBThrndz/2KwCgf9Z2LNtImFOAtc356ijcCwJdJrZEUWzPfJpMAAAStPWR/h2Ktc10CoIQRUKEabFNvNbKfndcAKSjtrVfHW5YorWTzxDKYAxLtOWM+P7yt51hIYPpsDTNTyB/EwNAtWxdMuV8GCfbgxjxIviUYSV/SQOrbVjitbx4N8alBeXcdZR+3Tl3xS8FlIkt0dbnWH3xlbPgKgUo3HEq+tX7C4EuAAbGeOmqCuLIJt69s3PeQ1epKGfapCQJO6vGmwAWf/C1Wau6td8dV123BaAAUfHw6gSwtP3ugxA/y6X9INszAOQgbwFAIC/MQb9/Kv2vF2/UByejlVVn1Xiby/X6rPd/qj/1ak71UYKuwQAAAABJRU5ErkJggg==");
					}
					#${pid} > menuseparator:last-child,
					#${pid} > menu[maxwidth] > .menu-right {
						display: none;
					}
					#${pid} > menu[boot] {
						color: #AA0000;
						font-weight: bold;
					}
				}`.replace(/;$/gm, " !important;"))));
		var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService);
		sss.loadAndRegisterSheet(Services.io.newURI("resource://" + subst), sss.USER_SHEET);
	},
	get gs() {
		var gs = Object.create(null);
		Object.assign(gs, ChromeUtils.importESModule("resource:///modules/sessionstore/SessionStore.sys.mjs"));
		gs.SessionStoreInternal = UcfPrefs.dbg.ref("SessionStoreInternal", gs.SessionStore.undoCloseTab);
		delete this.gs;
		return this.gs = gs;
	},
	splice(name, newName) {
		var ind = this.meta.order.indexOf(name);
		if (ind == -1) return;
		var args = [ind, 1];

		if (1 in arguments) args.push(newName);
		else {
			if (ind == this.meta.boot) this.meta.boot = null;
			else if (ind < this.meta.boot) this.meta.boot--;
		}
		this.meta.order.splice(...args);
	},
	get meta() {
		var file = Services.dirsvc.get("UChrm", Ci.nsIFile);
		file.append("simple_session_manager.json");
		this.path = file.path;
		try {
			this.data = JSON.parse(Cu.readUTF8File(file));
		} catch {
			this.pp = file.parent.path;
			this.data = Object.create(null);
		}
		var meta = this.data[mp];
		if (!meta) {
			var order = Object.keys(this.data);
			meta = this.data[mp] = {order, boot: null};
		}
		delete this.meta;
		return this.meta = meta;
	},
	io: {
		get OS() {
			delete this.OS;
			Cu.import("resource://gre/modules/osfile.jsm", this);
			return this.OS;
		},
		makeDirectory(path) {
			return (this.makeDirectory = this.OS.File.makeDir)(path);
		},
		writeJSON(path, obj) {
			var wa = this.OS.File.writeAtomic;
			return (this.writeJSON = (path, obj) => wa(path, JSON.stringify(obj)))(path, obj);
		}
	},
	async save(excWin) {
		var io = Cu.getGlobalForObject(Cu).IOUtils || this.io;
		if (this.pp)
			await io.makeDirectory(this.pp), delete this.pp;
		(this.save = excWin => {
			this.meta.order.length
				? io.writeJSON(this.path, this.data)
				: io.remove(this.path, {ignoreAbsent: true});
			for(var win of CustomizableUI.windows) {
				if (win == excWin) continue;
				var popup = win.document.getElementById(pid);
				if (popup) popup.fillFlag = false;
			}
		})(excWin);
	},
	get prompter() {
		var {prompt} = Services;
		var p = {}, args = [null, null, "UCF Simple Session Manager"];
		p.alert = prompt.alert.bind(...args);
		p.confirm = prompt.confirm.bind(...args);
		var pr = prompt.prompt.bind(...args);
		p.prompt = (msg, value) => {
			var res = {value};
			return pr(msg, res, null, {}) ? res.value : null;
		}
		delete this.prompter;
		return this.prompter = p;
	},
	observe(s, t, data) {
		Services.obs.removeObserver(this, "quit-application");
		if (data.includes("restart")) return;

		var {boot} = this.meta;
		if (boot == null) return;

		var state = this.data[this.meta.order[boot]];
		var ssi = this.gs.SessionStoreInternal;
		ssi.getCurrentState = () => state;
		Services.obs.removeObserver(ssi, "browser:purge-session-history");

		Services.prefs.setBoolPref("browser.sessionstore.resume_session_once", true);
	},
	get bwt() {
		delete this.bwt;
		var url = "resource:///modules/BrowserWindowTracker.sys.mjs";
		return this.bwt = ChromeUtils.importESModule(url).BrowserWindowTracker;
	},
	getTabLabel() {
		return `${
			this.bwt.getTopWindow().gBrowser.selectedTab.label.slice(0, 70)
		}`;
	},
	getName(state) {
		var tl = 0;
		for(var w of state.windows) tl += w.tabs.length;
		return `${tl}(B) [${
			new Date().toLocaleString("mn").replace(" ", " - ")
		}]`;
	},
	exists(name) {
		this.meta;
		return (this.exists = name => name in this.data &&
			!this.prompter.alert("Сессия с тем же именем уже существует!"))(name);
	},
	getState() {
		return JSON.parse(this.gs.SessionStore.getBrowserState());
	},
	get spref() {
		var pref = "browser.sessionstore.interval";
		var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
		var wait = cb => timer.initWithCallback(cb, 1e3, timer.TYPE_ONE_SHOT);
		delete this.spref;
		return this.spref = async cb => {
			var val = Services.prefs.getIntPref(pref);
			Services.prefs.setIntPref(pref, 100);
			await new Promise(wait);
			Services.prefs.setIntPref(pref, val);
		}
	},
	async saveSession(name = this.getName(this.getState()), getTabLabel = this.getTabLabel()) {
		var saveName = this.prompter.prompt("Сохранить", getTabLabel );
		var name = (saveName + ", " + name );
		if (name == null || saveName == null) return true;

		if (this.exists(name)) return this.saveSession(name);

		await this.spref();
		this.data[name] = this.getState();

		this.meta.order.push(name);
		//this.meta.order.unshift(name);
		//if (this.meta.boot != null) this.meta.boot++;
	},
	restoreSession(name, win) {
		var ss = this.gs.SessionStore;
		var state = JSON.stringify(this.data[name]);
		win ? ss.setWindowState(win, state) : ss.setBrowserState(state);
	},
	renameSession(name, newName = name) {
		var newName = this.prompter.prompt(`Переименовать "${name}" в:`, newName);
		if (newName == null || newName == name) return true;

		if (this.exists(newName)) return this.renameSession(name, newName);

		var {data} = this;
		this.splice(name, newName);
		data[newName] = data[name];
		delete data[name];
	},
	removeSession(name) {
		if (!this.prompter.confirm(`Вы уверены, что хотите удалить: ${name} ?`))
			return true;
		delete this.data[name];
		this.splice(name);
	}
}).init()))("ucf-ssm-menupopup", "{07cae4f5-18b0-487b-80eb-973304af9528}");


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

Выделить код

Код:

//
try {CustomizableUI.createWidget({
	label: "Переключить текущий поисковик",
	id: "ucf_ToggleCurrentSearchEngine",
	localized: false,
	image: "data:image/png;base64,.....",
	excludeHiddenOneOffs: true,
	gn: () => Services.search.defaultEngine,
	gp: () => Services.search.defaultPrivateEngine,
	sn: val => Services.search.defaultEngine = val,
	sp: val => Services.search.defaultPrivateEngine = val,
	onCreated(btn) {
		btn.type = "menu";
		btn.setAttribute("image", this.image);

		var win = btn.ownerGlobal;
		var popup = btn.appendChild(win.document.createXULElement("menupopup"));
		var pr = win.PrivateBrowsingUtils.isWindowPrivate(win);

		popup.getDefaultEngine = pr ? this.gp : this.gn;
		popup.setDefaultEngine = pr ? this.sp : this.sn;

		popup.linkedObject = this;
		popup.addEventListener("command", this.command);
		popup.addEventListener("popupshowing", this.popupshowing);

		this.autoOpenCloseFeature(win, btn);
		this.updButton(btn, win);
	},
	command(e) {
		this.setDefaultEngine(e.target.engine);
	},
	popupshowing() {
		this.shouldRebuild && this.linkedObject.rebuild(this, this.ownerDocument);
	},
	getEngines() {
		var ve = Services.search.getVisibleEngines;
		if (!this.excludeHiddenOneOffs) return (this.getEngines = ve)();

		var arr = [];
		var args = this.fx116
			? [e => !e.hideOneOffButton]
			: Object.defineProperty(
				[function(e) {return !this.includes(e.name);}], "1", {get: () => {
					var str = Services.prefs.getStringPref(this.pref);
					return str ? str.split(",") : arr;
				}}
			);
			return (this.getEngines = async () => (await ve()).filter(...args))();
	},
	async rebuild(popup, doc) {
		popup.textContent = "";
		var df = doc.createDocumentFragment();
		var de = popup.getDefaultEngine().wrappedJSObject, jsde = this.json(de);
		var check = true;
		for(var engine of await this.getEngines()) {
			if (check && engine.name == de.name && this.json(engine) == jsde) {
				check = false; continue;
			}
			var menuitem = df.appendChild(doc.createXULElement("menuitem"));
			menuitem.engine = engine;
			menuitem.label = engine.name;
			menuitem.className = "menuitem-iconic";
			menuitem.image = await this.img(engine);
		}
		popup.append(df);
		delete popup.shouldRebuild;
	},
	async updButton(btn, win) {
		this.updButton = () => {};
		Services.search.isInitialized || await Services.search.init();
		this.fx116 = "hideOneOffButton" in Services.search.defaultEngine;

		var topics = ["browser-search-engine-modified", "quit-application-granted"];
		for(var topic of topics) Services.obs.addObserver(this, topic, false);
		this.observe = (s, topic) => this[topic[0]]();

		var remove = () => topics.forEach(
			topic => Services.obs.removeObserver(this, topic)
		);
		var {id} = this;
		var wins = callback => {
			for(var win of CustomizableUI.windows) {
				var btn = win.document.getElementById(id);
				btn && callback(btn, win);
			}
		}
		if (this.excludeHiddenOneOffs && !this.fx116) {
			var setRebuild = btn => btn.firstChild.shouldRebuild = true;
			var {pref} = this, obs = () => wins(setRebuild);
			Services.prefs.addObserver(pref, obs);
			this.q = () => remove(Services.prefs.removeObserver(pref, obs));
		}
		else this.q = remove;

		var updButton = (btn, win) => {
			var popup = btn.firstChild;
			var engine = popup.getDefaultEngine();
			/*btn.label =*/ btn.tooltipText = engine.name;
			popup.shouldRebuild = true;
			win.requestAnimationFrame(async () => btn.icon.src = await this.img(engine));
		}
		(this.b = () => wins(updButton))();
		this.updButton = updButton;
		btn.tooltipText || updButton(btn, win);
	},
	pref: "browser.search.hiddenOneOffs",
	json: e => JSON.stringify(e.toJSON()),
	img: async e => await e.getIconURL?.() || e.iconURI?.spec || "chrome://browser/skin/search-engine-placeholder.png",

	// https://github.com/Infocatcher/Custom_Buttons/blob/master/code_snippets/autoOpenCloseMenu.js
	// Automatically open menu on mouse over (and hide it on mouse out)
	autoOpenCloseFeature(win, btn, openDelay = 20000, closeDelay = 350) {
		var _openTimer = 0;
		var _closeTimer = 0;
		btn.onmouseover = function(e) {
			win.clearTimeout(_closeTimer);
			if(e.target == btn && closeOtherMenus()) {
				btn.open = true;
				return;
			}
			_openTimer = win.setTimeout(function() {
				btn.open = true;
			}, openDelay);
		};
		btn.onmouseout = function(e) {
			win.clearTimeout(_openTimer);
			_closeTimer = win.setTimeout(function() {
				btn.open = false;
			}, closeDelay);
		};
		function closeOtherMenus() {
			return win.Array.prototype.some.call(
				btn.parentNode.getElementsByTagName("*"),
				function(node) {
					if(
						node != btn
						&& win.XULElement.isInstance(node)
						// See https://github.com/Infocatcher/Custom_Buttons/issues/28
						//&& node.boxObject
						//&& node.boxObject instanceof Components.interfaces.nsIMenuBoxObject
						&& "open" in node
						&& node.open
						&& node.getElementsByTagName("menupopup").length
					) {
						node.open = false;
						return true;
					}
					return false;
				}
			);
		}
	}
});} catch(ex) {Cu.reportError(ex);}


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

Выделить код

Код:

//
(async func => CustomizableUI.createWidget({
	id: "ucf_SaveAsPNG",
	label: "Сохранить как PNG",
	tooltiptext: "Сохранить как PNG",
	localized: false,
	// defaultArea: CustomizableUI.AREA_NAVBAR,
	onCreated(btn) {
		var win = btn.ownerGlobal;
		new win.Function("_id, xhtmlns, addDestructor", func.toString().slice(7, -1)).call(
			btn, this.id, "http://www.w3.org/1999/xhtml",
			destructor => win.addEventListener("unload", destructor, {once: true})
		);
		btn.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGwSURBVHjarJPPaxNBGIafmd1km23saqtRaLB6kFYl2ELpRayCVL20RvCmJwk9ePNWvPsXtCA9eNCDeO0hLemPo96LYAuKkRhtsBViEtI0rrt+GyFQ1oMkfjAf883M+8w7w4zyfZ9uwsxms10RzCBNTd3sSLy2lvsDCCKzWMCKGHgotOTAlpamJPm+jCmFL00bmmbD5WlmsKXTdBltB3bcxowaLWJwsTpi4bvNVu1JMmVn5cmcqTB6/DAg3hcjGpUFskjZjlgFKdmvVIiISKlfUhlyQMVPywgDjvX1YCjpOBZ2fZ+9+TuUpxcZuz7Ebl7ArkfM0dgDcOAFCvfwHTi9URJJi2RCqEuPmRxPUXn9imodLo1D6orGjMHW6govZqfDDkYviDXBlZ4vsFsrk8vXqZRWeDY8hz55lvvpGU7sFEn0RhhTtTBg/tETCvlNdj68papsUoND3Lh6iyPOOYrvv/JufYNrqcssbW8zkjwdBnz7/JKL50/x4F4at9GgsFckX/2IefQHZyb6mbl9l2LpE8ff1PnufWkD/t87yC1v/pvi4V8AwZvuNFS33/m3AAMAhEeAiqLmty4AAAAASUVORK5CYII=");
	}
}))(() => {


((main, parts) => this._handleClick = () => {
	var df = MozXULElement.parseXULToFragment(`
		<menupopup>
			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
				label="Сохранить всю страницу как PNG"
				value="all"/>

			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAOwSURBVHjaYmBiYuJvamo+xiIsLLL3uaazMUAAMTY3txxjAAFubq7/ZiYm/wECACIA3f8AGhobzGhoaP8QFRr/6enp/wT5+fgBLS0tAOLi4gCnp6cAAoixtrbuFBsb6x+QNnZ2jo0MXz9/+Qdk/wfhO3fu/Gc5cuwoA4NqHEOoszjDrVu3GFiAogxJtiwMagrCQHMeMgAEoHhsdRCG4Tj463+rxGFIqN2CYI7wBpiaWhzPheF5SMhIKliCYiQtyTC4hvSLcu7cHYQQl5SSDyFGa23UWkfvfXTORWNMpiIYH2NV1xXN5gvq9kfqNjtSStF7mkAhBPTXHm3TYL1qsV2+8LyfIaXEbRjAyuzpUOCcI+cMYgzp31hCnft+fgII6Cm95oiICOd///4CFTAwAOWBgBFIM7Lw8vLZM5w7d+7/t2/fwA4DOfDp02d/QQ4GWv1z6tSpU5g+ffrI8OnTJyagFUx2Tu5MsnIyTDk52UyfPn5k/vXr9x+mjx8+MsTFxTGERMUzVK09yCAsn8AwY+ZMhlevX0Pc9PTZM4a3b94wXHjwmaEsu5th87GpQIcyMdy+fZvh79+/DEwfP35giIuPZ3hzbTeDCtcjhngnEwZVVRWGz58/g+ON5dOnz6BYYKioqGY4eOggg5SuBYORkRHDixcvQL5hZNTR0flnY2PD+O8fKAwYgGHwHyTMwMzMwrBt2zZHgAA9Uz1Lw1AUPWlKE1vFlEIFHRoKQgcRtDSLIAiCgoJuiovYP6DopnR1EdRYHO3W6tbg5K5LwclmVQviRwutpSaQtI3P+yIK7/Eu94t77jkPiqIcGUaZ5vlmlvXFbNui12LEBSPo/uU29/EYrYQ1m01WqVRYPn++FQiHI3Mj8RG4jst7wHFcFAoFaFoGsixBliRoGc338dg3AQuKojc2OoZ+vzcZ4JRwQNQAxVIJsVgMuVwO8/OL2D29xPjSHvpCHAf7+36M57iuSzUdnzISg4d3AvxgVqHrJ5BCIcxu7KCdWoLx3MFLaBjOxDJSC1lIsowzXUfVNFEnJflrJi2i1WrBtm1MpzNgAoN5U8TtySE61Sccb6/jIjuF3uMduo5DOWk/l9fwxQr0o+6J+rSqqlxjQq1WQ9kw8PH2CoF5cLtd8gaRSCSwsrqKZDJJu3A8WZbFer2hB2nLAc5ZNBplHFskEsEmSUcURV8q+GXTH5ffBo1OUzNFGUa7/cm4eGcEIbA2MBBW6df8VnC1s3/zv8mfScchaq+GhgavfwBr4dP0kYqtcwAAAABJRU5ErkJggg=="
				label="Сохранить видимую часть страницы как PNG"
				value="page"/>

			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKaSURBVHjaYpx94sPD44//CDG9+/SDp8bsBw9AADEmrX7zmQEIGF+//fD/w8dPDAABxNix6/nbcA0GIQYoYGJiZGSQFBdmOHnqLAMrKwsDQAAxJq958/n/f4jsm2//GBifP3v+/8cfiMDDj/8ZmHi5ORl4gPjPn18M///9ZWDh5uVm4GJkYfj56zcD9z9GBoAAYmwH2aL8UwimDRk8+fyfgYWRgZFBgI+b4TsWBR+BtoNcxcDDwwW2R1iQn2HvgcMMX799Y/j6/TvDzz+/GVj+/fvPvO0BC8N/oGpGhv8ML3m0GS59EwWbcP/9XwaAAGSRwQ3CMAxFfxq3TgrqiSMswARMwSJMxZEdGAXBAEiQquICJMFuekDqwTlY38/Wi9kf78OqNTO8kndretBh+1puuvl+R8Dpwm9iruG8xPP/tIGzhULc1OB6voJJXxGjQpumGpsKCc8wEqISwGJfA1P1IYCEmCegkTmqLIELT/8KC+/x+cbxBpEPOl8zbn0q5voWXdcipQQrgZiC/QlQORmzNBAEUfjt7N4ichBUDIKCWNsJhiBiZ2mZf5XC0v/gT7C10lqwNMVBkBC9kKAmt7e+uQtJxBS6cNwy7L2b981bTrz/fnmIxnHTYN081i1F8PQacddDzm0M+UfAYPw/gfyTlqMNVfOJVxIGpfxNgMcZRwoYpkVpe1ezLn/jplc7B2wWb+1gwwfuvxg31lwi7MAimOU4E+IevuXoXt+gILTE1aQ1VBJnwNYR9ltXFKCweM9RWFpYCnjWiukU7dYJLs7PUMyKuglNAZt6GVncZ7SgIU2ZJ8M/bLqVQEFYN9hJPQ5201pgvhztjnk2ZhOYh+esd/vY39ZPZYWBXlURqZ4Qwg8uZXU3gM7p3vAbsXveewxHZWUAAAAASUVORK5CYII="
				label="Сохранить выбранный элемент страницы как PNG"
				value="click"/>

			<menuitem class="menuitem-iconic"
				image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAFo9M/3AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAL/SURBVHjaYijccvd25ZHvv5n4RSUVQs2vswAEECOIxwACy+8v/3/30Zv/AAHE0Hb6y+//UPDv4///LD/+MjJ8+PqF4cvnnww/vgkxAAQQY8Xhb78YgQCsj5mFgQGm/P+v///fvvj/n+n/fwaGe//vMexg2Mrw7BsDA8vXHwwMPN/5GdQ/mzG8BbIBAoiRfdqvX8xf/rBCDAEaAWL8ByOgmZwMLBXGvxnrzLgYVj9cyfCL+TfDj9+/Gd7//sAQw5vK8PEXAwMTRBsD0Li3DNKikgwC4twM3NIMDF9/fGf4CVTA8u0fM+ODjwwM7qJZDH8/MTBI/GFgUAba/QYo+fEnAwNAADKoXQVhIAjOJYfgsxIR/AwRi/yBlY3gz9j7FUI+wt7CwkbEzs7CNIo2Yl53t3duElDBhYXZZXaWGbHYKW0M4b+cex7WG9FaxmxGfNcfYNH3T+pr/LcSzu1h3Wx11EU2eOkYYRSi7jVgrQeb1TDtzFHkLgu13GQIBgGkJ6GFxva8h6XqX0kgRlF+AfkEw4S7ujEGbKnAzDa6GNEYLvOh2N6wOUGaVXHKayqQsHeiHgyfqKJ51sbxjvAWoG1qWWkYiKJnZhKTpgapK1tEslRw50+4camIGz/ObfcuBP0HBREEn12I+MBUadPM5HomqbWgi8kJydzHOfdctXmU29Vhbhqq8leNfxQyLP6lWgjjdxds9Yxs95ZRVAo+h8yFqEau2fHEKjpKqgojRDg+H0iw1gb212NY2oC/MCxz5DxGaYgPsKrGsnRYUEusnvKb4MNqnBlN5aZD8q54Hb2gP+gj62bohOyKdVxgobTBk3vE7cMzDhYPkVA0NW0xaBrlk6BZtRN20I16SMIWPt0CJlLQowqJStE2YwqnQRbE2ZwazrYURDquL5zcnCINU4xdiYo7QXXo4TesSMaIGGRTJ6jnqBxvkJNwfhFa2Ovu1jlrwXjD2gZ9wITeKVhImEFXgXcIgrvri/udqySztpDZemJurTzK70h/3h1xY3xpvwGLI42vrCwxmAAAAABJRU5ErkJggg=="
				label="Сохранить выбранную область страницы как PNG"
				value="clipping"/>
		</menupopup>
	`);
	var popup = df.firstChild;
	popup.setAttribute("context", "");

	popup.addEventListener("command", e => popup.handleCommand(e));

	popup.handleCommand = e => {
		var name = _id + ":DataURLReady";
		main = main.replace("%MESSAGE_NAME%", name);

		var urls = {}, configurable = true, enumerable = true;
		Object.entries(parts).forEach(([key, part]) => Object.defineProperty(urls, key, {
			configurable, enumerable, get() {
				var value = `data:;charset=utf-8,({${
					encodeURIComponent(main + part)
				}%0A}).init("${key}")`;
				Object.defineProperty(urls, key, {configurable, enumerable, value});
				return value;
		}}));

		// Получить название вкладки без не сохраняемых символов и лишних пробелов .....
		var getTabLabel = () => {
			var label = gBrowser.selectedTab.label;
			var label = label.replace(/[:+.\\\/<>?*|"]+/g, " ").replace(/\s\s+/g, " ");
			return label.substring(0, 50);
		}

		var listener = msg => {
			var fp = makeFilePicker();
			// fp.init(window, "Сохранить как…", fp.modeSave); // Farby: https://forum.mozilla-russia.org/viewtopic.php?pid=809686#p809686 .....
			fp.init(
				!("inIsolatedMozBrowser" in window.browsingContext.originAttributes)
				  ? window.browsingContext
				  : window
				, "Сохранить как…", fp.modeSave);
			fp.appendFilter("", "*.png");

			var fileName = getTabLabel();
			fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '').replace(/\s+/g, '_').slice(0, 100).replace(/^\s+|\s+$/g, '');
			var fileDate = (function () {
			  var d = new Date(), z = function(n){return (n < 10 ? '0' : '') + n};
			  return '[' + z(d.getFullYear()) + '_' + z(d.getMonth()+1) + '_' + z(d.getDate()) + '\u2014' + z(d.getHours()) + '_' + z(d.getMinutes()) + '_' + z(d.getSeconds()) + ']';
			})();

			fp.defaultString = fileName + "_" + fileDate + ".png";
			fp.open(res => res == fp.returnCancel || !fp.file || makeWebBrowserPersist().saveURI(
				Services.io.newURI(msg.data), document.nodePrincipal,
				null, null, null, null, null, fp.file, null, null
			));
		}
		messageManager.addMessageListener(name, listener);
		addDestructor(() => messageManager.removeMessageListener(name, listener));

		(popup.handleCommand = e => gBrowser.selectedBrowser.messageManager
			.loadFrameScript(urls[e.target.value], false)
		)(e);
	}
	this.append(df);
	(this._handleClick = () => popup.openPopup(this, "after_start"))();
})(`
	init(cmd) {
		cmd.startsWith("c")
			? this[cmd].init(this[cmd].parent = this)
			: this[cmd]();
	},
	capture(win, x, y, width, height) {
		var canvas = win.document.createElementNS("${xhtmlns}", "canvas");
		canvas.width = width;
		canvas.height = height;
		var ctx = canvas.getContext("2d");
		var tryDraw = ind => {
			try {ctx.drawWindow(win, x, y, canvas.width, canvas.height, "white")}
			catch(ex) {canvas.height = ind * canvas.width; tryDraw(--ind);}
		}
		tryDraw(17);
		sendAsyncMessage("%MESSAGE_NAME%", canvas.toDataURL("image/png"));
	},
	`, {

	all: `all() {
		var win = content;
		this.capture(win, 0, 0, win.innerWidth + win.scrollMaxX, win.innerHeight + win.scrollMaxY);
	}`,
	page: `page() {
		var win = content, doc = win.document, body = doc.body, html = doc.documentElement;
		var scrX = (body.scrollLeft || html.scrollLeft) - html.clientLeft;
		var scrY = (body.scrollTop || html.scrollTop) - html.clientTop;
		this.capture(win, scrX, scrY, win.innerWidth, win.innerHeight);
	}`,
	clipping: `clipping: {
		handleEvent(e) {
			if (e.button) return false;
			e.preventDefault();
			e.stopPropagation();
			switch(e.type) {
				case "mousedown":
					this.downX = e.pageX;
					this.downY = e.pageY;
					this.bs.left = this.downX + "px";
					this.bs.top = this.downY + "px";
					this.body.appendChild(this.box);
					this.flag = true;
					break;
				case "mousemove":
					if (!this.flag) return;
					this.moveX = e.pageX;
					this.moveY = e.pageY;
					if (this.downX > this.moveX) this.bs.left = this.moveX + "px";
					if (this.downY > this.moveY) this.bs.top  = this.moveY + "px";
					this.bs.width = Math.abs(this.moveX - this.downX) + "px";
					this.bs.height = Math.abs(this.moveY - this.downY) + "px";
					break;
				case "mouseup":
					this.uninit();
					break;
			}
		},
		init() {
			var win = {};
			Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
				.getFocusedElementForWindow(content, true, win);
			this.win = win.value;

			this.doc = this.win.document;
			this.body = this.doc.body;
			if (!HTMLBodyElement.isInstance(this.body)) {
				Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService)
					.showAlertNotification("${self.image}", ${JSON.stringify(self.label)}, "Не удается захватить!");
				return false;
			}
			this.flag = null;
			this.box = this.doc.createElement("div");
			this.bs = this.box.style;
			this.bs.border = "red dashed 1px";
			this.bs.position = "absolute";
			this.bs.zIndex = "2147483647";
			this.defaultCursor = this.win.getComputedStyle(this.body, "").cursor;
			this.body.style.cursor = "crosshair";
			["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.addEventListener(type, this, true));
		},
		uninit() {
			var pos = [this.win, parseInt(this.bs.left), parseInt(this.bs.top), parseInt(this.bs.width), parseInt(this.bs.height)];
			this.body.style.cursor = this.defaultCursor;
			this.body.removeChild(this.box);
			this.parent.capture.apply(this, pos);
			["click", "mouseup", "mousemove", "mousedown"].forEach(type=> this.doc.removeEventListener(type, this, true));
		}
	}`,
	click: `click: {
		getPosition() {
			var html = this.doc.documentElement;
			var body = this.doc.body;
			var rect = this.target.getBoundingClientRect();
			return [
				this.win,
				Math.round(rect.left) + (body.scrollLeft || html.scrollLeft) - html.clientLeft,
				Math.round(rect.top) + (body.scrollTop || html.scrollTop) - html.clientTop,
				parseInt(rect.width),
				parseInt(rect.height)
			];
		},
		highlight() {
			this.orgStyle = this.target.hasAttribute("style") ? this.target.style.cssText : false;
			this.target.style.cssText += "outline: red 1px solid; outline-offset: 1px; -moz-outline-radius: 2px;";
		},
		lowlight() {
			if (this.orgStyle) this.target.style.cssText = this.orgStyle;
			else this.target.removeAttribute("style");
		},
		handleEvent(e) {
			switch(e.type){
				case "click":
					if (e.button) return;
					e.preventDefault();
					e.stopPropagation();
					this.lowlight();
					this.parent.capture.apply(this, this.getPosition());
					this.uninit();
					break;
				case "mouseover":
					if (this.target) this.lowlight();
					this.target = e.target;
					this.highlight();
					break;
			}
		},
		init() {
			this.win = content;
			this.doc = content.document;
			["click", "mouseover"].forEach(type=> this.doc.addEventListener(type, this, true));
		},
		uninit() {
			this.target = false;
			["click", "mouseover"].forEach(type=> this.doc.removeEventListener(type, this, true));
		}
	}`
});
});


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

Выделить код

Код:

//
(async (id, popup, self) => (self = {

	clickInterval: 5*60,
	intervals: [
		10, 15, 30, 60, 3*60, 5*60, 10*60, 15*60, 30*60, 45*60, 60*60,
	],
	async init() {
		this.addStyle();
		var dsp = e => this[e.type](e);
		var tc = document.getElementById("tabbrowser-tabs");
		ucf_custom_scripts_win[id] = this;

		Object.entries({
			popupshowing: popup,
			TabClose: tc,
			SSTabRestored: tc,
			EndSwapDocShells: document.getElementById("tabbrowser-tabpanels")
		})
			.forEach(([type, trg], ind) => trg.addEventListener(type, dsp, ind == 3));

		await SessionStore.promiseAllWindowsRestored;
		for(var tab of gBrowser.tabs)
			tab.linkedPanel || this.maybeInitTab(tab);
	},
	maybeInitTab(tab) {
		var sec = this.sec(tab);
		sec && this.initTab(tab, sec, true);
	},
	mousedown(e) {
		if (e.button) return;
		e.stopImmediatePropagation();
		self.destroyTab(this.closest("tab"));
	},
	initTab(tab, sec, skipSet) {
		skipSet || SessionStore.setCustomTabValue(tab, id, sec);
		var img = document.createXULElement("hbox");
		img.className = id;
		// img.onmousedown = this.mousedown; // клик по иконке отключает перезагрузку вкладки .....

		tab.throbber.before(img);
		tab.setAttribute(id, setInterval(this.reload, sec * 1e3, tab));
	},
	destroyTab(tab) {
		clearInterval(tab.getAttribute(id));
		SessionStore.deleteCustomTabValue(tab, id);
		tab.removeAttribute(id);
		tab.querySelector("." + id).remove();
	},
	addStyle() {
		var css = `
			tab.tabbrowser-tab[${id}] .${id} {
				width: 16px;
				height: 16px;
				position: relative;
				margin-top: -1px;
				margin-inline-start: -2px;
				margin-inline-end: -14px;
				background-position: top right;
				background-repeat: no-repeat;
				background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEHSURBVHjaYmQAArWgflEglQrEgUCsAsR3gHg9EM++ta7wNSNIASMjQx/rr7fhEd6WrAbaygy3Hr1l2Hjw+u8Xbz+v/P+foYgFZIKEMG94foARKxfTDwZ3G3UGb6Cgj406a2rrhvDnbz5fZwJZ4W+vyRroacdw4MB+hjt37jA8e/aMYd/2NQzOxrKsIHmQIhU1OWGQ0xiSk5MZVFVVGdasWcPg4eHBYKglDxJWAVl3B+gGE5AV8vLyDLdv3wbTrKysDOuOHgMpugMyaT3IkbeBjgVJqKiogOnbUMeD5JmFNT1ufP3+S2b/mfua7z//YH778RvD1iO3GLoXHwH7DqionZGYcAIIMADkw2lofXkQ/wAAAABJRU5ErkJggg==");
				z-index: 1000;
			}
			tab.tabbrowser-tab[${id}]:-moz-locale-dir(rtl) .${id} {
				background-position: top right;
			}
			tab.tabbrowser-tab[${id}] .tab-icon-image {
				display: -moz-box;
			}
			tab.tabbrowser-tab[${id}][pendingicon] .tab-icon-image {
				visibility: hidden;
			}
			#context_autoreloadTab[checked] > menupopup > :nth-child(2),
			#context_autoreloadTab:not([checked]) > menupopup > :first-child {
				display: none;
			}
			#context_autoreloadTab[checked] > .menu-iconic-left > image {
				fill: currentColor;
				-moz-context-properties: fill;
				list-style-image: url("chrome://global/skin/icons/check.svg");
			}
			/*
			tab.tabbrowser-tab[${id}] .tab-throbber,
			tab.tabbrowser-tab[${id}] .tab-icon-pending,
			tab.tabbrowser-tab[${id}]:not([pendingicon]) .tab-icon-image:not([src],[busy],[pinned],[crashed],[sharing]) {
				display: none;
			}
			*/
		`.replace(/;\s*\n/g, " !important;\n");
		windowUtils.loadSheetUsingURIString(
			"data:text/css," + encodeURIComponent(css), windowUtils.USER_SHEET
		);
	},
	get tab() {
		return TabContextMenu.contextTab;
	},
	sec(tab) {
		return SessionStore.getCustomTabValue(tab, id);
	},
	click(menu) {
		var {tab} = this;
		var has = menu.toggleAttribute("checked");
		has
			? this.initTab(tab, this.clickInterval)
			: this.destroyTab(tab);

		var w = menu.clientWidth;
		this.setLabel(has && self.clickInterval);

		if (this.menupopup.state == "open")
			this.updMenupopup(),
			menu.clientWidth != w && setTimeout(this.move, 50);
	},
	changeInterval(tab, sec) {
		clearInterval(tab.getAttribute(id)),
		SessionStore.setCustomTabValue(tab, id, sec),
		tab.setAttribute(id, setInterval(this.reload, sec * 1e3, tab));
	},
	cmd(e) {
		var {value} = e.target;
		if (value == this.currSec) return;

		var {tab} = this;
		this.setLabel(value);

		if (this.menu.hasAttribute("checked"))
			this.changeInterval(tab, value);
		else
			this.menu.toggleAttribute("checked"),
			this.initTab(tab, value);
	},
	reload(tab) {
		gBrowser.reloadTab(tab);
	},
	get shouldHide() {
		return !this.tab.linkedBrowser.currentURI.scheme.startsWith("http");
	},
	format(sec) {
		var map = new Map();
		// resource://gre/modules/PluralForm.jsm
		var f = n => n % 10 == 1 && n % 100 != 11 ? 0 : n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2;
		var hh = ["", "а", "ов"], ms = ["а", "ы", ""];
		return (this.format = sec => {
			var res = map.get(sec = +sec);
			if (!res) {
				var num, arr = [];
				if ((num = Math.floor(sec / 3600)) > 0)
					sec -= num * 3600,
					arr.push(`${num} час${hh[f(num)]}`);
				if ((num = Math.floor(sec / 60)) > 0)
					sec -= num * 60,
					arr.push(`${num} минут${ms[f(num)]}`);
				sec > 0 && arr.push(`${sec} секунд${ms[f(sec)]}`);
				map.set(sec, res = arr.join(" "));
			}
			return res;
		})(sec);
	},
	async prompt(val) {
		var {tab} = this, sec = this.sec(tab);
		var res = await Services.prompt.asyncPrompt(
			null, Services.prompt.MODAL_TYPE_WINDOW,
			val ? "ЕЩЁ РАЗ:" : "Задать интервал обновления",
			"Введите число секунд авто-обновления",
			val || sec || this.clickInterval, null, null
		);
		if (!res.get("ok")) return;

		var val = res.get("value");
		if (!val) return;
		if (!isFinite(val)) return this.prompt(val);

		var val = String(Math.round(val) || 1);
		sec ? this.changeInterval(tab, val) : this.initTab(tab, val);
	},
	initShadowDOM() {
		delete this.initShadowDOM;
		this.initShadowDOM();

		var df = MozXULElement.parseXULToFragment(
			`<menuitem closemenu="single" label="Не перезагружать"/>
			<menuitem closemenu="single" value="${self.clickInterval}"
				label="${self.format(self.clickInterval)}" type="radio"/>
			<menuitem label="Другой…"/>
			<menuseparator/>`
		);
		var {children} = df;
		children[0].addEventListener("command", e => self.menu.click(e.stopPropagation()));
		children[2].addEventListener("command", e => self.prompt(e.stopPropagation()));

		var menuitem = children[1];

		for(var sec of self.intervals) {
			if (sec == self.clickInterval) continue;
			menuitem = menuitem.cloneNode(false);
			menuitem.setAttribute("value", sec);
			menuitem.setAttribute("label", self.format(sec));
			df.append(menuitem);
		}
		this.append(df);
	},
	setLabel(sec) {
		this.menu.setAttribute("label", (this.currSec = sec)
			? `Интервал перезагрузки:   ${this.format(sec)}`
			: "Задать интервал перезагрузки"
		);
	},
	popupshowing(e) {
		if (this.shouldHide) return;

		var df = MozXULElement.parseXULToFragment(
			`<menu id="context_autoreloadTab" class="menu-iconic">
				<menupopup/>
			</menu>`
		);
		var menu = this.menu = df.firstChild;
		menu.onclick = e => e.target == menu && this.click(menu);

		var menupopup = this.menupopup = menu.firstChild;
		menupopup.addEventListener("command", e => this.cmd(e));

		menupopup.initShadowDOM = this.initShadowDOM;
		popup.querySelector("#context_duplicateTab").after(menu);

		this.clickInterval = String(this.clickInterval);
		this.move = () => menupopup.moveToAnchor(menu, "end_before");

		this.updMenupopup = () => {
			var old = menupopup.querySelector("[checked=true]");
			var cur = this.currSec && menupopup.querySelector(`[value="${this.currSec}"]`);
			if (old != cur)
				old?.removeAttribute("checked"),
				cur && cur.setAttribute("checked", true);
		}
		(this.popupshowing = e => {
			if (e.target == popup) {
				if (menu.hidden = this.shouldHide) return;

				var sec = this.sec(this.tab);
				var has = menu.hasAttribute("checked");
				if (Boolean(sec) ^ has)
					has = !has,
					menu.toggleAttribute("checked");

				var curr = has && sec;
				curr !== this.currSec && this.setLabel(curr);
			}
			else if (e.target == menupopup) this.updMenupopup();
		})(e);
	},
	TabClose(e) {
		var intervalId = e.target.getAttribute(id);
		if (!intervalId) return;
		clearInterval(intervalId);

		var tab = e.detail.adoptedBy;
		tab?.ownerGlobal.ucf_custom_scripts_win[id].initTab(tab, this.sec(e.target));
	},
	SSTabRestored(e) {
		var tab = e.target;
		tab.hasAttribute(id) || this.maybeInitTab(tab);
	},
	async EndSwapDocShells(e) {
		var br = e.detail, trg = e.target;
		await new Promise(requestAnimationFrame);

		var win = br.ownerGlobal;
		if (!win.closed) return;
		var tab = win.gBrowser.getTabForBrowser(br);
		if (!tab) return;

		var sec = this.sec(tab);
		if (sec)
			tab = gBrowser.getTabForBrowser(trg),
			tab.hasAttribute(id) || this.initTab(tab, sec);
	}
}).init())("ucf-tab-auto-reload", document.getElementById("tabContextMenu"));

Отредактировано Dumby (08-02-2025 15:28:18)

Отсутствует

 

№187708-02-2025 17:15:34

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 636
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby пишет

unter_officer
Во навалился!

Dumby, всё отлично работает!
Огромное спасибо! drinks.gif


«The Truth Is Out There»

Отсутствует

 

№187814-02-2025 20:59:18

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

Re: UCF - ваши кнопки, скрипты…

ucf_hookClicks 3.9 изменений около 1000, Demo-профиль пашет в новом UCF,
в подсказке кнопки Расширения отображаются версии UCF и ucf_hookClicks


Вывод 7 типов счётчика в любую кнопку, обновление по-умолчанию каждые 30 мин.
1) тип=пуст | учётка не та: количество расширений в кнопке (это по-умолчанию)
2) обратный отсчёт дней до замены пароля, зелёный фон (нужно указать число дней)
3) тип >0 дни, на которые превышен срок пароля, красный
4) тип=S срок от обновления выбранной учётки, серый
5) тип=Р количество паролей/пользователей, синий
6) тип=B количество закладок, голубой
7) aG! если чужой UserAgent, красный
Читаются лишь даты обновления учётной записи! без паролей!


синтаксис опции: тип[:update sec:button] | сайт | имя[:user] | текст меню %s
%s значение счётчика, %d склонение слова день, %u $USER из системы или [:user]
Изменить тип счётчика: нажать 1 сек мышь на кнопке Расширения, открыть опцию
Счётчик обновится через 3 минуты или принудительно: Колёсико в строке Пароли
от типа меняются подсказка кнопки Дополнения, 8-я строка Пароли меню Действия


пример инфы в кнопке "Обновить", вместо %u подстановка $USER из системы
отсчёт от 30 до 0 зелёный фон, затем на красном число дней, когда пароль старше:
"30:60:reload-button | https://accounts.google.com | %u@gmail.com | %s %d лимита"


Бóльшая часть кода взята из форума, я лишь адаптаровал и объединял чужие примеры.

Отредактировано Dobrov (14-02-2025 21:10:03)

Отсутствует

 

№187914-02-2025 23:48:13

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

Re: UCF - ваши кнопки, скрипты…

Dobrov
Поясните  пожалуйста, как скачать ваш демо профиль?

Отсутствует

 

№188017-02-2025 18:55:40

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby посмотрите пожалуйста кнопку

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

Выделить код

Код:

//Восстановить фавиконки закладок
(async (
    id = "ucf-loads-favicons",
    label = "Восстановить фавиконки",
    tooltiptext = "Восстановить фавиконки закладок",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M3.6.6v14.8L8 11l4.4 4.4V.6z'/></svg>",
    maxrequests = 50, // Максимальное количество параллельных запросов
    maxtimeout = 30, // Длительность до прерывания запроса в секундах
    alertnotification = true, // Уведомление о завершении поиска фавиконок для закладок

    favicons = {
        _favrunning: false,
        get alertsService() {
            delete this.alertsService;
            return this.alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
        },
        showAlert(title, val) {
            try {
                this.alertsService.showAlertNotification(null, title, val, false);
            } catch(e) {}
        },
        favSearchStart() {
            if (this._favrunning) return;
            this._favrunning = true;
            this.callWithEachWindow(id, {fill: "color-mix(in srgb, currentColor 20%, #e31b5d)"});
            PlacesUtils.promiseBookmarksTree(PlacesUtils.bookmarks.rootGuid).then(root => {
                var urlsList = [];
                var convert = (node, url) => {
                    if (node.children)
                        node.children.map(convert);
                    else if ((url = node.uri) && /^(?:https?|ftp|file):/.test(url))
                        urlsList.push(url);
                };
                convert(root);
                var favForPage = siteURI => {
                    return new Promise(resolve => {
                        try {
                            siteURI = Services.io.newURI(siteURI);
                        } catch(e) {
                            resolve(null);
                        }
                        PlacesUtils.favicons.getFaviconURLForPage(siteURI, uri => {
                            if (uri === null)
                                resolve(siteURI);
                            else
                                resolve(null);
                        });
                    });
                };
                Promise.all(urlsList.map(favForPage)).then(results => this.favSearchResults(results.filter(url => url !== null)));
            });
        },
        favComplete(favsuccesslength, favmaxlength) {
            this._favrunning = false;
            this.callWithEachWindow(id, {fill: ""});
            if (alertnotification)
                this.showAlert("Поиск фавиконок", `Успешно обработано - ${favsuccesslength}, не удалось обработать - ${favmaxlength - favsuccesslength}`);
        },
        favSearchResults(results) {
            var favmaxlength = results.length;
            var favsuccesslength = 0;
            if (!favmaxlength) {
                this.favComplete(0, 0);
                return;
            }
  var favmaxtimeout = maxtimeout * 1000;
            var _favmaxlength = favmaxlength;
            var splice = results.splice(0, maxrequests);
            var favSearchPage = siteURI => {
                (new Promise(resolve => {
                    try {
                        let req = new XMLHttpRequest();
                        req.mozBackgroundRequest = true;
                        req.open("GET", siteURI.spec, true);
                        req.responseType = "document";
                        req.overrideMimeType("text/html");
                        req.timeout = favmaxtimeout;
                        req.onload = () => {console.log(req)
                            try {
                                let doc = req.responseXML, favURI;
                                if (doc) {
                                    let links = doc.querySelectorAll("head link[href][rel~='icon']"), lastlink, is16, is32, isany;
                                    for (let link of links) {
                                        if (link.sizes.length === 1) {
                                            let size = link.sizes[0];
                                            if (/any/i.test(size))
                                                isany = link;
                                            else if (/32x32/i.test(size))
                                                is32 = link;
                                            else if (/16x16/i.test(size))
                                                is16 = link;
                                        }
                                        lastlink = link;
                                    }
                                    links = isany || is32 || is16 || lastlink;
                                    if (links)
                                        favURI = links.href;
                                }
                                if (!favURI)
                                    favURI = `${req.responseURL ? Services.io.newURI(req.responseURL).prePath : siteURI.prePath}/favicon.ico`;
                                let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
                                let request = PlacesUtils.favicons.setAndFetchFaviconForPage(siteURI, Services.io.newURI(favURI), false, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, {
                                    onComplete() {
                                        ++favsuccesslength;
                                        resolve();
                                        timer.cancel();
                                        timer = null;
                                        request = null;
                                    },
                                }, Services.scriptSecurityManager.getSystemPrincipal());
                                if (!request) {
                                    resolve();
                                    timer = null;
                                    return;
                                }
                                timer.initWithCallback(() => {
                                    resolve();
                                    try {
                                        request.cancel();
                                    } catch(e) {}
                                    timer = null;
                                    request = null;
                                }, favmaxtimeout, timer.TYPE_ONE_SHOT);
                            } catch(e) {
                                resolve();
                            }
                        };
                        req.onabort = () => {
                            resolve();
                        };
                        req.onerror = req.ontimeout = () => {
                            resolve();
                            req.abort();
                        };
                        req.send(null);
                    } catch(e) {
                        resolve();
                    }
                })).then(() => {
                    if (!(--_favmaxlength)) {
                        this.favComplete(favsuccesslength, favmaxlength);
                        return;
                    }
                    if (!results.length) return;
                    favSearchPage(results.shift());
                });
            };
            splice.map(favSearchPage);
        },
        callWithEachWindow(buttonID, atr) {
            var getW = CustomizableUI.getWidget(buttonID);
            if (getW.instances.length)
                for (let {node} of getW.instances) {
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
            else
                for (let win of CustomizableUI.windows) {
                    let node = getW.forWindow(win).node;
                    if (!node) continue;
                    for (let a in atr)
                        node.style.setProperty(a, atr[a]);
                }
        },
    }
) => {Services.io.getProtocolHandler("resource")
    .QueryInterface(Ci.nsIResProtocolHandler)
    .setSubstitution(`${id}-img`, Services.io.newURI(img));
    CustomizableUI.createWidget({
        id,
        label,
        tooltiptext,
        localized: false,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            btn.style.setProperty("list-style-image", `url("resource://${id}-img")`, "important");
            if (favicons._favrunning)
                btn.style.setProperty("fill", "color-mix(in srgb, currentColor 20%, #e31b5d)");
        },
        onCommand(e) {
            favicons.favSearchStart();
        },
    });
})();

она ошибок не выдает но и фавиконки не ищет.В расширении add_toolbar_buttons.2024.10.14.21 от Vitaliy V. эта кнопка  все нормально находит .Я ему написал а в ответ тишина. Он сюда наверно не заходит.

Отсутствует

 

№188117-02-2025 22:35:13

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

Re: UCF - ваши кнопки, скрипты…

egorsemenov06 пишет

В расширении add_toolbar_buttons.2024.10.14.21 от Vitaliy V. эта кнопка  все нормально находит

Ты хочешь, чтобы я из расширения код перерисовал?
Хорошо, попробую.

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

Выделить код

Код:

(async widget => widget = CustomizableUI.createWidget({

	maxtimeout: 30,  // Длительность до прерывания запроса в секундах
	maxrequests: 50, // Максимальное количество параллельных запросов
	alertnotification: true, // Уведомление о завершении поиска фавиконок для закладок
	image: "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M3.6.6v14.8L8 11l4.4 4.4V.6z'/></svg>",

	id: "ucf-loads-favicons",
	label: "Восстановить фавиконки",
	tooltiptext: "Восстановить фавиконки закладок",

	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		this.setFill(btn);
		btn.style.setProperty("list-style-image", this.lsi, "important");
		btn._handleClick = this.favSearchStart;
	},
	get lsi() {
		this.favSearchStart = this.favSearchStart.bind(this);
		ChromeUtils.defineESModuleGetters(this, {
			NetUtil: "resource://gre/modules/NetUtil.sys.mjs"
		});
		var subst = this.id + "-img";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(this.image));
		delete this.lsi;
		return this.lsi = `url("${this.image = "resource://" + subst}")`;
	},
	setFill(btn) {
		this.favrunning
			? btn.style.setProperty("fill", "color-mix(in srgb, currentColor 20%, #e31b5d)")
			: btn.style.removeProperty("fill");
	},
	setBtnsFill() {
		for(var win of CustomizableUI.windows) {
			var btn = widget.forWindow(win).node;
			btn && this.setFill(btn);
		}
	},
	get showAlert() {
		delete this.showAlert;
		return this.showAlert = Cc["@mozilla.org/alerts-service;1"]
			.getService(Ci.nsIAlertsService).showAlertNotification.bind(null, this.image);
	},
	favSearchStart() {
		if (this.favrunning) return;
		this.favrunning = true;
		this.setBtnsFill();
		PlacesUtils.promiseBookmarksTree(PlacesUtils.bookmarks.rootGuid).then(root => {
			var urlsList = [];
			var convert = (node, url) => {
				if (node.children)
					node.children.map(convert);
				else if ((url = node.uri) && /^(?:https?|ftp|file):/.test(url))
					urlsList.push(url);
			};
			convert(root);
			var favForPage = siteURI => {
				return new Promise(resolve => {
					try {
						siteURI = Services.io.newURI(siteURI);
					} catch {
						resolve(null);
					}
					PlacesUtils.favicons.getFaviconURLForPage(siteURI, uri => resolve((uri === null) ? siteURI : null));
				});
			};
			Promise.all(urlsList.map(favForPage)).then(results => this.favSearchResults(results.filter(url => url !== null)));
		});
	},
	favComplete(favsuccesslength, favmaxlength) {
		this.favrunning = false;
		this.setBtnsFill();
		this.alertnotification && this.showAlert(
			"Поиск фавиконок",
			`Успешно обработано - ${favsuccesslength}, не удалось обработать - ${favmaxlength - favsuccesslength}`
		);
	},
	favSearchResults(results) {
		var favmaxlength = _favmaxlength = results.length;
		var favsuccesslength = 0;
		if (!favmaxlength) {
			this.favComplete(0, 0);
			return;
		}
		var {maxrequests} = this;
		var favmaxtimeout = this.maxtimeout * 1000;

		var setFaviconForPage = "setAndFetchFaviconForPage" in PlacesUtils.favicons

			? async (siteURI, favURI) => {
				var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
				var request = PlacesUtils.favicons.setAndFetchFaviconForPage(siteURI, favURI, false, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, {
					onComplete() {
						++favsuccesslength;
						timer.cancel();
						timer = request = null;
					},
				}, Services.scriptSecurityManager.getSystemPrincipal());
				if (!request) {
					timer = null;
					return;
				}
				timer.initWithCallback(() => {
					try {
						request.cancel();
					} catch {}
					timer = request = null;
				}, favmaxtimeout, timer.TYPE_ONE_SHOT);
			}

			: async (siteURI, uri, type) => {
				var resolver = Promise.withResolvers();
				if (uri.schemeIs("data"))
					resolver.resolve(uri);
				else {
					let {NetUtil} = this;
					let channel = NetUtil.newChannel({
						uri,
						loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
						securityFlags:
							Ci.nsILoadInfo.SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT |
							Ci.nsILoadInfo.SEC_COOKIES_INCLUDE |
							Ci.nsILoadInfo.SEC_ALLOW_CHROME |
							Ci.nsILoadInfo.SEC_DISALLOW_SCRIPT,
						contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON,
					});
					NetUtil.asyncFetch(channel, async (input, status, request) => {
						if (!Components.isSuccessCode(status)) {
							resolver.reject(status);
							return;
						}
						try {
							let data = NetUtil.readInputStream(input, input.available());
							let {contentType} = request.QueryInterface(Ci.nsIChannel);
							input.close();
							let buffer = new Uint8ClampedArray(data);
							let blob = new Blob([buffer], {type: type || contentType});
							let dataURL = await new Promise((resolve, reject) => {
								let reader = new FileReader();
								reader.onload = () => resolve(reader.result);
								reader.onerror = e => reject(e);
								reader.readAsDataURL(blob);
							});
							resolver.resolve(Services.io.newURI(dataURL));
						} catch (e) {
							resolver.reject(e);
						}
					});
				}
				try {
					PlacesUtils.favicons.setFaviconForPage(siteURI, uri, await resolver.promise);
					++favsuccesslength;
				} catch {}
			};
		
		var favSearchPage = siteURI => {
			new Promise(resolve => {
				let req = new XMLHttpRequest({mozAnon: false});
				req.mozBackgroundRequest = true;
				req.open("GET", siteURI.spec, true);
				req.responseType = "document";
				req.overrideMimeType("text/html");
				req.timeout = favmaxtimeout;
				req.onload = async () => {
					try {
						let doc = req.responseXML, favURI, favType;
						if (doc) {
							let lastlink, is16, is32, isany;
							for (let link of doc.head.querySelectorAll("link[href][rel~='icon']")) {
								if (link.sizes.length === 1) {
									let size = link.sizes[0];
									if (/any/i.test(size))
										isany = link;
									else if (/32x32/i.test(size))
										is32 = link;
									else if (/16x16/i.test(size))
										is16 = link;
								}
								lastlink = link;
							}
							let icon = (isany || is32 || is16 || lastlink);
							favURI = icon?.href;
							favType = icon?.type;
						}
						if (!favURI) {
							favURI = `${req.responseURL ? Services.io.newURI(req.responseURL).prePath : siteURI.prePath}/favicon.ico`;
							favType = "image/x-icon";
						}
						setFaviconForPage(siteURI, Services.io.newURI(favURI), favType);
					} catch {}
					resolve();
				};
				req.onabort = () => resolve();
				req.onerror = req.ontimeout = () => {
					resolve();
					req.abort();
				};
				req.send(null);
			}).then(() => {
				if (!(--_favmaxlength)) {
					this.favComplete(favsuccesslength, favmaxlength);
					return;
				}
				if (results.length)
					favSearchPage(results.shift());
			});
		};
		results.splice(0, maxrequests).map(favSearchPage);
	}
}))();

Отсутствует

 

№188218-02-2025 00:11:42

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby пишет
egorsemenov06 пишет

В расширении add_toolbar_buttons.2024.10.14.21 от Vitaliy V. эта кнопка  все нормально находит

Ты хочешь, чтобы я из расширения код перерисовал?
Хорошо, попробую.

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

Выделить код

Код:

(async widget => widget = CustomizableUI.createWidget({

	maxtimeout: 30,  // Длительность до прерывания запроса в секундах
	maxrequests: 50, // Максимальное количество параллельных запросов
	alertnotification: true, // Уведомление о завершении поиска фавиконок для закладок
	image: "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M3.6.6v14.8L8 11l4.4 4.4V.6z'/></svg>",

	id: "ucf-loads-favicons",
	label: "Восстановить фавиконки",
	tooltiptext: "Восстановить фавиконки закладок",

	defaultArea: CustomizableUI.AREA_NAVBAR,
	localized: false,
	onCreated(btn) {
		this.setFill(btn);
		btn.style.setProperty("list-style-image", this.lsi, "important");
		btn._handleClick = this.favSearchStart;
	},
	get lsi() {
		this.favSearchStart = this.favSearchStart.bind(this);
		ChromeUtils.defineESModuleGetters(this, {
			NetUtil: "resource://gre/modules/NetUtil.sys.mjs"
		});
		var subst = this.id + "-img";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(this.image));
		delete this.lsi;
		return this.lsi = `url("${this.image = "resource://" + subst}")`;
	},
	setFill(btn) {
		this.favrunning
			? btn.style.setProperty("fill", "color-mix(in srgb, currentColor 20%, #e31b5d)")
			: btn.style.removeProperty("fill");
	},
	setBtnsFill() {
		for(var win of CustomizableUI.windows) {
			var btn = widget.forWindow(win).node;
			btn && this.setFill(btn);
		}
	},
	get showAlert() {
		delete this.showAlert;
		return this.showAlert = Cc["@mozilla.org/alerts-service;1"]
			.getService(Ci.nsIAlertsService).showAlertNotification.bind(null, this.image);
	},
	favSearchStart() {
		if (this.favrunning) return;
		this.favrunning = true;
		this.setBtnsFill();
		PlacesUtils.promiseBookmarksTree(PlacesUtils.bookmarks.rootGuid).then(root => {
			var urlsList = [];
			var convert = (node, url) => {
				if (node.children)
					node.children.map(convert);
				else if ((url = node.uri) && /^(?:https?|ftp|file):/.test(url))
					urlsList.push(url);
			};
			convert(root);
			var favForPage = siteURI => {
				return new Promise(resolve => {
					try {
						siteURI = Services.io.newURI(siteURI);
					} catch {
						resolve(null);
					}
					PlacesUtils.favicons.getFaviconURLForPage(siteURI, uri => resolve((uri === null) ? siteURI : null));
				});
			};
			Promise.all(urlsList.map(favForPage)).then(results => this.favSearchResults(results.filter(url => url !== null)));
		});
	},
	favComplete(favsuccesslength, favmaxlength) {
		this.favrunning = false;
		this.setBtnsFill();
		this.alertnotification && this.showAlert(
			"Поиск фавиконок",
			`Успешно обработано - ${favsuccesslength}, не удалось обработать - ${favmaxlength - favsuccesslength}`
		);
	},
	favSearchResults(results) {
		var favmaxlength = _favmaxlength = results.length;
		var favsuccesslength = 0;
		if (!favmaxlength) {
			this.favComplete(0, 0);
			return;
		}
		var {maxrequests} = this;
		var favmaxtimeout = this.maxtimeout * 1000;

		var setFaviconForPage = "setAndFetchFaviconForPage" in PlacesUtils.favicons

			? async (siteURI, favURI) => {
				var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
				var request = PlacesUtils.favicons.setAndFetchFaviconForPage(siteURI, favURI, false, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, {
					onComplete() {
						++favsuccesslength;
						timer.cancel();
						timer = request = null;
					},
				}, Services.scriptSecurityManager.getSystemPrincipal());
				if (!request) {
					timer = null;
					return;
				}
				timer.initWithCallback(() => {
					try {
						request.cancel();
					} catch {}
					timer = request = null;
				}, favmaxtimeout, timer.TYPE_ONE_SHOT);
			}

			: async (siteURI, uri, type) => {
				var resolver = Promise.withResolvers();
				if (uri.schemeIs("data"))
					resolver.resolve(uri);
				else {
					let {NetUtil} = this;
					let channel = NetUtil.newChannel({
						uri,
						loadingPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
						securityFlags:
							Ci.nsILoadInfo.SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT |
							Ci.nsILoadInfo.SEC_COOKIES_INCLUDE |
							Ci.nsILoadInfo.SEC_ALLOW_CHROME |
							Ci.nsILoadInfo.SEC_DISALLOW_SCRIPT,
						contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE_FAVICON,
					});
					NetUtil.asyncFetch(channel, async (input, status, request) => {
						if (!Components.isSuccessCode(status)) {
							resolver.reject(status);
							return;
						}
						try {
							let data = NetUtil.readInputStream(input, input.available());
							let {contentType} = request.QueryInterface(Ci.nsIChannel);
							input.close();
							let buffer = new Uint8ClampedArray(data);
							let blob = new Blob([buffer], {type: type || contentType});
							let dataURL = await new Promise((resolve, reject) => {
								let reader = new FileReader();
								reader.onload = () => resolve(reader.result);
								reader.onerror = e => reject(e);
								reader.readAsDataURL(blob);
							});
							resolver.resolve(Services.io.newURI(dataURL));
						} catch (e) {
							resolver.reject(e);
						}
					});
				}
				try {
					PlacesUtils.favicons.setFaviconForPage(siteURI, uri, await resolver.promise);
					++favsuccesslength;
				} catch {}
			};
		
		var favSearchPage = siteURI => {
			new Promise(resolve => {
				let req = new XMLHttpRequest({mozAnon: false});
				req.mozBackgroundRequest = true;
				req.open("GET", siteURI.spec, true);
				req.responseType = "document";
				req.overrideMimeType("text/html");
				req.timeout = favmaxtimeout;
				req.onload = async () => {
					try {
						let doc = req.responseXML, favURI, favType;
						if (doc) {
							let lastlink, is16, is32, isany;
							for (let link of doc.head.querySelectorAll("link[href][rel~='icon']")) {
								if (link.sizes.length === 1) {
									let size = link.sizes[0];
									if (/any/i.test(size))
										isany = link;
									else if (/32x32/i.test(size))
										is32 = link;
									else if (/16x16/i.test(size))
										is16 = link;
								}
								lastlink = link;
							}
							let icon = (isany || is32 || is16 || lastlink);
							favURI = icon?.href;
							favType = icon?.type;
						}
						if (!favURI) {
							favURI = `${req.responseURL ? Services.io.newURI(req.responseURL).prePath : siteURI.prePath}/favicon.ico`;
							favType = "image/x-icon";
						}
						setFaviconForPage(siteURI, Services.io.newURI(favURI), favType);
					} catch {}
					resolve();
				};
				req.onabort = () => resolve();
				req.onerror = req.ontimeout = () => {
					resolve();
					req.abort();
				};
				req.send(null);
			}).then(() => {
				if (!(--_favmaxlength)) {
					this.favComplete(favsuccesslength, favmaxlength);
					return;
				}
				if (results.length)
					favSearchPage(results.shift());
			});
		};
		results.splice(0, maxrequests).map(favSearchPage);
	}
}))();

Спасибо Большущие!!!!!

Отсутствует

 

№188319-02-2025 13:12:06

Viatcheslav
Участник
 
Группа: Members
Откуда: г. Бобруйск, Беларусь
Зарегистрирован: 23-11-2016
Сообщений: 325
UA: Firefox 115.0

Re: UCF - ваши кнопки, скрипты…

Garalf пишет

Dobrov
Поясните  пожалуйста, как скачать ваш демо профиль?

https://github.com/VicDobrov/UserChromeFiles/archive/refs/heads/main.zip

Отсутствует

 

№188419-02-2025 16:51:23

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

Re: UCF - ваши кнопки, скрипты…

Viatcheslav
Благодарю!

Отсутствует

 

№188524-02-2025 13:44:15

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby в этой кнопке

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	get initCode() {
		this.event = Object.create(null);
		delete this.initCode;
		return this.initCode = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/ucf-cbbtn-ToggleRestartlessAddons.js"
		));
	},
	get icon() {
		var icon = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path style='fill:none;stroke:context-fill rgb(39, 174, 129);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.9 15.3H3.2c-.88 0-1.6-.6-1.6-1.4v-2.7c0-.4.33-.6.74-.6h1.72c.7 0 1.25-.64 1.25-1.2 0-.64-.55-1.15-1.25-1.15H2.34c-.41 0-.74-.32-.74-.68V5.84c0-.81.72-1.48 1.6-1.48h2.36V3.13c0-1.21.93-2.297 2.21-2.419C9.23.57 10.5 1.62 10.5 2.98v1.38h2.4c.9 0 1.5.67 1.5 1.48v8.06c0 .8-.6 1.4-1.5 1.4z'/></svg>";
		var subst = this.id.toLowerCase() + "-icon";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(icon));
		delete this.icon;
		return this.icon = "resource://" + subst;
	},
	onCreated(btn) {
		btn.setAttribute("image", this.icon);
		new btn.ownerGlobal.Function("self,event,_phase", this.initCode)
			.call(btn, btn, this.event, "init");
	}
}))();

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

Выделить код

Код:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

var ADDONS_URL = "about:addons";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		tab.collapsed = false;

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

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

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

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

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

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

раньше было когда по ней ПКМ кликаешь в начале бегали точки а сейчас пустое место а потом крутяться стрелки.Скажите пожалуйста можно ли вернуть эти бегающие точки?

Отредактировано egorsemenov06 (24-02-2025 18:33:10)

Отсутствует

 

№188624-02-2025 20:43:33

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

Re: UCF - ваши кнопки, скрипты…

egorsemenov06
Ну, раньше был
chrome://browser/skin/tabbrowser/tab-connecting.png
а теперь нет.


Можно заменить в коде на тот, который был раньше.
В смысле вытащить из Firefox, в котором ещё есть.
Вот из 115, например

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

Выделить код

Код:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAACGFjVEwAAAA8AAAAAO2clZcAAAJAUExURQAAAFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVFJSVJYMuIkAAAC/dFJOUwABAgMEBQYHCAoLDA0ODxASExUXGRocHR4fICIjJCUmKCkrLC0uLzAxMjM0Njc4OTs8P0BCREVGR0lKS01OUVNVVldZWltdXl9iY2VmaGprbW5wcnR1d3h5e3x+f4CBgoOEh4iJiouMjo+Rk5WXmZqbnJ2en6CjpKWmp6ipqqusrq+xsrS2t7i5u76/wcPExcbJysvMzc/R09TX2Nna29zd3t/h4uPk5ebn6Onq6+3u7/Dx8vP19vf4+fr7/P3+jK1v1gAAABpmY1RMAAAAAAAAABAAAAAQAAAAAAAAAAAAAgBkAABmr1PfAAAAHklEQVQY02NgoAuwXrXR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

Отсутствует

 

№188724-02-2025 21:17:48

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby пишет

egorsemenov06
Ну, раньше был
chrome://browser/skin/tabbrowser/tab-connecting.png
а теперь нет.


Можно заменить в коде на тот, который был раньше.
В смысле вытащить из Firefox, в котором ещё есть.
Вот из 115, например

Спасибо но на табах при загрузке страницы есть эти бегающие точки значит они где-то лежат в omni.ja я их не нашел.Посмотрите пожалуйста еще разок может можно сделать без этого base64

Отредактировано egorsemenov06 (24-02-2025 21:32:35)

Отсутствует

 

№188825-02-2025 12:00:45

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

Re: UCF - ваши кнопки, скрипты…

egorsemenov06 пишет

Спасибо но на табах при загрузке страницы есть эти бегающие точки значит они где-то лежат в omni.ja я их не нашел

Есть и лежат
chrome://browser/skin/tabbrowser/loading.svg
но теперь сами по себе без стиля "не бегают".

Отсутствует

 

№188925-02-2025 22:17:55

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

xrun1 пишет
egorsemenov06 пишет

Спасибо но на табах при загрузке страницы есть эти бегающие точки значит они где-то лежат в omni.ja я их не нашел

Есть и лежат
chrome://browser/skin/tabbrowser/loading.svg
но теперь сами по себе без стиля "не бегают".

тогда скорее всего это loading-burst.svg.Ну а стиля у вас конечно же нет.Может мегамозг-Dumby придумает стиль для этой кнопки.

Отредактировано egorsemenov06 (25-02-2025 23:40:09)

Отсутствует

 

№189025-02-2025 23:43:06

unter_officer
Участник
 
Группа: Members
Откуда: Санкт-Петербург
Зарегистрирован: 27-03-2011
Сообщений: 636
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

egorsemenov06 пишет

может можно сделать без этого base64

egorsemenov06 пишет

Ну а стиля у вас конечно же нет.

Чтобы не городить стили в скрипте, можно попробовать так.
Сохраните себе на комп иконку, которую вам дал Dumby.
Положите её рядом со скриптом, а в скрипте укажите к ней путь, например: chrome://user_chrome_files/content/custom_scripts/custom_script/tab-connecting.png

Отредактировано unter_officer (25-02-2025 23:44:30)


«The Truth Is Out There»

Отсутствует

 

№189125-02-2025 23:56:46

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

unter_officer пишет
egorsemenov06 пишет

может можно сделать без этого base64

egorsemenov06 пишет

Ну а стиля у вас конечно же нет.

Чтобы не городить стили в скрипте, можно попробовать так.
Сохраните себе на комп иконку, которую вам дал Dumby.
Положите её рядом со скриптом, а в скрипте укажите к ней путь, например: chrome://user_chrome_files/content/custom_scripts/custom_script/tab-connecting.png

Именно так я и сделал,но хотелось бы что бы все было в скрипте в одном месте.и потом на темной теме не видно бегающих точек.А все должно быть по фэншую.

Отсутствует

 

№189226-02-2025 09:11:38

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

Re: UCF - ваши кнопки, скрипты…

egorsemenov06 пишет

тогда скорее всего это loading-burst.svg.Ну а стиля у вас конечно же нет.

Не-не, xrun1 всё правильно сказал.

на темной теме не видно бегающих точек

Это аргумент.


Тогда, можно так попробовать.
В коде есть конструктор


function ProgressIcon(btn) {
    …
}


Заменить его на такой.
(совместимость идёт лесом)

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

Выделить код

Код:

function ProgressIcon(btn) {
	var {icon} = btn, origIcon = icon.src;

	var box = btn.ownerDocument.createXULElement("hbox");
	box.toggleAttribute("busy");
	box.toggleAttribute("fadein");
	box.className = "tab-throbber";

	btn.style.setProperty(
		"min-width", btn.getBoundingClientRect().width + "px", "important"
	);
	icon.replaceWith(box);

	this.loading = () => {
		box.replaceWith(icon);
		btn.style.removeProperty("min-width");
		icon.src = "chrome://global/skin/icons/loading.svg";
	}
	this.restore = () => icon.src = origIcon;
}

Отсутствует

 

№189326-02-2025 09:52:00

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby пишет

Заменить его на такой.

Спасибо Большое.!!!!!!теперь без этих точек и пустого места по фэншую.Правда кнопка на мгновение пропадает а потом появляеться на панели.Конда меняешь тему становиться все четко до выхода из браузера.Незнаю это нормальное поведение кнопки или нет?Можно ли исправить это поведение?
bc78ee9736495cd25da80265d4deab58.gif

Отредактировано egorsemenov06 (26-02-2025 19:00:16)

Отсутствует

 

№189427-02-2025 18:05:16

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

Re: UCF - ваши кнопки, скрипты…

egorsemenov06
Копнул поглубже, и получается, что в таком виде,
кнопка какая-то недоадаптированная под UCF.


Надо заменить первый код,
тот, который создаёт виджет, на такой

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

Выделить код

Код:

(async () => CustomizableUI.createWidget({
	label: "Дополнения",
	id: "ucf-cbbtn-ToggleRestartlessAddons",
	localized: false,
	onCreated(btn) {
		var icon = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path style='fill:none;stroke:context-fill rgb(39, 174, 129);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M12.9 15.3H3.2c-.88 0-1.6-.6-1.6-1.4v-2.7c0-.4.33-.6.74-.6h1.72c.7 0 1.25-.64 1.25-1.2 0-.64-.55-1.15-1.25-1.15H2.34c-.41 0-.74-.32-.74-.68V5.84c0-.81.72-1.48 1.6-1.48h2.36V3.13c0-1.21.93-2.297 2.21-2.419C9.23.57 10.5 1.62 10.5 2.98v1.38h2.4c.9 0 1.5.67 1.5 1.48v8.06c0 .8-.6 1.4-1.5 1.4z'/></svg>";
		var subst = this.id.toLowerCase() + "-icon";
		Services.io.getProtocolHandler("resource")
			.QueryInterface(Ci.nsIResProtocolHandler)
			.setSubstitution(subst, Services.io.newURI(icon));
		icon = "resource://" + subst;

		var e = Object.create(null);
		var ael = (t, l, c, trg) => trg.addEventListener(t, l, c);
		var code = Cu.readUTF8URI(Services.io.newURI(
			"chrome://user_chrome_files/content/custom_scripts/custom_script/ucf-cbbtn-ToggleRestartlessAddons.js"
		));

		(this.onCreated = async btn => {
			btn.image = icon;
			var win = btn.ownerGlobal;
			await new Promise(win.setTimeout);
			new win.Function("self,event,_phase,addEventListener", code)
				.call(btn, btn, e, "init", ael);
		})(btn);
	}
}))();

Можно ли исправить это поведение?

Не знаю. Точки должны быть. Ну или серые песочные часы,
зависит от результирующей prefers-reduced-motion.
Впрочем, может и не быть, если вкладка about:addons уже есть открытая.


Но да, на вертикальном тулбаре,
высота кнопки проседает, поэтому лучше подморозить и её.

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

Выделить код

Код:

function ProgressIcon(btn) {
	var {icon} = btn, origIcon = icon.src;

	var box = btn.ownerDocument.createXULElement("hbox");
	box.toggleAttribute("busy");
	box.toggleAttribute("fadein");
	box.className = "tab-throbber";
	box.style.setProperty("pointer-events", "none", "important");

	var s = btn.style, r = btn.getBoundingClientRect();
	s.setProperty("min-width", r.width + "px", "important");
	s.setProperty("min-height", r.height + "px", "important");
	Object.defineProperty(btn, "open", {configurable: true});

	icon.replaceWith(box);
	icon.src = "chrome://global/skin/icons/loading.svg";

	this.loading = () => {
		box.replaceWith(icon);
		s.removeProperty("min-width");
		s.removeProperty("min-height");
	}
	this.restore = () => {
		icon.src = origIcon;
		delete btn.open;
	}
}

Отредактировано Dumby (27-02-2025 18:38:50)

Отсутствует

 

№189527-02-2025 18:53:09

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby пишет

Не знаю. Точки должны быть. Ну или серые песочные часы,
зависит от результирующей prefers-reduced-motion.
Впрочем, может и не быть, если вкладка about:addons уже есть открытая.

Спасибо большое!!!Все работает!!!От души!!!
и еще вопрос я вот заменил в этой же кнопке
if(!icon && mp.icons.useSVG)
mi.style.fill = "#15c";
на
if(!icon && mp.icons.useSVG)
mi.style.fill = "currentColor";
что бы в темной теме видно было иконки плагинов.
правильно ли я сделал?

Отредактировано egorsemenov06 (28-02-2025 11:32:09)

Отсутствует

 

№189601-03-2025 18:45:45

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby можно зарегистрировать эту кнопку в resource? 

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

Выделить код

Код:

(async url => {

	var path = "D:\\Portateble Program\\Mozilla Firefox\\Profiles\\opera-proxy.windows-386.lnk";

	var icons = [
		"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='rgb(39, 174, 129)'><path d='M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-8 3a3 3 0 1 0 0-6 3 3 0 0 0 0 6z'/></svg>",
		"data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='rgb(142, 142, 152)'><path d='M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-8 3a3 3 0 1 0 0-6 3 3 0 0 0 0 6z'/></svg>"
	];
	var labels = [
		"Active",
		"Not Active"
	];
	var tooltips = [
		"Active",
		"Not Active"
	];

	var type = "network.proxy.type";
	try {var exp = ChromeUtils.importESModule(url + "sys.mjs");}
	catch {exp = ChromeUtils.import(url + "jsm");}

	exp.PageActions.addAction(new exp.PageActions.Action({
		title: labels[1],
		iconURL: icons[1],
		pinnedToUrlbar: true,
		id: "ucf-opera-proxy",
		onPlacedInUrlbar(node) {
			var pref = Services.prefs.getIntPref(type) == 1, proc, active;
			var upd = () => {
				var state = pref && Boolean(proc);
				if (state == active) return;

				var ind = +!(active = state);
				this.setIconURL(icons[ind]);
				this.setTitle(labels[ind]);
				this.setTooltip(tooltips[ind]);
			}
			this["nsPref:changed"] = () => upd(
				pref = Services.prefs.getIntPref(type) == 1
			);
			this["process-finished"] = this["process-failed"] = () => upd(proc = null);

			this["quit-application-granted"] = t => {
				Services.obs.removeObserver(this, t);
				Services.prefs.removeObserver(type, this);
				active && Services.prefs.setIntPref(type, 0);
				proc?.kill();
			}
			this.observe = (s, topic) => this[topic](topic);
			Services.prefs.addObserver(type, this);
			Services.obs.addObserver(this, "quit-application-granted");

			var {id} = node;
			var style = `#${id} {display: flex !important;}\n` +
				`@media (max-width: 680px) {#${id} {visibility: collapse !important;}}`;

			(this.onPlacedInUrlbar = this._onPlacedInUrlbar = node => {
				var sheet = new node.ownerGlobal.CSSStyleSheet();
				sheet.replaceSync(style);
				node.ownerDocument.adoptedStyleSheets.push(sheet);
			})(node);

			var run = () => {
				var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
				file.initWithPath(path);
				(run = () => {
					proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
					//proc.startHidden = true;
					try {proc.init(file); proc.runwAsync([], 0, this);}
					catch {proc = null;}
					upd();
				})();
			}

			var timeout = 3000;

			var tid, oldState, reload = (gb, tab, state) => {
				tid = null;
				active != state && gb.reloadTab(tab);
			}
			this._onCommand = e => {
				tid ? clearTimeout(tid) : oldState = active;
				var gb = e.view.gBrowser;
				tid = setTimeout(reload, timeout, gb, gb.selectedTab, oldState);
				if (active) Services.prefs.setIntPref(type, 0),  proc.kill();
				else pref || Services.prefs.setIntPref(type, 1), proc || run();
			}
		}
	}));
})("resource:///modules/PageActions.");

.Пожалуйста посмотрите предыдущие сообщение

Отредактировано egorsemenov06 (01-03-2025 18:46:39)

Отсутствует

 

№189701-03-2025 23:45:28

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

Re: UCF - ваши кнопки, скрипты…

egorsemenov06 пишет

можно зарегистрировать эту кнопку в resource?

А почему нет, всё одно и то же.
Куда-нибудь после icons, но до добавления page action

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

Выделить код

Код:

//
	var rph = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
	icons.forEach((icon, ind) => {
		var subst = "ucf-opera-proxy-icon-" + ind;
		rph.setSubstitution(subst, Services.io.newURI(icon));
		icons[ind] = "resource://" + subst;
	});

Пожалуйста посмотрите предыдущие сообщение

Я его не понял.
У тебя в коде торчит
chrome://global/skin/plugins/pluginGeneric.svg


Но его давно уже нет.
Тогда надо ещё что-то типа этого, не уверен

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

Выделить код

Код:

/*
	get plugin() {
		delete this.plugin;
		return this.plugin = this.useSVG
			? this.platformVersion >= 65
				? "chrome://global/skin/plugins/pluginGeneric.svg"
				: "chrome://mozapps/skin/plugins/pluginGeneric.svg"
			: "chrome://mozapps/skin/plugins/pluginGeneric-16.png";
	},
*/
	get plugin() {
		delete this.plugin;
		var v = this.useSVG && this.platformVersion;
		return this.plugin =
			v >= 88 && "chrome://global/skin/icons/plugin.svg" ||
			v >= 81 && "chrome://global/skin/plugins/plugin.svg" ||
			v >= 65 && "chrome://global/skin/plugins/pluginGeneric.svg" ||
			v >= 57 && "chrome://mozapps/skin/plugins/pluginGeneric.svg" ||
			"chrome://mozapps/skin/plugins/pluginGeneric-16.png";
	},

Отсутствует

 

№1898Вчера 08:43:02

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 135.0

Re: UCF - ваши кнопки, скрипты…

Dumby пишет
egorsemenov06 пишет

можно зарегистрировать эту кнопку в resource?

А почему нет, всё одно и то же.
Куда-нибудь после icons, но до добавления page action

Пожалуйста посмотрите предыдущие сообщение

Я его не понял.
У тебя в коде торчит
chrome://global/skin/plugins/pluginGeneric.svg


Но его давно уже нет.
Тогда надо ещё что-то типа этого, не уверен

СПАСИБИЩЕ БОЛЬШУЩИЕ.!!!!!!!

Отсутствует

 

№1899Сегодня 21:24:04

egorsemenov06
Участник
 
Группа: Members
Зарегистрирован: 23-07-2024
Сообщений: 27
UA: Firefox 136.0

Re: UCF - ваши кнопки, скрипты…

Dumby посмотрите пожалуйста кнопку

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

Выделить код

Код:

//Очистить историю
(async (
    id = "ucf-clear-history",
    label = "Очистить историю",
    tooltiptext = "Очистить историю",
    img = "data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path style='fill:none;stroke:context-fill rgb(142, 142, 152);stroke-opacity:context-fill-opacity;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;' d='M5.5.7c1.1 0 1.1 1.6 0 1.6S4.4.7 5.5.7zm8.3.5L9.5 5.5s-1.24.1-2.68.12C5.18 7.15 4.12 7.66.822 8.01v1.2C3.35 13.3 4.97 15.2 9.21 15.2h1.09c1.4-1.7 2-2.9 2.5-4.9V9.21c-.5-1.19-1.6-2.41-1.6-2.41l4.1-4.1zm-10.3 2c1.7 0 1.7 2.6 0 2.6s-1.7-2.6 0-2.6z'/></svg>",
) => { Services.io.getProtocolHandler("resource")
    .QueryInterface(Ci.nsIResProtocolHandler)
    .setSubstitution(`${id}-img`, Services.io.newURI(img));
    CustomizableUI.createWidget({
        id,
        label,
        tooltiptext,
        defaultArea: CustomizableUI.AREA_NAVBAR,
        onCreated(btn) {
            btn.style.setProperty("list-style-image", `url("resource://${id}-img")`, "important");
        },
        onCommand(e) {
            var win = e.view;
            var itemsToClear = [
			    "historyFormDataAndDownloads",
                "history",
                "sessions",
                "formdata",
                "cache",
                "downloads",
                "offlineApps",
                "pluginData"
            ];
            var range = win.Sanitizer.getClearRange(0);
            win.Sanitizer.sanitize(itemsToClear, {
                ignoreTimespan: !range,
                range,
            }).then(() => {
                var alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
                alertsService.showAlertNotification(null, "История Очищена!", "", false);
                win.setTimeout(()=> alertsService.closeAlert(), 2000);
            });
        },
    });
})();

в [firefox] 136.0 выдает вот такую ошибку
Error sanitizing historyFormDataAndDownloads TypeError: item is undefined
    sanitizeInternal resource:///modules/Sanitizer.sys.mjs:998
    sanitize resource:///modules/Sanitizer.sys.mjs:329
    onCommand chrome://user_chrome_files/content/custom_scripts/custom_script/ucf-clear-history.js:31
    doWidgetCommand resource:///modules/CustomizableUI.sys.mjs:2293
    handleWidgetCommand resource:///modules/CustomizableUI.sys.mjs:2350
Sanitizer.sys.mjs:983:13
и нет уведомления

Отсутствует

 

№1900Сегодня 22:13:54

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

Re: UCF - ваши кнопки, скрипты…

egorsemenov06
а чем не кошерна штатная кнопка "Забыть о части истории" или меню "Удалить недавнюю историю" по Ctrl+Shift+Del ?
Есть ещё "Автоматическое удаление истории", и всё это без сторонних костылей…

Отсутствует

 

Board footer

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