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

Заказывай стафф с атрибутикой Mozilla и... пусть все вокруг завидуют тебе! Быть уникальным - быть с Mozilla!

№1442608-03-2020 13:43:29

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

Re: Custom Buttons

Dumby пишет

Замысел непонятен

Если тестирую браузер нужен AGENT_SHEET, если сайт - USER_SHEET (как то так, а с этим вроде бы все тестируется)

Dumby пишет

Хорошо бы посмотреть

Исправляю очень старый скрипт, пока на полпути

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

Выделить код

Код:

// ==UserScript==
// @name           SaveUserChromeJS.uc.js
// @author         ywzhaiqi
// @description    установка контекстного меню кнопок на github для скачивания скриптов
// @include        main
// @charset        UTF-8
// @version        0.4b
// @homepageURL    https://github.com/ywzhaiqi/userChromeJS/tree/master/SaveUserChromeJS
// @reviewURL      http://bbs.kafan.cn/thread-1590873-1-1.html
// ==/UserScript==

(function() {

// Включены ли уведомления после сохранения?
var notificationsAfterInstall = true;

// Загружается ли скрипт после сохранения (запускать не нужно)? Поддерживается только .uc.js, некоторые скрипты проблематичны.
var runWithoutRestart = false;


let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
if (!window.Services) Cu.import("resource://gre/modules/Services.jsm");

if(typeof window.saveUserChromeJS != "undefined"){
    window.saveUserChromeJS.uninit();
    delete window.saveUserChromeJS;
}

const RE_USERCHROME_JS = /\.uc(?:-\d+)?\.(?:js|xul)$|userChrome\.js$/i;
const RE_CONTENTTYPE = /text\/html/i;

var ns = window.saveUserChromeJS = {
    

    init: function() {
           // добавить в contentAreaContextMenu
              if ( document.getElementById("uc-install-menu") ) return; 
              var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
              var icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAAICAAP//AICAgMDAwP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcj7VkAAABfdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Ak5pfLAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAFxJREFUKFNljgEOwCAIAwuF/395RWXMrDExHLQA3gKYpVXojwPo6SpewDSzAHGAXvWpkQYagNSWN2EyfGUM+G/RQWZFOnSt9AZMR3nGwggivqF1Q12xQVzavRHwALQIAv2bDRzYAAAAAElFTkSuQmCC";
        var contextMenu = document.getElementById("contentAreaContextMenu");
        var menuitem = document.createElementNS(xulns, "menuitem");
        menuitem.setAttribute("id", "uc-install-menu");
        menuitem.setAttribute("label", "Установить для userChromeJS");
        menuitem.setAttribute("class", "menuitem-iconic");
        menuitem.setAttribute("image", icon);
        menuitem.setAttribute("accessKey", "I");
        menuitem.setAttribute("oncommand", "saveUserChromeJS.saveScript(gContextMenu.linkURL)");

        contextMenu.insertBefore(menuitem, contextMenu.firstChild);
        contextMenu.addEventListener("popupshowing", this, false);
        this._menuitem = menuitem;
    },
    handleEvent: function(event){
        switch(event.type){
            case "popupshowing":
                if (event.target != event.currentTarget) return;
                if(gContextMenu.onLink){
                    this._menuitem.hidden = !RE_USERCHROME_JS.test(gContextMenu.linkURL);
                }else{
                    this._menuitem.hidden = true;
                }
                break;
        }
    },
    
    observe: function(aSubject, aTopic, aData) {
        switch (aTopic) {
            case "content-document-global-created":
                let safeWin = aSubject;
                let chromeWin = this.getBrowserForContentWindow(safeWin).wrappedJSObject;
                if (!chromeWin) return;

                let gBrowser = chromeWin.gBrowser;
                if (!gBrowser) return;

                let lhref = safeWin.location.href;
                if(lhref.startsWith("view-source")) return;

                // Показать баннер установки scriptish, если пользователь переходит к .user.js
                // файл в top-level tab.
                if (safeWin === safeWin.top && RE_USERCHROME_JS.test(lhref) && !RE_CONTENTTYPE.test(safeWin.document.contentType)) {
                    safeWin.setTimeout(function(){
                        ns.showInstallBanner(gBrowser.getBrowserForDocument(safeWin.document));
                    }, 500);
                }

                if(safeWin.location.hostname == 'github.com'){
                    safeWin.addEventListener("DOMContentLoaded", function(){
                        ns.github_addButton(safeWin.document);

                        // github с history.pushstate, нужно добавить кнопку еще раз после загрузки страницы
                        // 2014-7-15:firefox 33(nightly)если цитируется unsafeWindow.$ это потерпит крах
                        if (Services.appinfo.version < 33) {
                            ns.github_addListener(safeWin);
                        }                    
                        var sWBrowser = gBrowser.getBrowserForContentWindow(safeWin);
                        if (!sWBrowser.ProgListener) {
                           sWBrowser.ProgListener = {
                              QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]),
                              onLocationChange: function() {
                                 safeWin.setTimeout(function() {
                                    ns.github_addButton(safeWin.document);
                                 }, 0);
                              }
                           };
                        };
                        try {
                           sWBrowser.addProgressListener(sWBrowser.ProgListener, Ci.nsIWebProgress.NOTIFY_LOCATION);
                           safeWin.addEventListener('beforeunload', function() {
                              sWBrowser.removeProgressListener(sWBrowser.ProgListener);
                           });
                        } catch(e) { };
                    }, false);
                }

                break;
        }
    },
    

    showInstallBanner: function(browser) {
        var notificationBox = gBrowser.getNotificationBox(browser);
        var greeting = "Das ist ein userChrome Script. Klicken Sie auf Installieren, um es zu verwenden. Nach dem Speichern im Chrome Ordner bitte einen Neustart durchführen.";
        var btnLabel = "Installieren";

        // Удалить существующие уведомления. Уведомления удаляются
        // автоматически по клику и по странице навигации, но нам нужно удалить
        // их самим в случае перезагрузки, или они складываются.
        for (var i = 0, child; child = notificationBox.childNodes[i]; i++)
            if (child.getAttribute("value") == "install-userChromeJS")
                notificationBox.removeNotification(child);

        var notification = notificationBox.appendNotification(
            greeting,
            "install-userChromeJS",
            null,
            notificationBox.PRIORITY_WARNING_MEDIUM, [{
                label: btnLabel,
                accessKey: "I",
                popup: null,
                callback: this.saveCurrentScript
            }
        ]);
    },
    github_addButton: function(doc){
        if(doc.getElementById("uc-install-button")) return;

        var rawBtn = doc.getElementById("raw-url");
        if(!rawBtn) return;

        var downURL = rawBtn.href;
        if(!RE_USERCHROME_JS.test(downURL)) return;

        var installBtn = doc.createXULElement("a");
        installBtn.setAttribute("id", "uc-install-button");
        installBtn.setAttribute("class", "btn btn-sm");
        installBtn.setAttribute("href", downURL);
        installBtn.innerHTML = "Installieren";
        installBtn.addEventListener("click", function(event){
            event.preventDefault();
            ns.saveScript(downURL);
        }, false);

        rawBtn.parentNode.insertBefore(installBtn, rawBtn);
    },
    github_addListener: function(win){
        var script = '\
            (function(){\
                var $ = unsafeWindow.jQuery;\
                if(!$) return;\
                $(document).on("pjax:success", function(){\
                    github_addButton(document);\
                });\
            })();\
        ';
        let sandbox = new Cu.Sandbox(win, {sandboxPrototype: win});
        sandbox.unsafeWindow = win.wrappedJSObject;
        sandbox.document     = win.document;
        sandbox.window       = win;
        sandbox.github_addButton = ns.github_addButton;
        Cu.evalInSandbox(script, sandbox);
    },
    saveCurrentScript: function(event){
        ns.saveScript();
    },
    saveScript: function(url) {
        var win = ns.getFocusedWindow();

        var doc, name, fileName, fileExt, charset;
        if(!url){
            url = win.location.href;
            doc = win.document;
            name = doc.body.textContent.match(/\/\/\s*@name\s+(.*)/i);
            charset = doc.body.textContent.match(/\/\/\s*@charset\s+(.*)/i);
        }

        name = name && name[1] ? name[1] : decodeURIComponent(url.split("/").pop());
        fileName = name.replace(/\.uc\.(js|xul)$|$/i, ".uc.$1").replace(/\s/g, '_');
        if (fileName.match(/\.uc\.$/i)) {  // Исправить имя
            var m = url.match(/\.(js|xul)$/);
            if (m)
                fileName += m[1];
        }
  
        fileExt = name.match(/\.uc\.(js|xul)$/i);
        fileExt = fileExt && fileExt[1] ? fileExt[1] : "js";
        charset = charset && charset[1] ? charset[1] : "UTF-8";

        // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Tutorial/Open_and_Save_Dialogs
        var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
        var err = false;
          try {
             fp.init(window, "", Ci.nsIFilePicker.modeSave);
          } catch(e) {
             fp.init(ns.getMostRecentWindow(), "", Ci.nsIFilePicker.modeSave);
             err = true;
             Application.console.log('SaveUserChromeJS.uc.js - error catched (A)');
          };
        var SCRIPTS_FOLDER = Services.dirsvc.get("UChrm", Ci.nsIFile);
        fp.appendFilter("*." + fileExt, "*.uc.js;*.uc.xul");
        fp.appendFilters(Ci.nsIFilePicker.filterAll);
        fp.displayDirectory = SCRIPTS_FOLDER; 
        fp.defaultExtension = fileExt;
        fp.defaultString = fileName;
        var nsIFilePicker = Components.interfaces.nsIFilePicker;
        fp.open(function (rv) {
        if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
              var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Ci.nsIWebBrowserPersist);
                persist.persistFlags = persist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
                var url2 = url.replace("github", "raw.githubusercontent").replace("\/blob", " ");
                var obj_URI;
                if(doc && fileExt != 'xul'){
                    obj_URI = doc.documentURIObject;
                }else{
                    obj_URI = Services.io.newURI(url2, null, null);
                }
                    if(notificationsAfterInstall){
                    persist.progressListener = {
                    onProgressChange: function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
                            if(aCurSelfProgress == aMaxSelfProgress){
                                    var win = err ? ns.getMostRecentWindow() : window;
                                    win.setTimeout(function(){
                                    ns.showInstallMessage({
                                        fileExt: fileExt,
                                        fileName: fileName,
                                        file: fp.file,
                                        charset: charset
                                    });
                                }, 100);
                            }
                        },
                        onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) { }
                    };
                }
                
                persist.saveURI(obj_URI, document.nodePrincipal, null, null, null, null, fp.file, null);
             }
         });
    },
    showInstallMessage: function(info){
        var isRun = (info.fileExt == "js");

        var mainAction, secondActions;
        if(runWithoutRestart && isRun){
            mainAction = {
                label: "Выполнить немедленно (без перезапуска).",
                accessKey: "a",
                callback: function(){
                    ns.runScript(info.file, info.charset);
                }
            };
            secondActions = [{
                label: "Перезагрузите сейчас",
                accessKey: "s",
                callback: ns.restartApp
            }];
        }else{
            mainAction = {
                label: "Перезагрузите сейчас",
                accessKey: "s",
                callback: ns.restartApp
            };
            secondActions = null;
        }

        var showedMsg = ns.popupNotification({
            id: "userchromejs-install-popup-notification",
            message: "'" + info.fileName + "' Установка завершена.",
            mainAction: mainAction,
            secondActions: secondActions,
            options: {
                removeOnDismissal: true,
                persistWhileVisible: true
            }
        });
    },
    popupNotification: function(details){
        var win = ns.getMostRecentWindow();
        if (win && win.PopupNotifications) {
            win.PopupNotifications.show(
                win.gBrowser.selectedBrowser,
                details.id,
                details.message,
                "",
                details.mainAction,
                details.secondActions,
                details.options);
            return true;
        }

        return false;
    },
    // Только поддержка us.js
        runScript: function(file, charset){
        window.userChrome_js.getScripts();
        if(window.userChromeManager){
            window.userChromeManager.rebuildScripts();
        }

        var dir = file.parent.leafName;
        if(dir.toLowerCase() == 'chrome' || (dir in window.userChrome_js.arrSubdir)){

            let context = {};
            Services.scriptloader.loadSubScript( "file:" + file.path, context, charset || "UTF-8");
        }
    },
    flushCache: function (file) {
        if (file)
             Services.obs.notifyObservers(file, "flush-cache-entry", "");
        else
             Services.obs.notifyObservers(null, "startupcache-invalidate", "");
    },
    getFocusedWindow: function() {
        var win = document.commandDispatcher.focusedWindow;
        try {
         return (!win || win == window) ? content : win;
      } catch(e) {
         Application.console.log('SaveUserChromeJS.uc.js - error catched (B)');
         return (!win || win == window) ? null : win;
      };
    },
    getMostRecentWindow: function(){
        return Services.wm.getMostRecentWindow("navigator:browser")
    },
    getBrowserForContentWindow: function(aContentWindow) {
      return aContentWindow.docShell.rootTreeItem.domWindow;
    },
    restartApp: function() {
        const appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].getService(Components.interfaces.nsIAppStartup);

        // Уведомить все окна о том, что приложение было запрошено.
        var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
        var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].createInstance(Components.interfaces.nsISupportsPRBool);
        os.notifyObservers(cancelQuit, "quit-application-requested", null);

        // Что-то прервало процесс выхода.
        if (cancelQuit.data) return;

        // Уведомить все окна о том, что приложение завершено.
        os.notifyObservers(null, "quit-application-granted", null);

        // Перечислить все окна и обработчики вызовов
        var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
        var windows = wm.getEnumerator(null);
        var win;
        while (windows.hasMoreElements()) {
            win = windows.getNext();
            if (("tryToClose" in win) && !win.tryToClose()) return;
        }
        let XRE = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
        if (typeof XRE.invalidateCachesOnRestart == "function") XRE.invalidateCachesOnRestart();
        appStartup.quit(appStartup.eRestart | appStartup.eAttemptQuit);
    }
};


