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)
Отсутствует
unter_officer
Во навалился!
Dumby, всё отлично работает!
Огромное спасибо!
«The Truth Is Out There»
Отсутствует
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)
Отсутствует
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. эта кнопка все нормально находит
Ты хочешь, чтобы я из расширения код перерисовал?
Хорошо, попробую.
(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); } }))();
Отсутствует
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); } }))();
Спасибо Большущие!!!!!
Отсутствует
Dobrov
Поясните пожалуйста, как скачать ваш демо профиль?
https://github.com/VicDobrov/UserChromeFiles/archive/refs/heads/main.zip
Отсутствует
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)
Отсутствует
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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
Отсутствует
egorsemenov06
Ну, раньше был
chrome://browser/skin/tabbrowser/tab-connecting.png
а теперь нет.Можно заменить в коде на тот, который был раньше.
В смысле вытащить из Firefox, в котором ещё есть.
Вот из 115, например
Спасибо но на табах при загрузке страницы есть эти бегающие точки значит они где-то лежат в omni.ja я их не нашел.Посмотрите пожалуйста еще разок может можно сделать без этого base64
Отредактировано egorsemenov06 (24-02-2025 21:32:35)
Отсутствует
Спасибо но на табах при загрузке страницы есть эти бегающие точки значит они где-то лежат в omni.ja я их не нашел
Есть и лежат
chrome://browser/skin/tabbrowser/loading.svg
но теперь сами по себе без стиля "не бегают".
Отсутствует
egorsemenov06 пишетСпасибо но на табах при загрузке страницы есть эти бегающие точки значит они где-то лежат в omni.ja я их не нашел
Есть и лежат
chrome://browser/skin/tabbrowser/loading.svg
но теперь сами по себе без стиля "не бегают".
тогда скорее всего это loading-burst.svg.Ну а стиля у вас конечно же нет.Может мегамозг-Dumby придумает стиль для этой кнопки.
Отредактировано egorsemenov06 (25-02-2025 23:40:09)
Отсутствует
может можно сделать без этого base64
Ну а стиля у вас конечно же нет.
Чтобы не городить стили в скрипте, можно попробовать так.
Сохраните себе на комп иконку, которую вам дал 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»
Отсутствует
egorsemenov06 пишетможет можно сделать без этого base64
egorsemenov06 пишетНу а стиля у вас конечно же нет.
Чтобы не городить стили в скрипте, можно попробовать так.
Сохраните себе на комп иконку, которую вам дал Dumby.
Положите её рядом со скриптом, а в скрипте укажите к ней путь, например: chrome://user_chrome_files/content/custom_scripts/custom_script/tab-connecting.png
Именно так я и сделал,но хотелось бы что бы все было в скрипте в одном месте.и потом на темной теме не видно бегающих точек.А все должно быть по фэншую.
Отсутствует
тогда скорее всего это 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; }
Отсутствует
Заменить его на такой.
Спасибо Большое.!!!!!!теперь без этих точек и пустого места по фэншую.Правда кнопка на мгновение пропадает а потом появляеться на панели.Конда меняешь тему становиться все четко до выхода из браузера.Незнаю это нормальное поведение кнопки или нет?Можно ли исправить это поведение?
Отредактировано egorsemenov06 (26-02-2025 19:00:16)
Отсутствует
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)
Отсутствует
Не знаю. Точки должны быть. Ну или серые песочные часы,
зависит от результирующей 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)
Отсутствует
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)
Отсутствует
можно зарегистрировать эту кнопку в 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"; },
Отсутствует
egorsemenov06 пишетможно зарегистрировать эту кнопку в resource?
А почему нет, всё одно и то же.
Куда-нибудь после icons, но до добавления page actionПожалуйста посмотрите предыдущие сообщение
Я его не понял.
У тебя в коде торчит
chrome://global/skin/plugins/pluginGeneric.svgНо его давно уже нет.
Тогда надо ещё что-то типа этого, не уверен
СПАСИБИЩЕ БОЛЬШУЩИЕ.!!!!!!!
Отсутствует