function log(arg){ Application.console.log("[SaveUserChromeJS]" + arg);}

function checkDoc(doc) {
    if (!(doc instanceof HTMLDocument)) return false;
    if (!window.mimeTypeIsTextBased(doc.contentType)) return false;
    if (!doc.body || !doc.body.hasChildNodes()) return false;
    if (doc.body instanceof HTMLFrameSetElement) return false;
    return true;
}

})();

window.saveUserChromeJS.init();


Что то с кодировкой в UTF-8 выходного файла не получается, не пойму где ее поменять можно ( в persist, saveURI или еще где-то)

Отредактировано Andrey_Krropotkin (09-03-2020 01:33:21)

Отсутствует

 

№1442709-03-2020 09:29:00

shadow_user
Участник
 
Группа: Members
Зарегистрирован: 14-02-2007
Сообщений: 169
UA: Firefox 72.0

Re: Custom Buttons

Andrey_Krropotkin пишет

Что то с кодировкой в UTF-8 выходного файла не получается, не пойму где ее поменять можно ( в persist, saveURI или еще где-то)

Я почему-то думал, что это кодировка всего выходного файла, меняю в AkelPad, в нем удобнее: сохранить как - снизу в выпадающем меню выбрать UTF-8, снять птичку BOM. Я ошибаюсь?

Отсутствует

 

№1442809-03-2020 10:29:48

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

Re: Custom Buttons

shadow_user не об этом речь, надо чтобы не менять в редакторе, а открываешь редактор, а там  уже нужная кодировка.

Отсутствует

 

№1442909-03-2020 11:49:41

shadow_user
Участник
 
Группа: Members
Зарегистрирован: 14-02-2007
Сообщений: 169
UA: Firefox 68.0

Re: Custom Buttons

Andrey_Krropotkin пишет

открываешь редактор, а там  уже нужная кодировка.

NotePad++: Опции -> Настройки -> Новый документ -> Синтаксис по умолч.: - CSS (или ...txt, batch...) -> Кодировка - UTF-8
Опции -> Настройки -> Разное -> Автоопределение кодировки - снять птичку.
Теперь при сохранении или открытии файлов .css, .txt, .bat автоматом будет устанавливаться кодировка UTF-8. У меня так и это работает. Тоже не то?

Отредактировано shadow_user (09-03-2020 11:50:19)

Отсутствует

 

№1443009-03-2020 12:04:06

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

Re: Custom Buttons

shadow_user зачем мне его открывать? Я скачал скрипт и он уже должен работать, а работать нормально будет только в utf8.

Отсутствует

 

№1443109-03-2020 14:39:00

shadow_user
Участник
 
Группа: Members
Зарегистрирован: 14-02-2007
Сообщений: 169
UA: Firefox 68.0

Re: Custom Buttons

Andrey_Krropotkin пишет

зачем мне его открывать?

Может, и незачем. А если он сохранен в другой кодировке? Открыл, посмотрел, перекодировал в utf-8. Грамотные старожилы подтянутся, может, направят в правильном направлении.

Отсутствует

 

№1443209-03-2020 16:31:05

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

Re: Custom Buttons

«Merge Day»

Трудно поверить, но, похоже, Custom Buttons за этот цикл не сломали.
Таким образом, остаётся десятка, обновление не требуется.
̣ ̣

Andrey_Krropotkin пишет

Если тестирую браузер нужен AGENT_SHEET, если сайт - USER_SHEET

Странное утверждение. AGENT_SHEET нужен для стилизации NAC.
В любом случае, можно сделать две (или даже все три) кнопки для preview разных типов.
Кстати, AGENT_SHEET preview может глючить, например для NAC-тултипа браузера.

Andrey_Krropotkin пишет

Код:

Посмотрел. Да, вполне подойдёт. Метод используется только в одном месте
для получения ссылки на gBrowser. Сам скрипт, конечно, для однопроцессного режима.
В saveURI() передаётся, почему-то, 8 аргументов, хотя idl говорит, что нужно 9.

Отсутствует

 

№1443309-03-2020 23:16:31

dezhnev
Участник
 
Группа: Members
Зарегистрирован: 21-04-2016
Сообщений: 71
UA: Firefox 73.0

Re: Custom Buttons

Трудно поверить, но, похоже, Custom Buttons за этот цикл не сломали.
Таким образом, остаётся десятка, обновление не требуется.

Dumby у меня что-то никак не появляется в профиле с 73его на 74b9-dev и найтли тоже от 090320
пытаюсь переустановить, пишет:

74b9-dev

скрытый текст
74devb9.png

75a1 nightly 090320
скрытый текст
nightly090320.png

эээх выходные, может чего не соображу?
config.js отсюда: https://forum.mozilla-russia.org/viewtopic.php?pid=777909#p777909
версия 0.0.7.0.0.10

Отсутствует

 

№1443410-03-2020 11:30:12

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

Re: Custom Buttons

dezhnev пишет

на 74b9-dev и найтли тоже от 090320
пытаюсь переустановить

Я собрал портаблы указаных версий и на обе CB установился без проблем.

Сообщение на скринах довольно экзотическое. Обычно, если что-то
пошло не так, то пишет типа «повреждено» или «непроверено»,
но чтобы «несовместимо», — как-то даже не припомню такого в истории
нынешних последних времён.

Помимо проверки установки на чистых профиле/портабл/инсталляции,
возможно, будет представлять интерес то, что во время установки
пишет консоль, если включить extensions.logging.enabled

Отсутствует

 

№1443510-03-2020 18:49:12

solombala
Забанен
 
Группа: Members
Зарегистрирован: 20-07-2019
Сообщений: 652
UA: Firefox 74.0

Re: Custom Buttons

Отсутствует

 

№1443610-03-2020 19:11:35

voqabuhe
Участник
 
Группа: Members
Зарегистрирован: 06-12-2011
Сообщений: 3032
UA: Firefox 74.0

Re: Custom Buttons

del

Отредактировано voqabuhe (10-03-2020 19:13:42)

Отсутствует

 

№1443710-03-2020 20:29:13

dezhnev
Участник
 
Группа: Members
Зарегистрирован: 21-04-2016
Сообщений: 71
UA: Firefox 74.0

Re: Custom Buttons

Dumby
вобщем проблема локализована, в config.js подгружался арсенал userchromeJS раньше, чем твоя "рефлексия" с nsvo,
в итоге оказалась интересная штука, я закомментил все, без чего можно обойтись, по крайней мере в моей конфигурации

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

Выделить код

Код:

//
try {(nsvo => {
    var o = Cu.getGlobalForObject(nsvo).Object, {freeze} = o, NEW;
    o.freeze = obj => {
        if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm")
            return freeze(obj);
        obj.MOZ_GOOGLE_LOCATION_SERVICE_API_KEY = false;                // что угодно из AppConstants.jsm //MOZ_REQUIRE_SIGNING
        if ((NEW = "MOZ_GOOGLE_SAFEBROWSING_API_KEY" in obj))            // что угодно из AppConstants.jsm //MOZ_ALLOW_ADDON_SIDELOAD
        //    lockPref("extensions.experiments.enabled", true);
       // else
        //    obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
       //     lockPref("extensions.legacy.enabled", true);
            
        return (o.freeze = freeze)(obj);
    }
    //lockPref("xpinstall.signatures.required", false);
    //lockPref("extensions.langpacks.signatures.required", false);

    nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {});
    var shouldVerify = nsvo.shouldVerifySignedState;
    nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);

    if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false;

})(Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}));}
catch(ex) {Cu.reportError(ex);}

// load UC
try {
  
  let {
  classes: Cc,
  interfaces: Ci,
  manager: Cm,
  utils: Cu
  } = Components;
  
  let cmanifest = Cc['@mozilla.org/file/directory_service;1'].getService(Ci.nsIProperties).get('UChrm', Ci.nsIFile);
  cmanifest.append('boulderdash');
  cmanifest.append('chrome.manifest');
  
  if(cmanifest.exists()){
    Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(cmanifest);
    Cu.import('chrome://userchromejs/content/ucm.jsm');
  }

} catch(ex) {};

Отредактировано dezhnev (10-03-2020 20:32:00)

Отсутствует

 

№1443810-03-2020 20:55:07

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

Re: Custom Buttons

Dumby посмотри пожалуйста кнопку, правленой раньше тобой

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

Выделить код

Код:

// http://infocatcher.ucoz.net/js/cb/switchKeybLayout.js
// https://github.com/Infocatcher/Custom_Buttons/tree/master/Switch_Keyboard_Layout

// Switch Keyboard Layout button for Custom Buttons
// (code for "code" section)

// (c) Infocatcher 2009, 2013-2014
// version 0.2.0 - 2014-10-20

// Convert text, typed in wrong keyboard layout.
// Configured for Russian <-> English.

(keybUtils => {
    var btn = this;
    var listener = {
        handleEvent(e) {
            if(e.target != btn)
                return;
            e.preventDefault();
            e.stopPropagation();
            this.switch();
        },
        switch() {
            var br = document.activeElement;
            br && br.localName == "browser" && br.isRemoteBrowser
                ? br.messageManager.loadFrameScript(this.url, false)
                : this.keybUtils.switchSelKeybLayout();
        },
        get url() {
            delete this.url;
            return this.url = `data:;charset=utf-8,(${
                encodeURIComponent(keybUtils)
            }).switchSelKeybLayout()`;
        },
        get keybUtils() {
            delete this.keybUtils;
            var url = "data:;charset=utf-8,this.keybUtils = " + encodeURIComponent(keybUtils);
            Services.scriptloader.loadSubScript(url, this);
            this.keybUtils.button = btn;
            this.keybUtils.getFocusedElement = function(_subCall, _focusFixed) {
                if(
                    !_focusFixed
                    && "closeMenus" in window
                    && document.commandDispatcher.focusedElement == this.button
                ) {
                    closeMenus(this.button);
                    setTimeout(function(_this) {
                        _this.switchSelKeybLayout(_subCall, true);
                    }, 0, this);
                    return;
                }
                return document.commandDispatcher.focusedElement;
            }
            return this.keybUtils;
        }
    };
    if(btn instanceof XULElement && addEventListener.length > 3) {
        addEventListener("command", listener, true, this.parentNode);
    }
    listener.switch();
})(`{
    //== Options
    noSelBehavior: { // Shift+Home
        ctrlKey:  false,
        altKey:   false,
        shiftKey: true,
        metaKey:  false,
        keyCode:  KeyEvent.DOM_VK_HOME,
        charCode: 0
    },
    // 0 - do nothing
    // 1 - convert all text
    // Or use object like following to simulate "keypress" event:

    convTableForward: { // ru -> en
        "\\"": "@",
        ":": "^",
        ";": "$",
        "?": "&",
        ",": "?",
        "/": "|",
        ".": "/",
        "э": "'",
        "б": ",",
        "ю": ".",
        "Ж": ":",
        "ж": ";",
        "Б": "<",
        "Ю": ">",
        "Э": "\\"",
        "х": "[",
        "ъ": "]",
        "ё": "\`",
        "Х": "{",
        "Ъ": "}",
        "Ё": "~",
        "№": "#",
        "Ф": "A",
        "ф": "a",
        "И": "B",
        "и": "b",
        "С": "C",
        "с": "c",
        "В": "D",
        "в": "d",
        "У": "E",
        "у": "e",
        "А": "F",
        "а": "f",
        "П": "G",
        "п": "g",
        "Р": "H",
        "р": "h",
        "Ш": "I",
        "ш": "i",
        "О": "J",
        "о": "j",
        "Л": "K",
        "л": "k",
        "Д": "L",
        "д": "l",
        "Ь": "M",
        "ь": "m",
        "Т": "N",
        "т": "n",
        "Щ": "O",
        "щ": "o",
        "З": "P",
        "з": "p",
        "Й": "Q",
        "й": "q",
        "К": "R",
        "к": "r",
        "Ы": "S",
        "ы": "s",
        "Е": "T",
        "е": "t",
        "Г": "U",
        "г": "u",
        "М": "V",
        "м": "v",
        "Ц": "W",
        "ц": "w",
        "Ч": "X",
        "ч": "x",
        "Н": "Y",
        "н": "y",
        "Я": "Z",
        "я": "z",
        __proto__: null
    },
    //== End of options

    get convTableBackward() {
        var ctb = { __proto__: null };
        var ctf = this.convTableForward;
        for(var c in ctf)
            ctb[ctf[c]] = c;
        delete this.convTableBackward;
        return this.convTableBackward = ctb;
    },
    inPrimaryLayout: function(s) {
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            if(c in this.convTableForward)
                return true;
            if(c in this.convTableBackward)
                return false;
        }
        return false;
    },
    switchKeybLayout: function(s, convTable) {
        var res = "";
        for(var i = 0, l = s.length; i < l; ++i) {
            var c = s.charAt(i);
            res += c in convTable ? convTable[c] : c;
        }
        return res;
    },
    getFocusedElement: function() {
        return Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .getFocusedElementForWindow(content, true, {});
    },
    switchSelKeybLayout: function(_subCall, _focusFixed) {
        var fe = this.getFocusedElement(_subCall, _focusFixed);
        if(!fe)
            return;
        if(fe instanceof HTMLInputElement || fe instanceof HTMLTextAreaElement) {
            var ta = fe;
            try {
                var val = ta.value;
                var sel = val.substring(ta.selectionStart, ta.selectionEnd);
            }
            catch(e) { // Non-text HTMLInputElement
                return;
            }
            if(!sel && val && this.noSelBehavior && !_subCall) {
                if(this.noSelBehavior == 1) {
                    ta.selectionStart = 0;
                    ta.selectionEnd = val.length;
                    sel = val;
                }
                else {
                    this.handleNoSel(ta);
                    return;
                }
            }
            if(!sel)
                return;
            var res = this.switchKeybLayout(
                sel,
                this.inPrimaryLayout(sel)
                    ? this.convTableForward
                    : this.convTableBackward
            );
            if(res != sel)
                this.insertText(ta, res);
        }
        else if(fe.contentEditable == "true") {
            var doc = fe.ownerDocument;

            var docURI = doc.documentURI;
            if(
                docURI.substr(0, 5) == "data:"
                && docURI.indexOf("chrome://browser/skin/devtools/") != -1
            ) {
                //~ todo: seems like we only can use paste from clipboard here...
                return;
            }

            var sel = doc.defaultView.getSelection();
            var rng = sel.rangeCount && sel.getRangeAt(0);
            var tmpNode;
            if(!rng || rng.collapsed) {
                if(!this.noSelBehavior || _subCall)
                    return;
                if(this.noSelBehavior == 1) {
                    var r = doc.createRange();
                    r.selectNodeContents(fe);
                    sel.removeAllRanges();
                    sel.addRange(r);
                    tmpNode = fe.cloneNode(true);
                }
                else {
                    this.handleNoSel(fe);
                    return;
                }
            }
            else {
                tmpNode = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
                tmpNode.appendChild(rng.cloneContents());
            }

            var orig = tmpNode.innerHTML;
            var convTable = this.inPrimaryLayout(tmpNode.textContent)
                ? this.convTableForward
                : this.convTableBackward;

            var _this = this;
            var parseChildNodes = function(node) {
                if(node instanceof Element) {
                    var childNodes = node.childNodes;
                    for(var i = childNodes.length - 1; i >= 0; --i)
                        parseChildNodes(childNodes[i]);
                }
                else if(node.nodeType == node.TEXT_NODE) {
                    var text = node.nodeValue;
                    var newText = _this.switchKeybLayout(node.nodeValue, convTable);
                    if(newText != text)
                        node.parentNode.replaceChild(doc.createTextNode(newText), node);
                }
            }
            parseChildNodes(tmpNode);

            var res = tmpNode.innerHTML;
            if(res != orig)
                doc.execCommand("insertHTML", false, res);
        }
    },
    handleNoSel: function(node) {
        this.select(node);
        this.switchSelKeybLayout(true);
    },
    select: function(node) {
        var e = this.noSelBehavior;
        if(!e || typeof e != "object")
            return;
        var evt = node.ownerDocument.createEvent("KeyboardEvent");
        evt.initKeyEvent(
            "keypress", true /*bubbles*/, true /*cancelable*/, node.ownerDocument.defaultView,
            e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
            e.keyCode, e.charCode
        );
        node.dispatchEvent(evt);
    },
    insertText: function(ta, text) {
        //var editor = ta.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor
        var editor = ta.editor
            .QueryInterface(Components.interfaces.nsIPlaintextEditor);
        if(editor.flags & editor.eEditorReadonlyMask)
            return;

        var sTop = ta.scrollTop;
        var sHeight = ta.scrollHeight;
        var sLeft = ta.scrollLeft;
        // var sWidth = ta.scrollWidth;

        if(text)
            editor.insertText(text);
        else
            editor.deleteSelection(0, 0);

        ta.scrollTop = sTop + (ta.scrollHeight - sHeight);
        ta.scrollLeft = sLeft; // + (ta.scrollWidth - sWidth);
    }
}`);


выдает NS_ERROR_XPC_BAD_CONVERT_JS: Could not convert JavaScript argument arg 0 [nsISupports.QueryInterface] - noSelBehavior: { // Shift:239

Отсутствует

 

№1443910-03-2020 21:22:13

Inko7
Участник
 
Группа: Members
Зарегистрирован: 09-11-2009
Сообщений: 957
UA: Firefox 73.0

Re: Custom Buttons

как не хватает функционала этой кнопки в дополнениях для новых версий лисы, их просто нет
привык к ней безумно, а в новом фоксе у меня какие-то постоянные траблы в целомс CB (скорее руки кривые)
Dumby где-то в теме писал, что можно код кнопки завернуть в такое  дополнение.
Подскажите как или, если кто сможет сам, помогите создать такое дополнение для всех в радость))

Отсутствует

 

№1444010-03-2020 22:42:22

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

Re: Custom Buttons

solombala пишет

Упал аддон в 74

Не вижу. Скачал, установил в 74.0, нормально работает,
ну кроме как в той части, которая пострадала от того,
что ты, зачем-то, выбросил button.css

Ещё раз
Может пропустил, но я упоминал баг, в котором для Firefox 74+
была изменена политика в отношении некоторых типов аддонов.

Приципиальное разрешение на paxmod'ы привязали
к подписячему флагу, а пользовательское произволение
реализуется посредством настройки extensions.experiments.enabled

А вот расширения запрещены полностью и для всех каналов обновления.

Конфигурация дадена. Если не нравится, тогда используй другую.
Если не нравится JS-воздействие как таковое, тогда обеспечь
аналогичное правкой omni.ja

Но если ничего не сделать, тогда конечо, bootstrap-расширения
(а другие и невозможны уже давно) работать не будут.

Andrey_Krropotkin пишет

Could not convert JavaScript argument arg 0 [nsISupports.QueryInterface]

nsIPlaintextEditor выбросили из Ci, а его функционал переместили в nsIEditor
Если обратная совместимость не нужна, то можно удалить
.QueryInterface(Components.interfaces.nsIPlaintextEditor);
а если нужна, то заменить, например, на
.QueryInterface(Components.interfaces.nsIPlaintextEditor || Ci.nsIEditor);

Отсутствует

 

№1444110-03-2020 23:13:23

solombala
Забанен
 
Группа: Members
Зарегистрирован: 20-07-2019
Сообщений: 652
UA: Firefox 74.0

Re: Custom Buttons

Dumby
Да оно даже не устанавливается ... Код новый  config.js есть?
extensions.experiments.enabled -true
где в omni.ja рихтовать ? Вроде все как всегда там отрихтовал...
Рихтанул кардинально ...Установилось...

Отредактировано solombala (10-03-2020 23:36:14)

Отсутствует

 

№1444210-03-2020 23:47:42

dezhnev
Участник
 
Группа: Members
Зарегистрирован: 21-04-2016
Сообщений: 71
UA: Firefox 74.0

Re: Custom Buttons

solombala
попробуй этот, фишка в том, что первый блок не трогай, а вниз ставь все свое что бы там ни было

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

Выделить код

Код:

//
try {(nsvo => {
    var o = Cu.getGlobalForObject(nsvo).Object, {freeze} = o, NEW;
    o.freeze = obj => {
        if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm")
            return freeze(obj);
        obj.MOZ_REQUIRE_SIGNING = false;
        if ((NEW = "MOZ_ALLOW_ADDON_SIDELOAD" in obj))
            lockPref("extensions.experiments.enabled", true);
        else
            obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
            lockPref("extensions.legacy.enabled", true);
            
        return (o.freeze = freeze)(obj);
    }
    lockPref("xpinstall.signatures.required", false);
    lockPref("extensions.langpacks.signatures.required", false);

    nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {});
    var shouldVerify = nsvo.shouldVerifySignedState;
    nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);

    if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false;

})(Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}));}
catch(ex) {Cu.reportError(ex);}

//
try {({
    ids: [
        "custombuttons@xsms.org",
    ],
    init(xrt) {
        if (xrt.inSafeMode) return;
        Cu.import("resource://gre/modules/addons/XPIProvider.jsm", this);
        var load = async file => {
            var rootURI = this.XPIInternal.getURIForResourceInFile(file, "");
            Cu.import(rootURI.resolve("startup.jsm"), {}).start(rootURI);
        }
        var proto = this.XPIInternal.BootstrapScope.prototype;
        var func = proto._beforeCallBootstrapMethod;

        proto._beforeCallBootstrapMethod = () => {
            proto._beforeCallBootstrapMethod = func;
            for(var addon of this.XPIInternal.XPIStates.enabledAddons())
                this.ids.includes(addon.id) && !addon.loader && load(addon.file);
        }
    }
}).init(Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime));}
catch(ex) {Cu.reportError(ex);}

//
try {
    Cu.createDocumentEncoder && Cc["@mozilla.org/moz/jssubscript-loader;1"]
        .getService(Ci.mozIJSSubScriptLoader).loadSubScript(String.raw`
            data:,var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");%0Avar {AddonManager} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");%0A%0Athis.lazyModules = {%0A%09OS: "resource://gre/modules/osfile.jsm",%0A%09Services: "resource://gre/modules/Services.jsm",%0A%09ConsoleAPI: "resource://gre/modules/Console.jsm",%0A%09Blocklist: "resource://gre/modules/Blocklist.jsm",%0A%09AddonInternal: "resource://gre/modules/addons/XPIDatabase.jsm"%0A};%0Aif ("@mozilla.org/intl/domlocalization;1" in Cc)%0A%09this.lazyModules.RDFDataSource = "resource://gre/modules/addons/RDFDataSource.jsm";%0Aelse%0A%09// Firefox 67+%0A%09// Bug 1523194 - Remove XPIDL for DOMLocalization and use do_ImportModule instead%0A%09// https://bugzilla.mozilla.org/show_bug.cgi?id=1523194%0A%09//%0A%09// Bug 857458 - Remove support for update.rdf%0A%09// https://bugzilla.mozilla.org/show_bug.cgi?id=857458%0A%0A%09XPCOMUtils.defineLazyGetter(this, "RDFDataSource", () => {%0A%09%09var obs = {};%0A%09%09var scs = Cc["@mozilla.org/streamConverters;1"].getService(Ci.nsIStreamConverterService);%0A%09%09var sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);%0A%09%09var sl =  Cc["@mozilla.org/network/stream-loader;1"].createInstance(Ci.nsIStreamLoader);%0A%0A%09%09var gzip = "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";%0A%09%09sis.data = atob(gzip);%0A%09%09obs.onStreamComplete = (a, b, c, d, result) => Cu.evalInSandbox(String.fromCharCode(...result), this);%0A%09%09sl.init(obs);%0A%09%09var converter = scs.asyncConvertData("gzip", "uncompressed", sl, null);%0A%09%09converter.onStartRequest(null, null);%0A%0A%09%09var args = [null, null, sis, 0, sis.data.length];%0A%09%09// Bug 1525319 - Investigate if we can remove Context argument from Channel methods (Firefox 67+)%0A%09%09// https://bugzilla.mozilla.org/show_bug.cgi?id=1525319%0A%09%09if (converter.onDataAvailable.length == 4) args.shift();%0A%09%09converter.onDataAvailable(...args);%0A%0A%09%09converter.onStopRequest(null, null, null);%0A%0A%09%09return RDFDataSource;%0A%09});%0AXPCOMUtils.defineLazyModuleGetters(this, this.lazyModules);%0Adelete this.lazyModules;%0A%0AXPCOMUtils.defineLazyGetter(this, "BOOTSTRAP_REASONS", () =>%0A%09ChromeUtils.import("resource://gre/modules/addons/XPIProvider.jsm", {})%0A%09%09.XPIProvider.BOOTSTRAP_REASONS%0A);%0AXPCOMUtils.defineLazyGetter(this, "logger", () =>%0A%09ChromeUtils.import("resource://gre/modules/Log.jsm", {})%0A%09%09.Log.repository.getLogger("addons.bootstrap")%0A);%0A%0A%0A// RDFManifestConverter.jsm%0Aconst RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";%0A%0Afunction EM_R(aProperty) {%0A%09return %60http://www.mozilla.org/2004/em-rdf%23%24{aProperty}%60;%0A}%0Afunction getValue(literal) {%0A%09return literal && literal.getValue();%0A}%0Afunction getProperty(resource, property) {%0A%09return getValue(resource.getProperty(EM_R(property)));%0A}%0A%0Aclass Manifest {%0A%09constructor(ds) {%0A%09%09this.ds = ds;%0A%09}%0A%09static loadFromString(text) {%0A%09%09return new this(RDFDataSource.loadFromString(text));%0A%09}%0A%09static loadFromBuffer(buffer) {%0A%09%09return new this(RDFDataSource.loadFromBuffer(buffer));%0A%09}%0A%09static async loadFromFile(uri) {%0A%09%09return new this(await RDFDataSource.loadFromFile(uri));%0A%09}%0A}%0A%0Aclass InstallRDF extends Manifest {%0A%09_readProps(source, obj, props) {%0A%09%09for (let prop of props) {%0A%09%09%09let val = getProperty(source, prop);%0A%09%09%09if (val != null) {%0A%09%09%09%09obj[prop] = val;%0A%09%09%09}%0A%09%09}%0A%09}%0A%09_readArrayProp(source, obj, prop, target, decode = getValue) {%0A%09%09let result = Array.from(%0A%09%09%09source.getObjects(EM_R(prop)), target => decode(target)%0A%09%09);%0A%09%09if (result.length) {%0A%09%09%09obj[target] = result;%0A%09%09}%0A%09}%0A%09_readArrayProps(source, obj, props, decode = getValue) {%0A%09%09for (let [prop, target] of Object.entries(props)) {%0A%09%09%09this._readArrayProp(source, obj, prop, target, decode);%0A%09%09}%0A%09}%0A%09_readLocaleStrings(source, obj) {%0A%09%09this._readProps(source, obj, ["name", "description", "creator", "homepageURL"]);%0A%09%09this._readArrayProps(source, obj, {%0A%09%09%09locale: "locales",%0A%09%09%09developer: "developers",%0A%09%09%09translator: "translators",%0A%09%09%09contributor: "contributors",%0A%09%09});%0A%09}%0A%09decode() {%0A%09%09let root = this.ds.getResource(RDFURI_INSTALL_MANIFEST_ROOT);%0A%09%09let result = {};%0A%0A%09%09let props = [%0A%09%09%09"id", "version", "type", "updateURL", "optionsURL",%0A%09%09%09"optionsType", "aboutURL", "iconURL",%0A%09%09%09"bootstrap", "unpack", "strictCompatibility"%0A%09%09];%0A%09%09this._readProps(root, result, props);%0A%0A%09%09let decodeTargetApplication = source => {%0A%09%09%09let app = {maxVersion: "*"};%0A%09%09%09this._readProps(source, app, ["id", "minVersion"]);%0A%09%09%09return app;%0A%09%09};%0A%0A%09%09let decodeLocale = source => {%0A%09%09%09let localized = {};%0A%09%09%09this._readLocaleStrings(source, localized);%0A%09%09%09return localized;%0A%09%09};%0A%0A%09%09this._readLocaleStrings(root, result);%0A%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"targetPlatform": "targetPlatforms"}%0A%09%09);%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"targetApplication": "targetApplications"}, decodeTargetApplication%0A%09%09);%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"localized": "localized"}, decodeLocale%0A%09%09);%0A%09%09this._readArrayProps(%0A%09%09%09root, result, {"dependency": "dependencies"}, source => getProperty(source, "id")%0A%09%09);%0A%09%09return result;%0A%09}%0A}%0A// fim RDFManifestConverter.jsm%0A%0A%0A// BootstrapLoader.jsm%0A/**%0A * Valid IDs fit this pattern.%0A */%0Avar gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i;%0A%0A// Properties that exist in the install manifest%0Aconst PROP_METADATA%09= [%0A%09"id", "version", "type", "internalName", "updateURL",%0A%09"optionsURL", "optionsType", "aboutURL", "iconURL"%0A];%0Aconst PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"];%0Aconst PROP_LOCALE_MULTI%09= ["developers", "translators", "contributors"];%0A%0A// Map new string type identifiers to old style nsIUpdateItem types.%0A// Retired values:%0A// 32 = multipackage xpi file%0A// 8 = locale%0A// 256 = apiextension%0A// 128 = experiment%0A// theme = 4%0Aconst TYPES = {%0A%09extension: 2,%0A%09dictionary: 64,%0A};%0A%0Aconst COMPATIBLE_BY_DEFAULT_TYPES = {%0A%09extension: true,%0A%09dictionary: true,%0A};%0A%0Aconst hasOwnProperty = Function.call.bind(Object.prototype.hasOwnProperty);%0A%0Afunction isXPI(filename) {%0A%09let ext = filename.slice(-4).toLowerCase();%0A%09return ext === ".xpi" || ext === ".zip";%0A}%0A%0A/**%0A * Gets an nsIURI for a file within another file, either a directory or an XPI%0A * file. If aFile is a directory then this will return a file: URI, if it is an%0A * XPI file then it will return a jar: URI.%0A *%0A * @param {nsIFile} aFile%0A *%09%09The file containing the resources, must be either a directory or an XPI file%0A *%0A * @param {string} aPath%0A *%09%09The path to find the resource at, "/" separated. If aPath is empty%0A *%09%09then the uri to the root of the contained files will be returned%0A *%0A * @returns {nsIURI}%0A *%09%09An nsIURI pointing at the resource%0A */%0Afunction getURIForResourceInFile(aFile, aPath) {%0A%09if (!isXPI(aFile.leafName)) {%0A%09%09let resource = aFile.clone();%0A%09%09if (aPath)%0A%09%09%09aPath.split("/").forEach(part => resource.append(part));%0A%0A%09%09return Services.io.newFileURI(resource);%0A%09}%0A%09return buildJarURI(aFile, aPath);%0A}%0A%0A/**%0A * Creates a jar: URI for a file inside a ZIP file.%0A *%0A * @param {nsIFile} aJarfile%0A *%09%09The ZIP file as an nsIFile%0A *%0A * @param {string} aPath%0A *%09%09The path inside the ZIP file%0A *%0A * @returns {nsIURI}%0A *%09%09An nsIURI for the file%0A */%0Afunction buildJarURI(aJarfile, aPath) {%0A%09let uri = Services.io.newFileURI(aJarfile);%0A%09uri = "jar:" + uri.spec + "!/" + aPath;%0A%09return Services.io.newURI(uri);%0A}%0A%0Avar BootstrapLoader = {%0A%09name: "bootstrap",%0A%09manifestFile: "install.rdf",%0A%09async loadManifest(pkg) {%0A%09%09/**%0A%09%09 * Reads locale properties from either the main install manifest root%0A%09%09 * or an em:localized section in the install manifest.%0A%09%09 *%0A%09%09 * @param {Object} aSource%0A%09%09 *%09%09The resource to read the properties from.%0A%09%09 *%0A%09%09 * @param {boolean} isDefault%0A%09%09 *%09%09True if the locale is to be read from the main install manifest root%0A%09%09 *%0A%09%09 * @param {string[]} aSeenLocales%0A%09%09 *%09%09An array of locale names already seen for this install manifest.%0A%09%09 *%09%09Any locale names seen as a part of this function will be added to this array%0A%09%09 *%0A%09%09 * @returns {Object}%0A%09%09 *%09%09An object containing the locale properties%0A%09%09 */%0A%09%09function readLocale(aSource, isDefault, aSeenLocales) {%0A%09%09%09let locale = {};%0A%09%09%09if (!isDefault) {%0A%09%09%09%09locale.locales = [];%0A%09%09%09%09for (let localeName of aSource.locales || []) {%0A%09%09%09%09%09if (!localeName) {%0A%09%09%09%09%09%09logger.warn("Ignoring empty locale in localized properties");%0A%09%09%09%09%09%09continue;%0A%09%09%09%09%09}%0A%09%09%09%09%09if (aSeenLocales.includes(localeName)) {%0A%09%09%09%09%09%09logger.warn("Ignoring duplicate locale in localized properties");%0A%09%09%09%09%09%09continue;%0A%09%09%09%09%09}%0A%09%09%09%09%09aSeenLocales.push(localeName);%0A%09%09%09%09%09locale.locales.push(localeName);%0A%09%09%09%09}%0A%0A%09%09%09%09if (locale.locales.length == 0) {%0A%09%09%09%09%09logger.warn("Ignoring localized properties with no listed locales");%0A%09%09%09%09%09return null;%0A%09%09%09%09}%0A%09%09%09}%0A%09%09%09for (let prop of [...PROP_LOCALE_SINGLE, ...PROP_LOCALE_MULTI]) {%0A%09%09%09%09if (hasOwnProperty(aSource, prop)) {%0A%09%09%09%09%09locale[prop] = aSource[prop];%0A%09%09%09%09}%0A%09%09%09}%0A%09%09%09return locale;%0A%09%09}%0A%0A%09%09let manifestData = await pkg.readString("install.rdf");%0A%09%09let manifest = InstallRDF.loadFromString(manifestData).decode();%0A%0A%09%09let addon = new AddonInternal();%0A%09%09for (let prop of PROP_METADATA) {%0A%09%09%09if (hasOwnProperty(manifest, prop)) {%0A%09%09%09%09addon[prop] = manifest[prop];%0A%09%09%09}%0A%09%09}%0A%09%09if (!addon.type) {%0A%09%09%09addon.type = "extension";%0A%09%09} else {%0A%09%09%09let type = addon.type;%0A%09%09%09addon.type = null;%0A%09%09%09for (let name in TYPES) {%0A%09%09%09%09if (TYPES[name] == type) {%0A%09%09%09%09%09addon.type = name;%0A%09%09%09%09%09break;%0A%09%09%09%09}%0A%09%09%09}%0A%09%09}%0A%09%09if (!(addon.type in TYPES))%0A%09%09%09throw new Error("Install manifest specifies unknown type: " + addon.type);%0A%0A%09%09if (!addon.id)%0A%09%09%09throw new Error("No ID in install manifest");%0A%09%09if (!gIDTest.test(addon.id))%0A%09%09%09throw new Error("Illegal add-on ID " + addon.id);%0A%09%09if (!addon.version)%0A%09%09%09throw new Error("No version in install manifest");%0A%0A%09%09addon.strictCompatibility =%0A%09%09%09!(addon.type in COMPATIBLE_BY_DEFAULT_TYPES) ||%0A%09%09%09"strictCompatibility" in manifest && manifest.strictCompatibility == "true";%0A%0A%09%09// Only read these properties for extensions.%0A%09%09if (addon.type == "extension") {%0A%09%09%09if (manifest.bootstrap != "true") {%0A%09%09%09%09throw new Error("Non-restartless extensions no longer supported");%0A%09%09%09}%0A%09%09%09if (%0A%09%09%09%09addon.optionsType &&%0A%09%09%09%09addon.optionsType != AddonManager.OPTIONS_TYPE_INLINE_BROWSER &&%0A%09%09%09%09addon.optionsType != AddonManager.OPTIONS_TYPE_TAB%0A%09%09%09) {%0A%09%09%09%09throw new Error("Install manifest specifies unknown optionsType: " + addon.optionsType);%0A%09%09%09}%0A%09%09} else {%0A%09%09%09// Convert legacy dictionaries into a format the WebExtension%0A%09%09%09// dictionary loader can process.%0A%09%09%09if (addon.type === "dictionary") {%0A%09%09%09%09addon.loader = null;%0A%09%09%09%09let dictionaries = {};%0A%09%09%09%09await pkg.iterFiles(({path}) => {%0A%09%09%09%09%09let match = /^dictionaries\/([^\/]+)\.dic$/.exec(path);%0A%09%09%09%09%09if (match) {%0A%09%09%09%09%09%09let lang = match[1].replace(/_/g, "-");%0A%09%09%09%09%09%09dictionaries[lang] = match[0];%0A%09%09%09%09%09}%0A%09%09%09%09});%0A%09%09%09%09addon.startupData = {dictionaries};%0A%09%09%09}%0A%0A%09%09%09// Only extensions are allowed to provide an optionsURL, optionsType,%0A%09%09%09// optionsBrowserStyle, or aboutURL. For all other types they are silently ignored%0A%09%09%09addon.aboutURL = null;%0A%09%09%09addon.optionsBrowserStyle = null;%0A%09%09%09addon.optionsType = null;%0A%09%09%09addon.optionsURL = null;%0A%09%09}%0A%0A%09%09addon.defaultLocale = readLocale(manifest, true);%0A%09%09let defaultLocaleEntries = Object.entries(addon.defaultLocale);%0A%0A%09%09let seenLocales = [];%0A%09%09addon.locales = [];%0A%09%09for (let localeData of manifest.localized || []) {%0A%09%09%09let locale = readLocale(localeData, false, seenLocales);%0A%09%09%09if (locale) {%0A%09%09%09%09for(let [key, val] of defaultLocaleEntries)%0A%09%09%09%09%09key in locale || Reflect.set(locale, key, val);%0A%09%09%09%09addon.locales.push(locale);%0A%09%09%09}%0A%09%09}%0A%0A%09%09addon.dependencies = Object.freeze(%0A%09%09%09"dependencies" in manifest ? Array.from(new Set(manifest.dependencies)) : []%0A%09%09);%0A%0A%09%09let seenApplications = [];%0A%09%09addon.targetApplications = [];%0A%09%09for (let targetApp of manifest.targetApplications || []) {%0A%09%09%09if (!targetApp.id || !targetApp.minVersion || !targetApp.maxVersion) {%0A%09%09%09%09logger.warn("Ignoring invalid targetApplication entry in install manifest");%0A%09%09%09%09continue;%0A%09%09%09}%0A%09%09%09if (seenApplications.includes(targetApp.id)) {%0A%09%09%09%09logger.warn(%0A%09%09%09%09%09"Ignoring duplicate targetApplication entry for " + targetApp.id + " in install manifest"%0A%09%09%09%09);%0A%09%09%09%09continue;%0A%09%09%09}%0A%09%09%09seenApplications.push(targetApp.id);%0A%09%09%09addon.targetApplications.push(targetApp);%0A%09%09}%0A%0A%09%09// Note that we don't need to check for duplicate targetPlatform entries since%0A%09%09// the RDF service coalesces them for us.%0A%09%09addon.targetPlatforms = [];%0A%09%09for (let targetPlatform of manifest.targetPlatforms || []) {%0A%09%09%09let platform = {%0A%09%09%09%09os: null,%0A%09%09%09%09abi: null,%0A%09%09%09};%0A%0A%09%09%09let pos = targetPlatform.indexOf("_");%0A%09%09%09if (pos != -1) {%0A%09%09%09%09platform.os = targetPlatform.substring(0, pos);%0A%09%09%09%09platform.abi = targetPlatform.substring(pos + 1);%0A%09%09%09} else {%0A%09%09%09%09platform.os = targetPlatform;%0A%09%09%09}%0A%09%09%09addon.targetPlatforms.push(platform);%0A%09%09}%0A%0A%09%09addon.userDisabled = false;%0A%09%09addon.softDisabled = addon.blocklistState == Blocklist.STATE_SOFTBLOCKED;%0A%09%09addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;%0A%0A%09%09addon.userPermissions = null;%0A%0A%09%09addon.icons = {};%0A%09%09if (await pkg.hasResource("icon.png")) {%0A%09%09%09addon.icons[32] = "icon.png";%0A%09%09%09addon.icons[48] = "icon.png";%0A%09%09}%0A%0A%09%09if (await pkg.hasResource("icon64.png")) {%0A%09%09%09addon.icons[64] = "icon64.png";%0A%09%09}%0A%0A%09%09return addon;%0A%09},%0A%0A%09// Bug 1512436 - Add new install location for built in search web extensions (Firefox 67+)%0A%09// https://bugzilla.mozilla.org/show_bug.cgi?id=1512436%0A%09// https://hg.mozilla.org/mozilla-central/diff/e3e75697e98c/toolkit/mozapps/extensions/internal/XPIProvider.jsm%23l1.396%0A%09//loadScope(addon, file) {%0A%09loadScope(addon, file = addon.file || addon._sourceBundle) {%0A%0A%09%09let uri = getURIForResourceInFile(file, "bootstrap.js").spec;%0A%09%09let principal = Services.scriptSecurityManager.getSystemPrincipal();%0A%0A%09%09let sandbox = new Cu.Sandbox(principal, {%0A%09%09%09sandboxName: uri,%0A%09%09%09addonId: addon.id,%0A%09%09%09wantGlobalProperties: ["ChromeUtils"],%0A%09%09%09metadata: { addonID: addon.id, URI: uri },%0A%09%09});%0A%0A%09%09try {%0A%09%09%09Object.assign(sandbox, BOOTSTRAP_REASONS);%0A%0A%09%09%09XPCOMUtils.defineLazyGetter(%0A%09%09%09%09sandbox, "console", () => new ConsoleAPI({ consoleID: %60addon/%24{addon.id}%60 })%0A%09%09%09);%0A%09%09%09Services.scriptloader.loadSubScript(uri, sandbox);%0A%0A%09%09} catch (e) {%0A%09%09%09logger.warn(%60Error loading bootstrap.js for %24{addon.id}%60, e);%0A%09%09}%0A%0A%09%09function findMethod(name) {%0A%09%09%09if (sandbox.name) {%0A%09%09%09%09return sandbox.name;%0A%09%09%09}%0A%09%09%09try {%0A%09%09%09%09let method = Cu.evalInSandbox(name, sandbox);%0A%09%09%09%09return method;%0A%09%09%09} catch (err) { }%0A%0A%09%09%09return () => {%0A%09%09%09%09logger.warn(%60Add-on %24{addon.id} is missing bootstrap method %24{name}%60);%0A%09%09%09};%0A%09%09}%0A%0A%09%09let install = findMethod("install");%0A%09%09let uninstall = findMethod("uninstall");%0A%09%09let startup = findMethod("startup");%0A%09%09let shutdown = findMethod("shutdown");%0A%0A%09%09return {%0A%09%09%09install: (...args) => install(...args),%0A%0A%09%09%09// https://github.com/xiaoxiaoflood/firefox-scripts/commit/b509dd0089dc7245e9797e0068b8fcff6ae3c776%0A%09%09%09//uninstall: (...args) => uninstall(...args),%0A%09%09%09uninstall(...args) {%0A%09%09%09%09var res = uninstall(...args);%0A%09%09%09%09// Forget any cached files we might've had from this extension.%0A%09%09%09%09Services.obs.notifyObservers(null, "startupcache-invalidate");%0A%09%09%09%09return res;%0A%09%09%09},%0A%0A%09%09%09startup(...args) {%0A%09%09%09%09if (addon.type == "extension") {%0A%09%09%09%09%09logger.debug(%60Registering manifest for %24{file.path}\n%60);%0A%09%09%09%09%09Components.manager.addBootstrappedManifestLocation(file);%0A%09%09%09%09}%0A%09%09%09%09return startup(...args);%0A%09%09%09},%0A%0A%09%09%09shutdown(data, reason) {%0A%09%09%09%09try {%0A%09%09%09%09%09return shutdown(data, reason);%0A%09%09%09%09} catch (err) {%0A%09%09%09%09%09throw err;%0A%09%09%09%09} finally {%0A%09%09%09%09%09if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {%0A%09%09%09%09%09%09logger.debug(%60Removing manifest for %24{file.path}\n%60);%0A%09%09%09%09%09%09Components.manager.removeBootstrappedManifestLocation(file);%0A%09%09%09%09%09}%0A%09%09%09%09}%0A%09%09%09},%0A%09%09};%0A%09},%0A};%0A// fim BootstrapLoader.jsm%0A%0A%0AAddonManager.addExternalExtensionLoader(BootstrapLoader);%0AObject.defineProperty(%0A%09AddonInternal.prototype,%0A%09"providesUpdatesSecurely",%0A%09{enumerable: true, value: true}%0A);%0A
        `.trim(), new Cu.Sandbox(Cu.getObjectPrincipal(this), {wantGlobalProperties: ["ChromeUtils", "atob"]}));
} catch(ex) {Cu.reportError(ex);}

Отсутствует

 

№1444310-03-2020 23:51:47

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

Re: Custom Buttons

Inko7 о какой кнопке речь?

Отсутствует

 

№1444411-03-2020 00:09:29

solombala
Забанен
 
Группа: Members
Зарегистрирован: 20-07-2019
Сообщений: 652
UA: Firefox 74.0

Re: Custom Buttons

dezhnev
От это - то шо надо! Кстати, этот код можно сократить? В AppConstants.jsm уже правил  и в префке тоже ( lockPref("extensions.legacy.enabled", true); и т.д)

скрытый текст
MOZ_REQUIRE_SIGNING: false
MOZ_ALLOW_ADDON_SIDELOAD: true
MOZ_ALLOW_LEGACY_EXTENSIONS: true

скрытый текст
try {(nsvo => {
    var o = Cu.getGlobalForObject(nsvo).Object, {freeze} = o, NEW;
    o.freeze = obj => {
        if (Components.stack.caller.filename != "resource://gre/modules/AppConstants.jsm")
            return freeze(obj);
        obj.MOZ_REQUIRE_SIGNING = false;
        if ((NEW = "MOZ_ALLOW_ADDON_SIDELOAD" in obj))
            lockPref("extensions.experiments.enabled", true);
        else
            obj.MOZ_ALLOW_LEGACY_EXTENSIONS = true,
            lockPref("extensions.legacy.enabled", true);
           
        return (o.freeze = freeze)(obj);
    }
    lockPref("xpinstall.signatures.required", false);
    lockPref("extensions.langpacks.signatures.required", false);

    nsvo = Cu.import("resource://gre/modules/addons/XPIInstall.jsm", {});
    var shouldVerify = nsvo.shouldVerifySignedState;
    nsvo.shouldVerifySignedState = addon => !addon.id && shouldVerify(addon);

    if (NEW) nsvo.XPIDatabase.isDisabledLegacy = () => false;

})(Cu.import("resource://gre/modules/WebRequestCommon.jsm", {}));}
catch(ex) {Cu.reportError(ex);}

Отредактировано solombala (11-03-2020 12:33:03)

Отсутствует

 

№1444511-03-2020 10:57:32

Inko7
Участник
 
Группа: Members
Зарегистрирован: 09-11-2009
Сообщений: 957
UA: Firefox 52.0

Re: Custom Buttons

Andrey_Krropotkin
о которой вы и беседовали - "// Switch Keyboard Layout button for Custom Buttons"

Отсутствует

 

№1444612-03-2020 09:57:04

VORON
Участник
 
Группа: Members
Зарегистрирован: 03-04-2013
Сообщений: 144
UA: Firefox 68.0

Re: Custom Buttons

Посоветуйте кнопку для отображения логинов и паролей?В 54й лисе точно такая была,а для 68й что то не найду.

Отсутствует

 

№1444712-03-2020 20:21:15

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

Re: Custom Buttons

Dumby посмотри пожалуйста код, что то не получается. В блоке saveScript - если XMLHttpRequest() сделать асинхронным, то все рушится. Может есть вариант по другому сделать сохранение.

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

Выделить код

Код:

(function() {

// Включены ли уведомления после сохранения?
var notificationsAfterInstall = true;

// Загружается ли скрипт после сохранения (запускать не нужно)? Поддерживается только .uc.js, некоторые скрипты проблематичны.
var runWithoutRestart = true;

let { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
if (!window.Services) Cu.import("resource://gre/modules/Services.jsm");

const RE_USERCHROME_JS = /\.uc(?:-\d+)?\.(?:js|xul)$|userChrome\.js$/i;
const RE_CONTENTTYPE = /text\/html/i;

var ns = window.saveUserChromeJS = {

    init: function() {
           // добавить в contentAreaContextMenu
              if ( document.getElementById("uc-install-menu") ) return; 
              var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
        var icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAAICAAP//AICAgMDAwP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcj7VkAAABfdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8Ak5pfLAAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAFxJREFUKFNljgEOwCAIAwuF/395RWXMrDExHLQA3gKYpVXojwPo6SpewDSzAHGAXvWpkQYagNSWN2EyfGUM+G/RQWZFOnSt9AZMR3nGwggivqF1Q12xQVzavRHwALQIAv2bDRzYAAAAAElFTkSuQmCC";
        var contextMenu = document.getElementById("contentAreaContextMenu");
              var menuitem = document.createElementNS(xulns, "menuitem");
        menuitem.setAttribute("id", "uc-install-menu");
        menuitem.setAttribute("label", "Установить для userChromeJS");
        menuitem.setAttribute("class", "menuitem-iconic");
        menuitem.setAttribute("image", icon);
        menuitem.setAttribute("accessKey", "I");
        menuitem.setAttribute("oncommand", "saveUserChromeJS.saveScript(gContextMenu.linkURL)");

              contextMenu.insertBefore(menuitem, contextMenu.firstChild);
              contextMenu.addEventListener("popupshowing", this, false);
        this._menuitem = menuitem;
    },
    handleEvent: function(event){
        switch(event.type){
            case "popupshowing":
                if (event.target != event.currentTarget) return;
                if(gContextMenu.onLink){
                    this._menuitem.hidden = !RE_USERCHROME_JS.test(gContextMenu.linkURL);
                }else{
                    this._menuitem.hidden = true;
                }
                break;
        }
    },
    saveScript: function(url) {
        var name, fileName, fileExt, charset;
        name = name && name[1] ? name[1] : decodeURIComponent(url.split("/").pop());
        fileName = name.replace(/\.uc\.(js|xul)$|$/i, ".uc.$1").replace(/\s/g, '_');
        if (fileName.match(/\.uc\.$/i)) {  // Исправить имя
            var m = url.match(/\.(js|xul)$/);
            if (m)
                fileName += m[1];
        }
        charset = "UTF-8";
            var fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
            var err = false;
          try {
             fp.init(window, "", Ci.nsIFilePicker.modeSave);
          } catch(e) {
             fp.init(ns.getMostRecentWindow(), "", Ci.nsIFilePicker.modeSave);
             err = true;
             Application.console.log('SaveUserChromeJS.uc.js - error catched (A)');
          };
        var SCRIPTS_FOLDER = Services.dirsvc.get("UChrm", Ci.nsIFile);
        var xhr = new XMLHttpRequest();
        var url2 = url.replace("github", "raw.githubusercontent").replace("\/blob", " ");
        xhr.open('GET', url2, false);
        xhr.send();
        if (xhr.status == 200) {
        var uc = Components.classes['@mozilla.org/intl/scriptableunicodeconverter'].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
          uc.charset = 'utf-8';
          var text = uc.ConvertFromUnicode(xhr.responseText);
            }

        fp.displayDirectory = SCRIPTS_FOLDER; 
            fp.appendFilters(Ci.nsIFilePicker.filterAll);
            fp.defaultExtension = fileExt;
            fp.defaultString = fileName;
            var nsIFilePicker = Components.interfaces.nsIFilePicker;
            fp.open(function (rv) {
        if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {

        var foStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
        foStream.init(fp.file, 0x02|0x20|0x08, 0666, 0);
        foStream.write(text, text.length);
        foStream.close();
            if(notificationsAfterInstall){
                                    var win = err ? ns.getMostRecentWindow() : window;
                                    win.setTimeout(function(){
                                    fileExt1 = name.match(/\.uc\.(js|xul)$/i);
                                    fileExt1 = fileExt && fileExt[1] ? fileExt[1] : "js";
                                    ns.showInstallMessage({
                                        fileExt: fileExt1,
                                        fileName: fileName,
                                        file: fp.file,
                                        charset: charset
                                    });
                                }, 100);
                                      }
             }
         });

    },
    showInstallMessage: function(info){
        var isRun = (info.fileExt == "js");

        var mainAction, secondActions;
        if(runWithoutRestart && isRun){
            mainAction = {
                label: "Выполнить без перезапуска.",
                accessKey: "a",
                callback: function(){
                var dir = Services.dirsvc.get("UChrm", Ci.nsIFile);
                dir.initWithPath(dir.path + "\\" + info.fileName);
                var url = Services.io.newFileURI(dir).spec;
                let context = {};
                Services.scriptloader.loadSubScript( url, context, "UTF-8");

             var showedMsg1 = ns.popupNotification({
             id: "userchromejs1-install-popup-notification",
             message: "Скрипт запущен",
             options: {
                removeOnDismissal: true,
                persistWhileVisible: true
                      }
                   });
                }
            };
            secondActions = [{
                label: "Перезагрузите сейчас",
                accessKey: "s",
                callback: ns.restartApp
            }];
        }else{
            mainAction = {
                label: "Перезагрузите сейчас",
                accessKey: "s",
                callback: ns.restartApp
            };
            secondActions = null;
        }

        var showedMsg = ns.popupNotification({
            id: "userchromejs-install-popup-notification",
            message: "'" + info.fileName + "' Установка завершена.",
            mainAction: mainAction,
            secondActions: secondActions,
            options: {
                removeOnDismissal: true,
                persistWhileVisible: true
            }
        });
    },
    popupNotification: function(details){
        var win = ns.getMostRecentWindow();
        if (win && win.PopupNotifications) {
            win.PopupNotifications.show(
                win.gBrowser.selectedBrowser,
                details.id,
                details.message,
                "",
                details.mainAction,
                details.secondActions,
                details.options);
            return true;
        }

        return false;
    },
    getMostRecentWindow: function(){
        return Services.wm.getMostRecentWindow("navigator:browser")
    },
    restartApp: function() {
       Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart)
    }
};
function log(arg){ Application.console.log("[SaveUserChromeJS]" + arg);}
})();
window.saveUserChromeJS.init();

Отсутствует

 

№1444812-03-2020 22:10:38

solombala
Забанен
 
Группа: Members
Зарегистрирован: 20-07-2019
Сообщений: 652
UA: Firefox 74.0

Re: Custom Buttons

Dumby
СВ новый какой ? =base64 не хочет прописывать , или код какой?
Нашел СВ 10, ОК! Нужен  код "Удалить куки " Нет, не куки , "активные сеансы" ...
"Смена Языка кириллица-латиница в поиске" - того в 74...Кто мастерил? Князь- анархист?

Отредактировано solombala (13-03-2020 11:11:25)

Отсутствует

 

№1444913-03-2020 18:41:20

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

Re: Custom Buttons

VORON пишет

для 68й что то не найду

В add_toolbar_buttons есть такая кнопка.
В крайнем случае можно попробовать сделать экстракт для вкладки Код.

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

Выделить код

Код:

var wt = "Toolkit:PasswordManager";
var win = Services.wm.getMostRecentWindow(wt);
var uri = gBrowser.currentURI;
try {
    var filter = Services.eTLD.getBaseDomain(uri);
} catch(ex) {
    var filter = uri.asciiHost;
}
var setFilter = e => (win || e.target.ownerGlobal).setFilter(filter);

if (win) win.focus(), setFilter(); else openDialog(
    "chrome://passwordmgr/content/passwordManager.xul",
    wt, "chrome,dialog=no,centerscreen,resizable"
).addEventListener("pageshow", setFilter, {once: true});

Andrey_Krropotkin пишет

В блоке saveScript - если XMLHttpRequest() сделать асинхронным, то все рушится.

Так делал?

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

Выделить код

Код:

//xhr.open('GET', url2, false);
        //xhr.send();
        //if (xhr.status == 200) {

        xhr.open('GET', url2, true);
        xhr.onloadend = () => {
            if (xhr.status == 200) {
            // и всё дальнейшее
        }
        xhr.send();

solombala пишет

Нужен  код "Удалить куки " Нет, не куки , "активные сеансы"

Я фик знает, мне их взять негде. Может так попробуй
ChromeUtils.import("resource:///modules/Sanitizer.jsm")
    .Sanitizer.sanitize(["sessions"]);

Отсутствует

 

№1445013-03-2020 19:36:32

solombala
Забанен
 
Группа: Members
Зарегистрирован: 20-07-2019
Сообщений: 652
UA: Firefox 74.0

Re: Custom Buttons

Dumby
Хвала! Меняю резольвер и этот код  очень нужен был . Круто!

Отсутствует

 

Board footer

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