Attributes Inspector 0.6.4 (2016-02-23)
Совместимость: все приложения на движке gecko: [firefox] Firefox, [seamonkey] SeaMonkey, [thunderbird] Thunderbird etc.
Автор: Infocatcher
Описание:
Показывает всплывающую подсказку со всеми атрибутами.
Использование:
   Клик средней кнопкой мыши или левой с зажатым Ctrl открывает DOM Inspector
   (удерживайте дополнительно Shift, чтобы включить блокировщик всплывающих подсказок и меню)
   Удерживайте Shift, чтобы показывать и не скрывать всплывающие подсказки и меню
Сочетания клавиш:
   Escape – отмена или отключить блокировщик всплывающих подсказок и меню
   Ctrl+Up, Ctrl+Down – перейти к родительскому/дочернему узлу
   Ctrl+Left, Ctrl+Right – перейти к предыдущему/следующему узлу
   Ctrl+Shift+C – копировать содержимое всплывающей подсказки
   Ctrl+Shift+W – исследовать объект window узла в DOM Inspector'e

attrsInspector-ru.png

Установить: attrsInspector.html
Код: attrsInspector.js
Инициализация:

Выделить код

Код:

this.onmouseover = function(e) {
    this.focusedWindow = Components.classes["@mozilla.org/appshell/window-mediator;1"]
        .getService(Components.interfaces.nsIWindowMediator)
        .getMostRecentWindow(null);
};
this.onmouseout = function(e) {
    this.focusedWindow = null;
};
this.setAttribute("oncommand", "this.focusedWindow && this.focusedWindow.focus();");

Также код можно использовать из других расширений, позволяющих выполнять произвольный код в контексте главного окна приложения, например, из Mouse Gestures.

Известные ограничения
Проблемы с позиционированием всплывающей подсказки при перемещении по DOM-дереву в Firefox 3.0 и 3.5
В Firefox 3.0 и 3.5 невозможно перехватить клик по всплывающей подсказке
В Firefox 1.5 и 2.0 при зажатом Shift всплывающие подсказки не показываются.
В Firefox 1.5 нельзя загрузить полное дерево (ограничения соответствующей версии DOM Inspector'а)

Осторожно, это тестовая версия!

Разрабатываемая версия

Attributes Inspector 0.6.0pre11 (2012-10-18)
* Улучшена обработка удаления кнопки.
+ Добавлено центрирование выбранного узла в панели DOM Inspector'а (настройка "_nodePosition").

Attributes Inspector 0.6.0pre12 (2012-12-05)
* Улучшено определение наличия DOM Inspector'а: теперь, если не используется API DOM Inspector'а, то проверка наличия окна с API не делается.
+ Клик по кнопке с любым нажатым модификатором включает отладочный режим.
* Улучшен "встроенный" режим: если код включен в другую кнопку, оригинальный onDestroy будет сохранен.
* Оптимизирована работа с таймерами nsITimer.
+ Добавлена поддержка приватного режима: если скопировать содержимое всплывающей подсказки, буфер обмена будет очищен после выхода из приватного режима.
* Если доступен новый метод Object.defineProperty(), deprecated __defineGetter__() больше не используется.

Исправление ошибки «TypeError: Value is not callable» в Nightly 20.0a1 (2012-12-19) (https://bugzilla.mozilla.org/show_bug.cgi?id=790978)

Attributes Inspector 0.6.0 (2012-12-20)
x Исправлена возможная ошибка при использовании extDevToolsMouseGesturesLauncher.js.
x Исправлено использование MutationObserver в Firefox 20.0a1 и новее.

13-01-2013 22:06:26
Ну, вся история версий целиком, а то жалко удалять. :)

    0.6.0pre12 (2012-12-05)
* Улучшено определение наличия DOM Inspector'а: теперь, если не используется API DOM Inspector'а, то проверка наличия окна с API не делается.
+ Клик по кнопке с любым нажатым модификатором включает отладочный режим.
* Улучшен "встроенный" режим: если код включен в другую кнопку, оригинальный onDestroy будет сохранен.
* Оптимизирована работа с таймерами nsITimer.
+ Добавлена поддержка приватного режима: если скопировать содержимое всплывающей подсказки, буфер обмена будет очищен после выхода из приватного режима.
* Если доступен новый метод Object.defineProperty(), deprecated __defineGetter__() больше не используется.

    0.6.0pre11 (2012-10-18)
* Улучшена обработка удаления кнопки.
+ Добавлено центрирование выбранного узла в панели DOM Inspector'а (настройка "_nodePosition").

    0.6.0pre10 (2012-08-17)
x Исправлена подсветка узла под курсором с помощью inIFlasher в модальных окнах, открытых из окна, в котором был запущен Attributes Inspector.
* Немного оптимизирована отрисовка всплывающей подсказки.

    0.6.0pre9 (2012-08-16)
+ Реализован вывод информации об атрибутах во время перетаскивания (drag and drop). К сожалению, никакие сочетания клавиш при этом не работают.
* Оптимизировано обновление всплывающей подсказки при очень частом изменении атрибутов узла под курсором.
x Исправлена индикация копирования в модальных окнах, открытых из окна, в котором был запущен Attributes Inspector.

    0.6.0pre7 (2012-07-07)
* Убрано использование deprecated E4X.

    0.6.0pre6 (2012-07-05)
x Блокировщик всплывающих подсказок и меню работал только в окне, из которого был запущен Attributes Inspector.
* Улучшена производительность при работе с MutationObserver'ом.

    0.6.0pre5 (2012-07-05)
* Удаленные атрибуты теперь тоже сортируются и выводятся в общем списке.
* Добавлено отображение пространства имен удаленных атрибутов (будет показано только при отличии от пространства имен DOM-узла).
x При подсветке измененных атрибутов не учитывалось пространство имен.

    0.6.0pre4 (2012-07-04)
* Для отслеживания изменения атрибутов теперь используется более производительный MutationObserver (Firefox 14 и выше).
* Блокировщик всплывающих подсказок и меню отключается только при закрытии окна DOM Inspector'а, открытого после запуска блокировщика.

    0.6.0pre3 (2012-07-04)
x Исправлена обработка изменений атрибутов (ранее ошибочно учитывались изменения в дочерних узлах).

    0.6.0pre2 (2012-07-04)
+ Добавлена подсветка добавленных и измененных атрибутов и отображение удаленных атрибутов (учитываются только изменения для текущего узла, при переходе к другому узлу история изменений теряется).
- Удалены больше не требующиеся хаки для принудительной перерисовки в Firefox 16.0a1.

    0.6.0pre (2012-07-02)
* Увеличен номер версии: слишком много изменений :)
* Изменен механизм блокировки всплывающих подсказок, расширена настройка "_popupLocker". По умолчанию открытие DOM Inspector'а с зажатым Shift включает блокировку всех всплывающих меню и подсказок в окне.
* Улучшена подсветка текущего узла в Firefox 16.0a1.

    0.5.2pre (2011-08-30)
+ Добавлена возможность настройки толщины и вида рамки для выделения узла под курсором.
* Добавлена возможность перемещения по DOM-дереву по Ctrl+Shift+Up/Down (для исследования всплывающих подсказок).
+ Добавлено отображение размера узла под курсором (примечание: getBoundingClientRect() может возвращать дробные значения, они будут округлены до 3-х знаков после запятой).
+ Добавлена возможность отображения пространства имен во всплывающей подсказке ("var _showNamespaceURI = 2;").
+ При открытии меню всплывающая подсказка переоткрывается, чтобы оказаться сверху (если в ОС включена анимация при появлении или скрытии подсказок, подсказка будет немного "мигать").
+ Добавлена возможность блокирования всплывающих подсказок и меню от закрытия ("var _popupLocker = true;").
* Для получения родительского узла теперь используется inIDOMUtils.getParentForNode() - позволяет получить доступ к анонимным узлам и в Firefox 2.0 и новее позволяет перейти к родительскому фрейму.

0.6.1pre3 - 2013-03-31 не открывает средней кнопкой полосу прокрутки (скролбар) на страницах в инспекторе.

bunda1 пишет

0.6.1pre3 - 2013-03-31 не открывает средней кнопкой полосу прокрутки (скролбар) на страницах в инспекторе.

С этим ничего не сделать: событие клика или не генерируется вообще, или перехватывается где-то раньше.
Можно нажать Ctrl+I/Ctrl+Shift+I, но это тоже не поможет в большинстве случаев из-за того, что DOM Inspector эти узлы вообще не показывает.

Attributes Inspector 0.6.1 (2013-05-19)
* При копировании теперь используется системный формат перевода строк.
* Улучшена работа при использовании extDevToolsMouseGesturesLauncher.js.
+ Ctrl+Left, Ctrl+Right - перейти к предыдущему/следующему узлу.
+ Добавлена возможность отображения текстовых узлов (настройки _excludeChildTextNodes и _excludeSiblingTextNodes).
* Улучшена обработка анонимных узлов (настройка _preferNotAnonymousChildNodes).
x Исправлено обновление всплывающей подсказки после отображения большого количества атрибутов.
* Небольшие улучшения и исправления.

После добавления работает нормально. Но через некоторое время кнопка перестаёт нажиматься. В меню добавления кнопок картинка становится растянутой. Mozilla v. 21

DAntES
Надо обновиться до Custom Buttons 0.0.5.6 (некоторые подробности, которые можно было найти поиском).

Infocatcher
В [nightly] перестали работать клавиши Escape и Ctrl+Shift+C

voqabuhe
Да, я видел.
https://github.com/Infocatcher/Custom_Buttons/issues/12
В разрабатываемой версии, вроде, получилось исправить.

Infocatcher пишет

В разрабатываемой версии, вроде, получилось исправить.

Спасибо, вроде всё работает.

Attributes Inspector 0.6.2 (2013-08-15)
x Исправлена работа сочетаний клавиш (#11, #12).
+ Добавлен вывод предупреждения, если DOM Inspector не установлен.
+ Добавлено отображение отступов (#14).

Infocatcher
Разреши попросить рассмотреть возможность добавить в Attributes Inspector
некую постороннюю функциональность.

Иногда случается, что хочется посмотреть в DOM Inspector'е
окно документа [исследуемой ноды].
Но добираться до этого окна вручную как-то неудобно.
Может быть добавить какой-нибудь хоткей, чтобы оно открылось сразу.

Для себя, пока что, прицепился так, но ...

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

Выделить код

Код:

else if( // Ctrl+W, Ctrl+Shift+W
                ctrlOrCtrlShift && (
                    e.keyCode == e.DOM_VK_W // keydown || keyup
                    || e.keyCode == 0 && String.fromCharCode(e.charCode).toUpperCase() == "W" // keypress
                )
            ) {
                this._checkPreventDefault(e);
                this.stopEvent(e);
                if(onlyStop)
                    return;
                this.stopSingleEvent(top, "keyup");
                var nodes = this._nodes;
                if(nodes.length) {
                    var nWin = nodes[0].ownerDocument.defaultView;
                    if(ctrlShift)
                        nWin = nWin.top;
                    var iWin = openDialog(
                        "chrome://inspector/content/object.xul",
                        "_blank", 
                        "chrome,all,dialog=no",
                        nWin
                    );
                    iWin.addEventListener("load", function load() {
                        iWin.removeEventListener("load", load, false);
                        var doc = iWin.document;
                        doc.title = doc.title + " - window: " + nWin.location;
                    }, false);
                    context.stop();
                }
            }

Dumby пишет

Иногда случается, что хочется посмотреть в DOM Inspector'е
окно документа [исследуемой ноды].

Хм, в принципе, можно и добавить.
Только я как-то этим отдельным окном для исследования объектов не пользуюсь совсем, пока что сделал вот такой набросок:
https://github.com/Infocatcher/Custom_B … 233e01b57a
https://github.com/Infocatcher/Custom_B … 5f66509f8b

Infocatcher

О, круто. Большое спасибо !

Кстати, вспомнилось, что в более ранних версиях DOM Inspector'а
аттрибут viewerListEntry для нужного пункта имел другое значение,
например 2.0.10.

Dumby пишет

Кстати, вспомнилось, что в более ранних версиях DOM Inspector'а
аттрибут viewerListEntry для нужного пункта имел другое значение,
например 2.0.10.

С этим, вроде, просто:
https://github.com/Infocatcher/Custom_B … d3eaf3a4db
Заодно посмотрел, как эффективно это дело роняет 1.5. :)

voqabuhe

В [nightly] перестали работать клавиши Escape и Ctrl+Shift+C

Та же проблема в Seamonkey 2.22.1.

MySh пишет

voqabuhe

В [nightly] перестали работать клавиши Escape и Ctrl+Shift+C

Та же проблема в Seamonkey 2.22.1.

Ой, пока был в отъезде, накопилось порядочно всяких уведомлений на почте... видимо, пропустил.
Проблемы, разумеется, будут одни и те же – разработчики решили переделать логику обработки событий клавиатуры.
Но ведь это все уже исправлено. Есть правда, еще одно аналогичное исправление, оно пока только в экспериментальной версии.

Infocatcher
Ой, прошу прощения! И правда, не заметил, что уже новая версия вышла.
Спасибо! Теперь всё работает.

Infocatcher
FF 33.0a1, AI 0.6.3pre3.
Components.classes['@mozilla.org/inspector/flasher;1'] is undefined :(.

Dumby пишет

FF 33.0a1, AI 0.6.3pre3.
Components.classes['@mozilla.org/inspector/flasher;1'] is undefined :(.

Какие молодцы, заодно легко можно пересчитать адекватных разработчиков.
Я еще использовал inIFlasher.repaintElement(), так аналогов, надо полагать, теперь больше нет. Кстати, nsIDOMWindowUtils.redraw() не помогает.

Подправил: https://github.com/Infocatcher/Custom_B … 58a4d952e9 (но принудительная перерисовка как была корявой, так и осталась).

Attributes Inspector 0.6.3 (2014-06-19)
[+] Добавлено сочетание клавиш Ctrl+W для исследования объекта window текущего окна.
[+] Добавлено отображение дополнительных свойств узла документа.
[*] Немного улучшена производительность: очистка всплывающей подсказки теперь делается непосредственно перед обновлением.
[x] Исправлено отключение блокировщика всплывающих меню по нажатию Escape (#12).
[*] Уже запущенный Attributes Inspector теперь ищется во всех окнах.
[x] Добавлена возможность принудительного обновления всплывающей подсказки в Firefox 29+ (на случай проблем с отображением, настройка "_forceRepaintTooltip") (#25).
[*] Упрощено использование из Scratchpad'а (devtools.chrome.enabled = true + Окружение – Браузер): добавлена обертка, чтобы не создавать глобальные переменные.
[x] Workaround для корректной работы без inIFlasher (bug 1018324).
[+] Добавлена настройка "_maxTooltipWidth".

Infocatcher
У меня на Nightly после использования AI (0.6.3 - 2014-06-19)
перестали удаляться из окна его listener'ы.

Оказалось, что вместо removeEventListener там теперь ... какая-то.
Вот картинка с чистого профиля.
Вроде так помогает:

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

Выделить код

Код:

function rel(type, func, useCapture, target) {
    //return (target || window).removeEventListener(type, func, useCapture);
    return EventTarget.prototype.removeEventListener.call((target || window), type, func, useCapture);
}

Dumby пишет

У меня на Nightly после использования AI (0.6.3 - 2014-06-19)
перестали удалятся из окна его listener'ы.

Оказалось, что вместо removeEventListener там теперь ... какая-то.
Вот картинка с чистого профиля.

Угу, я уже видел, видимо, объe10s'или что-нибудь.
Впрочем, там все равно надо будет что-то делать насчет e10s, только пока что не понятно: https://bugzilla.mozilla.org/show_bug.cgi?id=1051238

Dumby пишет

Вроде так помогает:

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

Выделить код

Код:

function rel(type, func, useCapture, target) {
    //return (target || window).removeEventListener(type, func, useCapture);
    return EventTarget.prototype.removeEventListener.call((target || window), type, func, useCapture);
}

О, спасибо!
Как назло в старых версиях или нету EventTarget.prototype.removeEventListener, или вываливается с ошибкой при вызове (или, кажется, тупо не делает ничего).
Пока вот так: https://github.com/Infocatcher/Custom_B … 01a763f045

Infocatcher
Здравствуйте.
На последней ночнушке (Win 8.1 x64, Firefox Nightly 41.0a1 x64 BuildID:20150524030234, CB 0.0.5.9pre1, AI 0.6.3, DOM Inspector 2.0.15), при использовании блокировщика всплывающих подсказок и меню, окно DOM Inspector'а перестало реагировать на левую клавишу мыши.
Поправка: это происходит, кажется, только если использовать блокировщик всплывающих подсказок и меню на контекстном меню urlbar'а. :)
Спасибо.

turbot
Вижу... как чинить – не понятно, но записал: https://github.com/Infocatcher/Custom_Buttons/issues/37
У меня и на релизном Firefox 38.0.1 воспроизводится.

Infocatcher пишет

У меня и на релизном Firefox 38.0.1 воспроизводится.

Обнаружил просто после обновления ночнушки, и сначала грешил на нее. Надо было переписать пост, но поленился. Виноват.

Infocatcher
Тут просили переделать по СКМ копировать тултип, да, стало более оперативно копировать название селектора, что в большинстве случаев и нужно, но иногда нужно и DOM запускать. Может можно копирование добавить без отключения вызова DOM, например на СКМ или ПКМ?

Чтобы не потерялось: начало про копирование.

villa7 пишет

Может можно копирование добавить без отключения вызова DOM, например на СКМ или ПКМ?

ПКМ занимать нельзя, будет неудобно исследовать контекстные меню.
И все равно ведь остается Ctrl(+Shift)+I.

а нет ли возможности сменить сочетание Ctrl+W на что-то другое? постоянно использовал для закрытия вкладки. а вчера дошли руки обновить кнопку и тут сюрприз.

спасибо.

z_mashine пишет

а нет ли возможности сменить сочетание Ctrl+W на что-то другое?

Хм, ну там все равно одно и то же было для Ctrl+W и Ctrl+Shift+W...
Оставил только второе сочетание: https://github.com/Infocatcher/Custom_B … 788f8811ac (установить можно отсюда).

На FF45 после выключения кнопки в консоли ошибок бесконечно появляется сообщении о ошибке:

Выделить код

Код:

Error: TypeError: tt.showPopup is not a function
Source File: chrome://custombuttons-context/content/button.js?windowId=Firefox&id=custombuttons-button4@code line 1 > Function
Line: 1088

bunda1
Ой, там вот это исправление было (и пора релиз выпускать). По идее, вот эта версия уже должна правильно работать.

Спасибо, теперь вроде всё нормально.

Attributes Inspector 0.6.4 (2016-02-23)
[x] Workaround для removeEventListener() при использовании compatibility shims для мультипроцессного режима (используются даже при отключенном мультипроцессном режиме).
[*] Улучшен способ принудительного обновления всплывающей подсказки в некоторых случаях (#30).
[-] Удалено сочетание клавиш Ctrl+W для исследования объекта window, используйте вместо него Ctrl+Shift+W.
[x] Некоторые фиктивные исправления для мультипроцессного режима.

Infocatcher
Словил падение браузера, в последних ночнушках (еще ~неделю назад этого не было) если изменена ширина попапа авесомбара так (либо с CTR) и стилем

Выделить код

Код:

#PopupAutoCompleteRichResult {
    margin: auto !important;
  }

При уводе курсора с попапа, с активным Attributes Inspector, браузер падает.
Это к вам, на багзиллу или просто себе :dumb:? :rolleyes:

turbot
Там из всех вмешательств только атрибут вешается и стили для него, так что со стороны кнопки и не поделаешь особо ничего.
Да если бы и делалось чего-нибудь этакое – падать не должно. Так что я за багзиллу. Будут ли править – еще вопрос, но вот что на обычных стилях можно так уронить – это не дело. Может там память портится, вдруг со страницы такое же провернуть можно. И еще и запустить чего.

Infocatcher
Понял. Спасибо. Попробую-таки одолеть багзиллу (или на мозиллазине отпишусь, в теме ночнушек. Там опытные в заведении багов и английском люди бывают).

Не успел. :D Похоже, исправили в последней ночнушке.

А поправить кнопку для работы в Pale Moon 26.5.0,27.4.0 не работает Escape,Ctrl+Shift+C,Ctrl+Shift+W...?
Пробывал с разными версиями CB.

rgdru пишет

А поправить кнопку для работы в Pale Moon 26.5.0,27.4.0 не работает Escape,Ctrl+Shift+C,Ctrl+Shift+W...?

Автор Pale Moon упоролся. :(
Когда основан на Gecko 28 с добавками (вроде как), но Services.appinfo.platformVersion возвращает "3.2.2".
Должно помочь: https://github.com/Infocatcher/Custom_B … 8fd4068656
Проверял только на Pale Moon 27.4.0.

Спасибо! Сейчас нормально работает и в 26.5.0 тоже.

Infocatcher пишет

основан на Gecko 28 с добавками (вроде как), но Services.appinfo.platformVersion возвращает "3.2.2".

Может на 38, ну а то что возвращает "3.2.2" так это скорее всего Goanna 3.2.2

Vitaliy V. пишет

Может на 38, ну а то что возвращает "3.2.2" так это скорее всего Goanna 3.2.2

Я поискал официальные упоминания, но каких-то особых привязок к версиям Firefox/Gecko не нашел, тут же функциональность важна, а не портированные исправления безопасности. Хорошо еще, что такая (не особо правильная) проверка только одна делается.

ДОбрый день! Как это сейчас установить?

69from пишет

Как это сейчас установить?

С помощью распорок: https://forum.mozilla-russia.org/viewto … 48#p745048
И обновленной версии Custom Buttons: https://forum.mozilla-russia.org/viewto … 57#p746057

Или можно запускать из встроенного редактора кода.
devtools.chrome.enabled = true
Shift+F4
В редакторе выбрать Окружение – Браузер
Вставить туда весь код и нажать Запустить

Infocatcher пишет
69from пишет

Как это сейчас установить?

С помощью распорок: https://forum.mozilla-russia.org/viewto … 48#p745048
И обновленной версии Custom Buttons: https://forum.mozilla-russia.org/viewto … 57#p746057

Или можно запускать из встроенного редактора кода.
devtools.chrome.enabled = true
Shift+F4
В редакторе выбрать Окружение – Браузер
Вставить туда весь код и нажать Запустить

А можно как-то проще? Чтобы не копать что такое распорки, переходя из одной ветки в другую? Custom Buttons не нашел версию, которая может установиться...
А про редактор кода понял, но что туда писать — опять же вопрос. Я в стилях горазд разобраться, но писать скрипты без помощи — на это времени не напасешься.

69from
Вот портативный F57 с отключеной проверкой цифровых подписей, папке с браузером находится Сustom Buttons который надо установить: https://yadi.sk/d/Xv4A8_Ir3PyJky

bunda1 пишет

69from
Вот портативный F57 с отключеной проверкой цифровых подписей, папке с браузером находится Сustom Buttons который надо установить: https://yadi.sk/d/Xv4A8_Ir3PyJky

дык у меня мак

Infocatcher
Bug 1427419
InspectorUtils.webidl
Так?

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

Выделить код

Код:

get dwu() {
            delete this.dwu;
            /*
            return this.dwu = Components.classes["@mozilla.org/inspector/dom-utils;1"]
                .getService(Components.interfaces.inIDOMUtils);
            */
            return this.dwu = "inIDOMUtils" in Components.interfaces
                ? Components.classes["@mozilla.org/inspector/dom-utils;1"]
                    .getService(Components.interfaces.inIDOMUtils)
                : InspectorUtils;
            
        },

Dumby пишет

О! Спасибо, обновил: https://github.com/Infocatcher/Custom_B … fa3c18c6a6
Самое ведь интересное – обильные правки в 27 частей без видимой пользы.
Вероятность ошибок-то растет... К примеру, эта опечатка до сих пор в коде.

И бедный DOM Inspector ломается все больше. :(

Привет, Infocatcher
Я как-то просил вас переделать в Attributes Inspector копирование атрибутов на СКМ
Не подскажете, что поменять в новом скрипте?

Ultima2m пишет

Я как-то просил вас переделать в Attributes Inspector копирование атрибутов на СКМ
Не подскажете, что поменять в новом скрипте?

Так ведь должна по-прежнему работать предыдущая замена, код в этой части не менялся.

Infocatcher пишет

Так ведь должна по-прежнему работать

Да я сдуру весь код заменил, а про старые исправления и забыл совсем - думал кнопку с исправлениями целиком ставил. Склероз.
Спасибо, исправил.

Привет, Infocatcher.
После обновления до ФФ58 отвалилось копирование по СКМ.
(У меня-то спец.кнопка :sick:)
Ctrl+Shift+C работает нормально.
Что посоветуешь?

Infocatcher
Разреши проконсультироваться.
Как лучше делать проверку на bug 1476145?
Пока сделал так

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

Выделить код

Код:

getScreenRect: function(node) {
            var win = node.ownerDocument.defaultView;
            var scale = 1;
            try {
                //var utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                //    .getInterface(Components.interfaces.nsIDOMWindowUtils);
                var utils = "windowUtils" in win && win.windowUtils
                    instanceof Components.interfaces.nsIDOMWindowUtils
                    ? win.windowUtils
                    : win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindowUtils);
                scale = utils.screenPixelsPerCSSPixel || 1;
            }

Dumby
Так и хорошо же. А с win.windowUtils || win.QueryInterface(…) можно нарваться на чужую глобальную переменную в старых версиях.
Обновил, спасибо: https://github.com/Infocatcher/Custom_B … f51f9dee5f
(заодно оказалось, что уже накопилось недопроверенное и незапушенное)

Скажите пожалуйста, а на 61и выше DOM Inspector работает? Я обновился с 59 на 61 и уменя стояла последняя версия Dom с сайта. В расширения он присутствует, а ваша кнопка (то же последняя версия с вашей странички) пишет, что он не найден. При попытке переустановить Dom пишем что в архиве ошибка, хотя это не так. Такая же ситуация и с консоль2 (с поддержкой до 63). Скачал на вашей страничке. В 59 все устанавливается и кнопка появляется, а на 61стала не активна и кнопка пропала. При переустановке пишет что тоже архив повреждён. Что  можете подсказать. Custom Buttons работает без проблем. Паратры для установки старых приложений включены.

И DOM Inspector, и Console² точно ломались где-то в процессе обновления Nightly, но точных дат и версий я не помню.
Судя по 1448162 - Disable XUL overlays, в Firefox 61+ их принципиально не запустить без переписывания в виде restartless bootstrap расширения.
По поводу якобы поврежденного архива – возможно, нужно обновить лекарство от цифровых подписей.

Infocatcher
А альтернатив нет для 63+ версий? Или девелоперских с webextensions experiments?

Karn пишет

А альтернатив нет для 63+ версий? Или девелоперских с webextensions experiments?

Есть встроенный Browser Toolbox, но он работает через удаленную отладку и, похоже, не позволяет задать узел для исследования внешним вызовом.
А альтернативы кто-то написать должен, причем в условиях, когда разработчики браузера стали ломать обратную совместимость усерднее прежнего. Я альтернатив не видел.
Новая консоль ошибок вон ущербная, уже сколько версий не посмотреть и не отфильтровать нормально ошибки, относящиеся к конкретному расширению... Даже ссылку на файл, из которого прилетела ошибка, не скопировать.

Infocatcher
Спасибо, да уж, придётся кастомить методом тыка и гугла, Browser Toolbox очень неудобная штука. Ладно хоть пока многое работает и сам браузер ускоряется, после 56 ощущается.

Infocatcher
Bug 1482389 - Convert TreeBoxObject to XULTreeElement
Таким образом слетели коды прокрутки в деревьях.
Сделал пока так, вроде работает

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

Выделить код

Код:

if(_nodePosition >= 0) {
                    /*
                    var tbo = viewer.mDOMTree.treeBoxObject;
                    var cur = tbo.view.selection.currentIndex;
                    var first = tbo.getFirstVisibleRow();
                    var visibleRows = tbo.height/tbo.rowHeight;
                    */
                    if("nsITreeBoxObject" in Components.interfaces) {
                        var tbo = viewer.mDOMTree.treeBoxObject;
                        var visibleRows = tbo.height/tbo.rowHeight;
                    } else {
                        var tbo = viewer.mDOMTree;
                        var visibleRows = tbo.getPageLength();
                    }
                    var cur = tbo.view.selection.currentIndex;
                    var first = tbo.getFirstVisibleRow();

...

                                    _log('inspectWindow(): scroll to "defaultView" entry');
                                    //var tbo = tree.treeBoxObject;
                                    var tbo = "nsITreeBoxObject" in Components.interfaces
                                        ? tree.treeBoxObject
                                        : tree;


На всякий случай, чтоб проверить, мои останки DOMi
dom_inspector-7.0.1-fx-paxmod.xpi
dom_inspector-7.0.1-fx-bootstrap.xpi

Dumby
О, DOMi! Я скучал по нему.
Записался в свидетели живого DOMi и обновил.
Спасибо!

Infocatcher  пишу Вам здесь, так не нашел отдельной темы. Кнопка  - New Button at Right from current работает и на 66.03, но в связи с их политикой, не  запоминает настройки. Т.е. после перезагрузки или при новом включении постоянно приходится вручную нажимать на кнопку. Можно ли как нибудь это обойти, чтобы запоминались настройки?
Да, еще Вы как то говорили, что в скором будущем исправите Custom Buttons: Source Editor для новых версий, можно ли ожидать это в ближайшее время?

Infocatcher
Bug 1568585 - Stop implementing JS-exposed QueryInterface on Window
Пришлось править. Вот getParentNode() даже не знаю, вроде не нужно, просто за компанию.

Ссылки для .docShell и .domWindow, оба Firefox 63+
Bug 1463291 - Move Document.docShell getter to Window
Bug 1463016: Part 5 - Add domWindow property to DocShellTreeItem and update callers to use it.

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

Выделить код

Код:

setClipboardData: function(dataObj, sourceWindow, clipId) {
            var ta = Components.classes["@mozilla.org/widget/transferable;1"]
                .createInstance(Components.interfaces.nsITransferable);
            if(sourceWindow && "init" in ta) {
                // The clipboard will be cleared when private browsing mode ends
                /*
                var privacyContext = sourceWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIWebNavigation)
                    .QueryInterface(Components.interfaces.nsILoadContext);
                */
                var privacyContext = this.fxVersion >= 70
                    ? sourceWindow.docShell.QueryInterface(Ci.nsILoadContext)
                    : sourceWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIWebNavigation)
                        .QueryInterface(Components.interfaces.nsILoadContext);
Выделить код

Код:

getParentNode: function(node) {
            try {
                var pn = this.domUtils.getParentForNode(node, true);
            }
            catch(e) {
                if(("" + e).indexOf("NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE") == -1)
                    Components.utils.reportError(e);
                pn = node.parentNode;
            }
            if(!pn && node.nodeType == Node.DOCUMENT_NODE) { // Firefox 1.5?
                /*
                pn = node.defaultView.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIWebNavigation)
                    .QueryInterface(Components.interfaces.nsIDocShell)
                    .chromeEventHandler;
                */
                pn = (this.fxVersion >= 70
                    ? node.ownerGlobal.docShell
                    : node.defaultView.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIWebNavigation)
                        .QueryInterface(Components.interfaces.nsIDocShell)
                )
                    .chromeEventHandler;
Выделить код

Код:

getTopWindow: function(node) {
            var win = node.ownerDocument && node.ownerDocument.defaultView
                || node.defaultView
                || node;
            //for(;;) {
            //    var browser = this.domUtils.getParentForNode(win.document, true);
            //    if(!browser)
            //        break;
            //    win = browser.ownerDocument.defaultView.top;
            //}
            try {
                /*
                return win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIWebNavigation)
                    .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                    .rootTreeItem
                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIDOMWindow);
                */
                return this.fxVersion >= 70
                    ? window.docShell.rootTreeItem.domWindow
                    : win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIWebNavigation)
                        .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                        .rootTreeItem
                        .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                        .getInterface(Components.interfaces.nsIDOMWindow);

Dumby
Спасибо!

Я правильно понимаю, что есть/будет DOM Inspector посвежее?
Тот, что по ссылке, сообщает мне о TypeError: data[index] is undefined из chrome://inspector/content/ViewerRegistry.jsm:55:3

Заодно стал проверять насчет nsITransferable.init()... похоже, фишку благополучно утопили:
Bug 1166840 - Remove the document argument from the clipboard helper APIs
Bug 1167952 - Remove unused document argument in uses of nsIClipboardHelper.{copyString|copyStringToClipboard}

If I understand it correctly, I should remove "aContext" parameter from "nsITransferable.init" method, and removes all of its usages in both C++ and JS. Right?

Судя по https://bug1166840.bmoattachments.org/a … id=8609184, достаточно nsITransferable.init(null):

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

Выделить код

Код:

// create a transferable for putting data on the clipboard
   nsCOMPtr<nsITransferable>
     trans(do_CreateInstance("@mozilla.org/widget/transferable;1", &rv));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
 
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
-  nsILoadContext* loadContext = doc ? doc->GetLoadContext() : nullptr;
-  trans->Init(loadContext);
+  trans->Init(nullptr);


Хвост у .getParentNode() и правда наследный... можно не трогать.

А вот window.docShell.rootTreeItem.domWindow вернет не то окно, если инспектор запустить из браузера, а кликнуть по какому-нибудь другому окну. Вроде как, вполне себе помогает window -> win

Пока вот так:
https://github.com/Infocatcher/Custom_B … ff8b32d723
На первый взгляд – работает.

Infocatcher
Упс, win, конечно же, проявил невнимательность.

ViewerRegistry.jsm — там rdf'ку читает fetch и отдаёт DOMParser'у, но грянул
Bug 467035 - <!DOCTYPE> ignores contentaccessible, leaks DTD strings and therefore browser UI locale
а у viewer-registry.rdf как раз такой DOCTYPE, с .dtd внутри.
Но если DOMParser попросить, вызвав forceEnableDTD(), то снова будет парсить.

dom_inspector-7.0.4a1-fx-paxmod.xpi
dom_inspector-7.0.4a1-fx-bootstrap.xpi

Спасибо, DOM Inspector ожил. :rock:

Но если DOMParser попросить, вызвав forceEnableDTD()…

Ну пожалуйста, работай!..
Честно говоря, я бы этим деятелям руки оторвал: раз есть включалка, то, надо думать, парсер делает некую проверку... и вот из этой проверки можно бы написать в консоль, что более не работает, вот ссылка на документацию.

Приветствую всех.
Пару месяцев назад пытался перейти на FF 67.Столкнулся с проблемой.
Dom Inspector не выделял узел дерева при клике мышкой.
Где то здесь на форуме было решение этой проблемы,которой я в то время благополучно воспользовался.
Но после FF 67 удалил.А сейчас пришло время ковырять FF 68,проблема вернулась а как её решить не могу найти.
Не помню где искал и что делал.

questman пишет

Приветствую всех.
Пару месяцев назад пытался перейти на FF 67.Столкнулся с проблемой.
Dom Inspector не выделял узел дерева при клике мышкой.
Где то здесь на форуме было решение этой проблемы,которой я в то время благополучно воспользовался.
Но после FF 67 удалил.А сейчас пришло время ковырять FF 68,проблема вернулась а как её решить не могу найти.
Не помню где искал и что делал.

Скорее всего у вас стоит Attributes Inspector версии 0.6.4.1
Вам нужна на данный момент версия 0.6.5pre
Когда будете устанавливать не нажимайте ссылку "Установить: attrsInspector.html" в этой теме.
Воспользуйтесь ссылкой "Код: attrsInspector.js", как раз эта более свежая версия.

05-08-2019 23:15:33

questman пишет
questman пишет

Приветствую всех.
Пару месяцев назад пытался перейти на FF 67.Столкнулся с проблемой.
Dom Inspector не выделял узел дерева при клике мышкой.
Где то здесь на форуме было решение этой проблемы,которой я в то время благополучно воспользовался.
Но после FF 67 удалил.А сейчас пришло время ковырять FF 68,проблема вернулась а как её решить не могу найти.
Не помню где искал и что делал.

Скорее всего у вас стоит Attributes Inspector версии 0.6.4.1
Вам нужна на данный момент версия 0.6.5pre
Когда будете устанавливать не нажимайте ссылку "Установить: attrsInspector.html" в этой теме.
Воспользуйтесь ссылкой "Код: attrsInspector.js", как раз эта более свежая версия.

Спасибо, всё заработало!
Приятно пообщаться с хорошим человеком!:lol:

Infocatcher опять пишу здесь по поводуCustom Buttons: Source Editor, т.к. здесь до Вас легче достучаться.
Не работали пункты контекстного меню на 68, я добавил в menuitem - oncommand

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

Выделить код

Код:

<menuitem id="menu_undo" label="&undoCmd.label;" accesskey="&undoCmd.accesskey;" oncommand="goDoCommand(\'cmd_undo\')" />\
                    <menuitem id="menu_redo" label="&redoCmd.label;" accesskey="&redoCmd.accesskey;" oncommand="goDoCommand(\'cmd_redo\')" />\
                    <menuseparator/>\
                    <menuitem id="menu_cut" label="&cutCmd.label;" accesskey="&cutCmd.accesskey;" oncommand="goDoCommand(\'cmd_cut\')" />\
                    <menuitem id="menu_copy" label="&copyCmd.label;" accesskey="&copyCmd.accesskey;" oncommand="goDoCommand(\'cmd_copy\')" />\
                    <menuitem id="menu_paste" label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;" oncommand="goDoCommand(\'cmd_paste\')" />\
                    <menuitem id="menu_delete" label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;" oncommand="goDoCommand(\'cmd_delete\')" />\
                    <menuseparator/>\
                    <menuitem id="menu_selectAll" label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;" oncommand="goDoCommand(\'cmd_selectAll\')" />


Не знаю правильно это или нет, но вроде работает. Не решенным осталось еще, что не работает кнопка "Внешний редактор", вообще не как не реагирует.
Может Вы все таки посмотрите, ну просто без кнопки, такое чувство, что что-то не хватает.

Andrey_Krropotkin
Я обычно и в других темах тоже вижу, просто не всегда есть время на осмысленные ответы.

Andrey_Krropotkin пишет

Не знаю правильно это или нет, но вроде работает.

На удивление не все еще отломали. У меня было опасение, что дублирование command/oncommand (от загружаемого оверлея в старых версиях и дописанное вручную) может привести к дублирующейся отработке команд, но, вроде (как минимум, в Firefox 52 и 56), работает нормально.
Добавил, спасибо: https://github.com/Infocatcher/Custom_B … a2ec1e4d2d

Заодно оживил в новых версиях: https://github.com/Infocatcher/Custom_B … fd057a0614


Andrey_Krropotkin пишет

Не решенным осталось еще, что не работает кнопка "Внешний редактор", вообще не как не реагирует.
Может Вы все таки посмотрите, ну просто без кнопки, такое чувство, что что-то не хватает.

Я не пользуюсь... В самых новых – и вовсе редактирование поломало, я открытием во вкладке спасаюсь. Dumby, насколько я помню, тоже говорил, что не пользуется...

Да-да, не пользуюсь, и Source Editor'ом тоже.
Не потому, что нехорош, а просто не пользуюсь.

Открытие внешнего редактора показалось мне
настолько простым, что думал Андрей подкумекает
это если не в тот же день, то на следующий.
Я просто дописал в начало метода initWindow()

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

Выделить код

Код:

window.edit_button = function edit_button() {
                var panel = document.getElementById(
                    "custombuttons-editbutton-tabbox"
                ).tabpanels.selectedPanel;
                if (/sourceEditor-(.+)/.test(panel.id))
                    panel = document.getElementById(RegExp.$1);
                if (panel.localName == "cbeditor")
                    window.edittarget(panel);
            }


И, ещё «See also» нарисовался:
Bug 1566457 - Loaders summer cleanup
То есть, предположительно, так
скрытый текст

Выделить код

Код:

//var require = Components.utils.import(loader, {}).devtools.require;
                var g = Components.utils.import(loader, {});
                var require = (g.devtools || g).require;

Infocatcher и Dumby спасибо и вот еще по этому коду вопрос (может только у меня - не знаю): неправильно работает выделение в URL кнопки, Имя, Изображение. Текст мышкой не выделяется (или как у меня двойной дубль-клик), но если нажать в контекстном меню после этого копировать или выделить все и копировать, то текст нормально копируется (т.е. не видно полосы выделения текста).

Andrey_Krropotkin пишет

(может только у меня - не знаю)

Подтвержаю, у меня на Win7 выделенное там тоже не подсвечивается фоном.
И, я тебе в начало метода initWindow() добавить посоветовал,
нет, в конец, конечно же.

Выделение можно стилем задать, а кодом — ничего лучше не придумал,
тоже в конец метода initWindow()

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

Выделить код

Код:

if(this.platformVersion >= 68) (function(events, win) {
                var listener = function(e) {
                    if(e.type == "unload") return destroy();
                    var sheets = win.document.styleSheets;
                    for(var ind = sheets.length - 1; ind; ind--) {
                        var sheet = sheets.item(ind);
                        if(sheet.href != "resource://devtools/client/themes/common.css")
                            continue;
                        destroy();
                        for(let ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
                            var rule = sheet.cssRules.item(ind);
                            if(rule.selectorText && rule.selectorText == "::selection")
                                return sheet.deleteRule(ind);
                        }
                    }
                }
                var destroy = function() {
                    events.forEach(function(type) {
                        win.removeEventListener(type, listener, false);
                    });
                }
                events.forEach(function(type) {
                    win.addEventListener(type, listener, false);
                });
            })(["mousedown", "keydown", "unload"], window);

Dumby спасибо, теперь вообще комфортнее стало.

Dumby есть ещё маленький такой момент, у Infocatcher есть ещё кнопка редактировать во вкладке, при совместной работе с  Source Editor-ом на 68 проблем не было, и в редакторе и во вкладке все было одинаково, на 69, Source Editor работает только в редакторе, а во вкладке как обычно, т. е. Source Editor не применяется, посмотри пожалуйста. В какой из этих двух кнопках где-то неисправность не знаю.

Andrey_Krropotkin пишет

В какой из этих двух кнопках где-то неисправность не знаю.

В коде Source Editor'а поиск:
isBrowserWindow: function(window) {

Как только увидишь — всё сразу станет ясно.

Dumby спасибо, слона и не заметил, заменил, все работает.

В 71 перестал работать, также как и dom inspector

Garalf пишет

В 71 перестал работать, также как и dom inspector

DOM Inspector и правда снова отвалился...
А вот Attributes Inspector, на первый взгляд, живой:

ntiWJv7.png

mqUnF2d.png

Что конкретно не работает и что пишет в консоль (Ctrl+Shift+J)?

Garalf пишет

В 71 перестал работать

В [firefox] 71 работает!

Да и в 73, на данный момент, работает.

А DOMi, ну давайте попробую скинуть.

DOMi-7.0.{4a1-5}-fx.js

Выделить код

Код:

// DOMi-7.0.{4a1-5}-fx.js
(async re => {
    var gzip = "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";

    var sel = "Select";
    try {sel = Services.strings.createBundle("chrome://global/locale/commonDialogs.properties")
        .GetStringFromName(sel);} catch(ex) {}
    var picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
    picker.init(window, sel + " DOMi 7.0.4a1", picker.modeOpen);
    picker.appendFilter(null, "dom_inspector-7.0.4a1-fx-*.xpi");
    await new Promise(resolve => picker.open(resolve));
    var {file} = picker; if (!file) return;
    var ln = file.leafName;
    if (!re.test(ln)) return alert("???\n" + ln);
    var {fileURL} = picker;

    var xpi = file.parent.clone();
    xpi.append(ln = ln.replace("4a1-", "5-"));
    file.copyTo(file.parent, ln);

    var obs = {}, data;
    var td = new TextDecoder(), te = new TextEncoder();
    var scs = Cc["@mozilla.org/streamConverters;1"].getService(Ci.nsIStreamConverterService);
    var sis = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
    var sl =  Cc["@mozilla.org/network/stream-loader;1"].createInstance(Ci.nsIStreamLoader);

    sis.data = atob(gzip);
    obs.onStreamComplete = (a, b, c, d, result) => data = td.decode(new Uint8Array(result));
    sl.init(obs);
    var converter = scs.asyncConvertData("gzip", "uncompressed", sl, null);

    converter.onStartRequest(null, null);
    var args = [null, null, sis, 0, sis.data.length];
    if (converter.onDataAvailable.length == 4) args.shift();
    converter.onDataAvailable(...args);
    converter.onStopRequest(null, null, null);

    var zw = Cc["@mozilla.org/zipwriter;1"].createInstance(Ci.nsIZipWriter);
    var mt = Date.now() * 1000, cp = zw.COMPRESSION_DEFAULT;
    var bootstrap = ln.includes("bootstrap");
    var prefix = "jar:" + fileURL.spec + "!/";
    var lr = /^¦(?:\d+)?$/, sep1 = "£", sep2 = "¥";

    zw.open(xpi, 0x04); // PR_RDWR
    for(var item of data.split(sep1)) {
        var [entry, val] = item.split(sep2);
        if (bootstrap && (entry == "manifest.json" || entry == "startup.jsm")) continue;
        if (val == "+") {
            zw.addEntryDirectory(entry, mt, false); continue;
        }
        if (zw.hasEntry(entry)) {
            if (val.includes("¦")) {
                var lines = val.split("\n");
                var oldLines = (await (await fetch(prefix + entry)).text()).split("\n");

                lines.forEach((line, ind) => {
                    if (lr.test(line)) lines[ind] = oldLines[
                        line.length == 1 ? ind : +line.slice(1)
                    ];
                });
                val = lines.join("\n");
            }
            zw.removeEntry(entry, false);
            if (val == "-") continue;
        }
        var stream = Cc["@mozilla.org/io/string-input-stream;1"]
            .createInstance(Ci.nsISupportsCString);
        stream.data = String.fromCharCode(...new Uint8Array(te.encode(val)));
        zw.addEntryStream(entry, mt, cp, stream, false);
        stream.close();
    }
    zw.close(); xpi.reveal();
})(
    /^dom_inspector-7\.0\.4a1-fx-(?:paxmod|bootstrap)\.xpi$/
);

Infocatcher, Dumby в кнопке для 71 версии

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

Выделить код

Код:

// https://github.com/Infocatcher/Custom_Buttons/tree/master/CB_Editor_Toggle_on_Top
// (c) Infocatcher 2012-2015
// version 0.1.11 - 2015-06-04
 // Hotkey: Ctrl+T 

const watcherId = "customButtonsToggleOnTop_" + this.id;
var {Components} = window; // Prevent garbage collection in Firefox 3.6 and older
var storage = (function() {
    if(!("Services" in window)) // Firefox 3.6 and older
        return Application.storage;
    var global = Components.utils.import("resource://gre/modules/Services.jsm", {});
    var ns = "_cbEditorToggleOnTopStorage";
    var storage = global[ns] || (global[ns] = Components.utils.getGlobalForObject(global).Object.create(null));
    return {
        get: function(key, defaultVal) {
            if(key in storage)
                return storage[key];
            return defaultVal;
        },
        set: function(key, val) {
            if(key === null)
                delete storage[key];
            else
                storage[key] = val;
        }
    };
})();
var watcher = storage.get(watcherId, null);
if(!watcher) {
    watcher = {
        btnPos: 0, // 0 - at top right window corner, 1 - at end of tabs, 2 - before dialog buttons spacer
        btnStyle: "toolbarbutton", // "button" or "toolbarbutton"
        btnChecked: true, // use "checked" style: true or false
         // http://www.iconfinder.com/icondetails/12276/16/gps_location_pin_icon
        icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjAx4SevkwAAAjxJREFUKM+FUk1oE0EU/mZnRU0U6cFLIT1KKkkNpIcSWwUvVty2l142hZpDoQcPgYQKQkUEz6V6sHgR/KGQQwleQg/xEKRRsIEmga4NoZa2oRGKpJaWmp2Z52UX12Dxg4838+D73ps3jzUYQycIgDc7l0oNAkgqpZ4n5uc/NgHsEEHDKZB/X5OJRGJcSplemJ6+6SZPFStvJ0Ta4eEhTNMcE0I8fDsxcQsAdPwHc6nUoBRCOz4+Rr1eR39//+18Pn/h/cjIGea+eS6VegLgKoBfRPRdKbXGOf8GIBmPx8cty0Kz2YTf74ff70cmkynpNSIAwP7+/t2ZmZnowcGBPDo6+rm1tdWo1Wo7pmnesSwLtm2jWCx+EkJwKSUnoln2wWnv9eTkMBE9TafT0b29PbTbbXDO0Wq1YNs2lpaWVpVSj8ZyuWXhaPg953CtXK6v9fVtr6ys9A4NDXU3Gg24JoVC4fPJycnjsVxu2TtMzgCUAVQAUKVSrwDtzc3NQCQS6d7d3XU7EK1W62twY+ML84i1N4zB5TvgYrVaPTs1NRXNZDKlrq4u+Hw+BAKBy1LK6zYA2xE+6/gqzhgbXl9fnw+FQrPBYPCHlBKjo6NRIjpPRD3kEdbcJWGMgTF2Y3Fx8UE4HE4yxl70WtYCEc1ms9kS51wDcM4r/FOS87BhGK90XTc0TeNuPgggFosNG4axOjAw8PJKxwIxAD26rt9XShWVUtvOWosO2h1RAJAMwCUAPseInGG6UTlm6l/8DZ+nFuAjSdH0AAAAAElFTkSuQmCC",
        iconPinned: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3AgNBjIpwH1VRwAAAgdJREFUKM+FkkFoU0EQhr/dtxFSquLBS8CAICISQci1oBWLKaaINL300psXDxH0ItSD4L21UKgUxV4aWnKyiIGaQwio2IQ0PQQpBaEhoaEk0dSQNsnb9WAepg+LPwz/7DD/7O7MiJIQuGEAV3QIiNowV4f0HlA0BskJsI8fo75YLHIEjw3ccIInivVxX1IqcXFu7l4Lnhq4BaD4P4Y6INsHB+zlcpwfH7+zG48PavCIvj8/B64CRxoqXdg8Bd+BqG9xMVJNpaiUy5zxehkcGCC2uppV28Y44rs3V1aCFIt2p15v/MzlSj/S6eKlmZnRairFoW2zmUx+boHVBsvAtEj+fV5Iw4vbS0tBe2uLw04H2+ulUy5zaNu8X17OdOGZgkTX3TADCRumP0xNZY3fz6/9fSr5PNVGg6/x+BfdE/Y3wxJAHtj6wzt5aHsSiQv+4WFfbWcHu9VCeDzdRq32DdgQfZMQom9JjDGngcn19fWF2shI9vrYWNBqNjFCND8mk+8MTDq5s65RWUKIUKFQmA0EAtP3pawdra0xGokE0dqrwe8kvgS2e1cjpURKORyLxTaUUg+UUuciUvIWQq8hU5uYMPOQmQcuu7fAsqxr4XD4jVIqLKW0nPgV4AmEFiDzCF65hQLwK6Ueaq0/aa13e2vddVnHxV3AFsBZYKBXyPSa6bDuFdP/st/BfOD54p3eIQAAAABJRU5ErkJggg==",

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

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

        get obs() {
            delete this.obs;
            return this.obs = Components.classes["@mozilla.org/observer-service;1"]
                .getService(Components.interfaces.nsIObserverService);
        },
        get ww() {
            delete this.ww;
            return this.ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                .getService(Components.interfaces.nsIWindowWatcher);
        },
        get wm() {
            delete this.wm;
            return this.wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                .getService(Components.interfaces.nsIWindowMediator);
        },
        init: function(reason) {
            this.obs.addObserver(this, "quit-application-granted", false);
            var ws = this.wm.getEnumerator(null);
            while(ws.hasMoreElements())
                this.initWindow(ws.getNext(), reason);
            this.ww.registerNotification(this);
        },
        destroy: function(reason) {
            this.obs.removeObserver(this, "quit-application-granted");
            var ws = this.wm.getEnumerator(null);
            while(ws.hasMoreElements())
                this.destroyWindow(ws.getNext(), reason);
            this.ww.unregisterNotification(this);
        },
        initWindow: function(window, reason) {
            if(!this.isTargetWindow(window))
                return;
            window.addEventListener("keypress", this, true);
            if(this.hasSizeModeChangeEvent)
                window.addEventListener("sizemodechange", this, false);
            else {
                window.addEventListener("resize", this, false); // Can detect only maximize/restore
                this.legacySizeModeChange(window);
            }

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

            document.insertBefore(document.createProcessingInstruction(
                "xml-stylesheet",
                'id="' + this.styleId + '" href="' + "data:text/css,"
                    + encodeURIComponent(style) + '" type="text/css"'
            ), document.documentElement);
        },
        removeStyle: function(document) {
            var mark = 'id="' + this.styleId + '"';
            for(var child = document.documentElement; child = child.previousSibling; ) {
                if(
                    child.nodeType == child.PROCESSING_INSTRUCTION_NODE
                    && child.data.substr(0, mark.length) == mark
                ) {
                    document.removeChild(child);
                    break;
                }
            }
        },
        getXulWin: function(window) {
            return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                .getInterface(Components.interfaces.nsIWebNavigation)
                .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                .treeOwner
                .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                .getInterface(Components.interfaces.nsIXULWindow);
        },
        
        setOnTop: function(btn, toggle) {
            var document = btn.ownerDocument;
            var root = document.documentElement;
            var onTop = root.getAttribute(this.onTopAttr) == "true";
            var xs = Components. classes ["@mozilla.org/xul/xulstore;1"].
        getService (Components. interfaces. nsIXULStore);
            if(toggle) {
                onTop = !onTop;
                root.setAttribute(this.onTopAttr, onTop);
                if(root.id) {
                    if (!("persist" in document)) {
                    if (!("persist" in xs))
            xs = ChromeUtils. import ("resource://gre/modules/Services.jsm"). Services. xulStore;

                document.persist = function (id, attrr){ 
                
            var node = document. getElementById (root.id);
            if (node) xs. persist (root.id, this.onTopAttr);
                 } 
                         
                    }    
                }
            }
            
            else if(!onTop) // Just opened or restored window always have zLevel == normalZ
                return;
            var window = document.defaultView;
            var state = window.windowState;
            var restore = onTop && state == window.STATE_MINIMIZED;
            if(restore || state == window.STATE_NORMAL) {
                if(restore)
                    onTop = false;
                let xulWin = this.getXulWin(window);
                xulWin.zLevel = onTop ? xulWin.raisedZ : xulWin.normalZ;
            }
            this.checkButton(btn, onTop);
        },
        toggleOnTop: function(window) {
            this.setOnTop(window.document.getElementById(this.btnId), true);
        },
        checkButton: function(btn, onTop) {
            btn.setAttribute(this.onTopAttr, onTop);
            this.btnChecked && btn.setAttribute("checked", onTop);
        }
    };
    storage.set(watcherId, watcher);
    watcher.init(watcher.REASON_STARTUP);
}
function destructor(reason) {
    if(reason == "update" || reason == "delete") {
        watcher.destroy(watcher.REASON_SHUTDOWN);
        storage.set(watcherId, null);
    }
}
if(
    typeof addDestructor == "function" // Custom Buttons 0.0.5.6pre4+
    && addDestructor != ("addDestructor" in window && window.addDestructor)
)
    addDestructor(destructor, this);
else
    this.onDestroy = destructor;


Выдает
скрытый текст
TypeError: btn is null button.js:187:4
destroyWindow chrome://custombuttons-context/content/button.js?windowId=Firefox&id=custombuttons-button58@init line 1 > Function:187
    observe chrome://custombuttons-context/content/button.js?windowId=Firefox&id=custombuttons-button58@init line 1 > Function:205

В кнопке
скрытый текст

Выделить код

Код:

// https://github.com/Infocatcher/Custom_Buttons/tree/master/CB_Source_Editor
// http://infocatcher.ucoz.net/js/cb/cbSourceEditor.js
// (c) Infocatcher 2012-2019
// version 0.1.0a9 - 2019-03-01

var options = {
    cssInHelp: true,
    codeMirror: {
        lineNumbers: true,
        enableCodeFolding: true,
        showTrailingSpace: true,
        lineWrapping: false,
        autocomplete: true,
        fontSize: 14
    },
    orion: {
        lineNumbers: true
    }
};
const watcherId = "customButtonsSourceEditor_" + this.id;
var {Components} = window; // Prevent garbage collection in Firefox 3.6 and older
var storage = (function() {
    if(!("Services" in window)) // Firefox 3.6 and older
        return Application.storage;
    var global = Components.utils.import("resource://gre/modules/Services.jsm", {});
    var ns = "_cbSourceEditorStorage";
    var storage = global[ns] || (global[ns] = Components.utils.getGlobalForObject(global).Object.create(null));
    return {
        get: function(key, defaultVal) {
            if(key in storage)
                return storage[key];
            return defaultVal;
        },
        set: function(key, val) {
            if(key === null)
                delete storage[key];
            else
                storage[key] = val;
        }
    };
})();
var watcher = storage.get(watcherId, null);
if(!watcher) {
    watcher = {
        REASON_STARTUP: 1,
        REASON_SHUTDOWN: 2,
        REASON_WINDOW_LOADED: 3,
        REASON_WINDOW_CLOSED: 4,

        get obs() {
            delete this.obs;
            return this.obs = Components.classes["@mozilla.org/observer-service;1"]
                .getService(Components.interfaces.nsIObserverService);
        },
        get ww() {
            delete this.ww;
            return this.ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
                .getService(Components.interfaces.nsIWindowWatcher);
        },
        get wm() {
            delete this.wm;
            return this.wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
                .getService(Components.interfaces.nsIWindowMediator);
        },
        get platformVersion() {
            delete this.platformVersion;
            return this.platformVersion = parseFloat(Services.appinfo.platformVersion);
        },
        get hasCodeMirror() {
            delete this.hasCodeMirror;
            return this.hasCodeMirror = Services.appinfo.name == "Pale Moon" //~ todo: test
                || this.platformVersion >= 27;
        },
        init: function(reason) {
            if(!this.hasCodeMirror) {
                this.isBrowserWindow = function() {
                    return false;
                };
            }
            this.obs.addObserver(this, "quit-application-granted", false);
            var ws = this.wm.getEnumerator(null);
            while(ws.hasMoreElements())
                this.initWindow(ws.getNext(), reason);
            this.ww.registerNotification(this);
        },
        destroy: function(reason) {
            this.obs.removeObserver(this, "quit-application-granted");
            var ws = this.wm.getEnumerator(null);
            while(ws.hasMoreElements())
                this.destroyWindow(ws.getNext(), reason);
            this.ww.unregisterNotification(this);
        },
        initWindow: function(window, reason, isFrame) {
            if(this.isBrowserWindow(window)) {
                this.initBrowserWindow(window, reason);
                return;
            }
            if(!this.isEditorWindow(window))
                return;
            _log("initWindow(): isFrame: " + isFrame);
            var document = window.document;
            if(isFrame)
                window.addEventListener("unload", this, false);

            Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
                .getService(Components.interfaces.mozIJSSubScriptLoader)
                .loadSubScript("chrome://global/content/globalOverlay.js", window);

            var isCodeMirror = false;
            try { // See chrome://browser/content/devtools/scratchpad.js
                Components.utils.import("resource:///modules/source-editor.jsm", window);
            }
            catch(e) {
                var loader = this.platformVersion >= 44 // See https://bugzilla.mozilla.org/show_bug.cgi?id=912121
                    ? "resource://devtools/shared/Loader.jsm"
                    : "resource://gre/modules/devtools/Loader.jsm";
                var g = Components.utils.import(loader, {});
                var require = (g.devtools || g).require;
                [
                    "devtools/sourceeditor/editor",
                    "devtools/client/sourceeditor/editor", // Firefox 44+
                    "devtools/client/shared/sourceeditor/editor" // Firefox 68+
                ].some(function(path) {
                    try {
                        return window.SourceEditor = require(path);
                    }
                    catch(e) {
                    }
                    return null;
                });
                isCodeMirror = true;
            }
            var SourceEditor = window.SourceEditor;

            // See view-source:chrome://browser/content/devtools/scratchpad.xul
            // + view-source:chrome://browser/content/devtools/source-editor-overlay.xul
            var psXUL = (isCodeMirror
            ? '<!DOCTYPE popupset [\
                <!ENTITY % editMenuStrings SYSTEM "chrome://global/locale/editMenuOverlay.dtd">\
                %editMenuStrings;\
                <!ENTITY % sourceEditorStrings SYSTEM "' + (
                    Services.appinfo.name == "Pale Moon" || Services.appinfo.name == "Basilisk"
                        ? this.platformVersion >= 4.1
                            ? "chrome://devtools/locale/sourceeditor.dtd"
                            : "chrome://global/locale/devtools/sourceeditor.dtd"
                        : this.platformVersion >= 45
                            ? "chrome://devtools/locale/sourceeditor.dtd"
                            : "chrome://browser/locale/devtools/sourceeditor.dtd"
                ) + '">\
                %sourceEditorStrings;\
            ]>\
            <popupset id="sourceEditorPopupset"\
                xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">\
                <menupopup id="sourceEditorContext"\
                    onpopupshowing="goUpdateSourceEditorMenuItems()">\
                    <menuitem id="menu_undo" label="&undoCmd.label;" accesskey="&undoCmd.accesskey;"\
                        oncommand="goDoCommand(\'cmd_undo\')" />\
                    <menuitem id="menu_redo" label="&redoCmd.label;" accesskey="&redoCmd.accesskey;"\
                        oncommand="goDoCommand(\'cmd_redo\')" />\
                    <menuseparator/>\
                    <menuitem id="menu_cut" label="&cutCmd.label;" accesskey="&cutCmd.accesskey;"\
                        oncommand="goDoCommand(\'cmd_cut\')" />\
                    <menuitem id="menu_copy" label="&copyCmd.label;" accesskey="&copyCmd.accesskey;"\
                        oncommand="goDoCommand(\'cmd_copy\')" />\
                    <menuitem id="menu_paste" label="&pasteCmd.label;" accesskey="&pasteCmd.accesskey;"\
                        oncommand="goDoCommand(\'cmd_paste\')" />\
                    <menuitem id="menu_delete" label="&deleteCmd.label;" accesskey="&deleteCmd.accesskey;"\
                        oncommand="goDoCommand(\'cmd_delete\')" />\
                    <menuseparator/>\
                    <menuitem id="menu_selectAll" label="&selectAllCmd.label;" accesskey="&selectAllCmd.accesskey;"\
                        oncommand="goDoCommand(\'cmd_selectAll\')" />\
                    <menuseparator/>\
                    <menuitem id="menu_find" label="&findCmd.label;" accesskey="&findCmd.accesskey;" />\
                    <menuitem id="menu_findAgain" label="&findAgainCmd.label;" accesskey="&findAgainCmd.accesskey;" />\
                    <menuseparator/>\
                    <menuitem id="se-menu-gotoLine"\
                        label="&gotoLineCmd.label;"\
                        accesskey="&gotoLineCmd.accesskey;"\
                        key="key_gotoLine"\
                        oncommand="goDoCommand(\'cmd_gotoLine\')"/>\
                </menupopup>\
            </popupset>'
            : '<popupset id="sourceEditorPopupset"\
                xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">\
                <menupopup id="sourceEditorContext"\
                    onpopupshowing="goUpdateSourceEditorMenuItems()">\
                    <menuitem id="se-menu-undo"/>\
                    <menuitem id="se-menu-redo"/>\
                    <menuseparator/>\
                    <menuitem id="se-menu-cut"/>\
                    <menuitem id="se-menu-copy"/>\
                    <menuitem id="se-menu-paste"/>\
                    <menuitem id="se-menu-delete"/>\
                    <menuseparator/>\
                    <menuitem id="se-menu-selectAll"/>\
                    <menuseparator/>\
                    <menuitem id="se-menu-find"/>\
                    <menuitem id="se-menu-findAgain"/>\
                    <menuseparator/>\
                    <menuitem id="se-menu-gotoLine"/>\
                </menupopup>\
            </popupset>'
            ).replace(/>\s+</g, "><");

            var ps = this.parseXULFromString(psXUL);
            document.documentElement.appendChild(ps);

            window.setTimeout(function() {
                function appendNode(nodeName, id) {
                    var node = document.createElementNS(xulns, nodeName);
                    node.id = id;
                    document.documentElement.appendChild(node);
                }
                appendNode("commandset", "editMenuCommands");
                appendNode("commandset", "sourceEditorCommands");
                appendNode("keyset", "sourceEditorKeys");
                appendNode("keyset", "editMenuKeys");

                this.loadOverlays(
                    window,
                    function done() {
                        window.setTimeout(function() {
                            var mp = document.getElementById("sourceEditorContext");
                            if(mp.state == "closed")
                                return;
                            Array.prototype.forEach.call(
                                mp.getElementsByAttribute("command", "*"),
                                function(mi) {
                                    var cmd = mi.getAttribute("command");
                                    var controller = document.commandDispatcher
                                        .getControllerForCommand(cmd);
                                    if(controller && !controller.isCommandEnabled(cmd))
                                        mi.setAttribute("disabled", "true");
                                }
                            );
                        }, 0);
                        if(!isCodeMirror)
                            return;
                        // See view-source:chrome://browser/content/devtools/scratchpad.xul in Firefox 27.0a1
                        window.goUpdateSourceEditorMenuItems = function() {
                            goUpdateGlobalEditMenuItems();
                            var commands = ["cmd_undo", "cmd_redo", "cmd_cut", "cmd_paste", "cmd_delete"];
                            commands.forEach(goUpdateCommand);
                        };
                        var cmdsMap = {
                            "se-menu-undo":   "cmd_undo",
                            "se-menu-redo":   "cmd_redo",
                            "se-menu-cut":    "cmd_cut",
                            "se-menu-copy":   "cmd_copy",
                            "se-menu-paste":  "cmd_paste",
                            "se-menu-delete": "cmd_delete",
                            __proto__: null
                        };
                        for(var id in cmdsMap) {
                            var mi = document.getElementById(id);
                            mi && mi.setAttribute("command", cmdsMap[id]);
                        }
                        // We can't use command="cmd_selectAll", menuitem will be wrongly disabled sometimes
                        var enabledCmdsMap = {
                            "se-menu-selectAll": "cmd_selectAll",
                            "se-menu-findAgain": "cmd_findAgain",
                            __proto__: null
                        };
                        for(var id in enabledCmdsMap) {
                            var mi = document.getElementById(id);
                            if(mi) {
                                mi.removeAttribute("command");
                                mi.removeAttribute("disabled");
                                mi.setAttribute("oncommand", "goDoCommand('" + enabledCmdsMap[id] + "');");
                            }
                        }
                        // Workaround: emulate keyboard shortcut
                        var keyCmdsMap = {
                            "menu_find":      { keyCode: KeyboardEvent.DOM_VK_F, charCode: "f".charCodeAt(0), ctrlKey: true },
                            "menu_findAgain": { keyCode: KeyboardEvent.DOM_VK_G, charCode: "g".charCodeAt(0), ctrlKey: true },
                            __proto__: null
                        };
                        var _key = function() {
                            var e = this._keyData;
                            var evt = document.createEvent("KeyboardEvent");
                            evt.initKeyEvent(
                                "keydown", true /*bubbles*/, true /*cancelable*/, window,
                                e.ctrlKey || false, e.altKey || false, e.shiftKey || false, e.metaKey || false,
                                e.keyCode || 0, e.charCode || 0
                            );
                            document.commandDispatcher.focusedElement.dispatchEvent(evt);
                        };
                        for(var id in keyCmdsMap) {
                            var mi = document.getElementById(id);
                            if(mi) {
                                mi.removeAttribute("command");
                                mi.removeAttribute("disabled");
                                mi.setAttribute("oncommand", "this._key();");
                                mi._keyData = keyCmdsMap[id];
                                mi._key = _key;
                            }
                        }
                        // Fix styles for autocomplete tooltip
                        function css(uri) {
                            document.insertBefore(document.createProcessingInstruction(
                                "xml-stylesheet",
                                'href="' + uri + '" type="text/css"'
                            ), document.documentElement);
                        }
                        css("resource://devtools/client/themes/variables.css");
                        css("resource://devtools/client/themes/common.css");
                        css("chrome://devtools/skin/tooltips.css");
                    },
                    ["chrome://global/content/editMenuOverlay.xul", function check(window) {
                        return window.document.getElementById("editMenuCommands").hasChildNodes();
                    }],
                    ["chrome://browser/content/devtools/source-editor-overlay.xul", function check(window) {
                        return window.document.getElementById("sourceEditorCommands").hasChildNodes();
                    }]
                );
            }.bind(this), 500); // We should wait to not break other extensions with document.loadOverlay()

            var tabs = document.getElementById("custombuttons-editbutton-tabbox");
            var selectedPanel = tabs.selectedPanel;
            Array.prototype.slice.call(document.getElementsByTagName("cbeditor")).forEach(function(cbEditor) {
                if("__sourceEditor" in cbEditor)
                    return;
                var code = cbEditor.value;
                var isCSS = options.cssInHelp && cbEditor.id == "help";
                if(isCodeMirror) {
                    var opts = {
                        mode: isCSS
                            ? SourceEditor.modes.css
                            : SourceEditor.modes.js,
                        value: code,
                        lineNumbers: true,
                        enableCodeFolding: true,
                        showTrailingSpace: true,
                        autocomplete: true,
                        contextMenu: "sourceEditorContext"
                    };
                    var optsOvr = options.codeMirror;
                    for(var opt in optsOvr) if(optsOvr.hasOwnProperty(opt))
                        opts[opt] = optsOvr[opt];
                    var se = new SourceEditor(opts);
                    if("codeMirror" in se) window.setTimeout(function() {
                        if("insertCommandsController" in se)
                            se.insertCommandsController(); // Pale Moon and Basilisk
                        else
                            this.insertCommandsController(se);
                    }.bind(this), 200);
                }
                else {
                    var se = new SourceEditor();
                }
                se.__isCodeMirror = isCodeMirror;
                var seElt = document.createElementNS(xulns, "hbox");
                if(cbEditor.id)
                    seElt.id = "sourceEditor-" + cbEditor.id;
                seElt.className = "sourceEditor";
                seElt.setAttribute("flex", 1);
                seElt.__sourceEditor = se;
                cbEditor.parentNode.insertBefore(seElt, cbEditor);
                //cbEditor.setAttribute("hidden", "true");
                cbEditor.setAttribute("collapsed", "true");
                cbEditor.parentNode.appendChild(cbEditor);
                cbEditor.__sourceEditor = se;
                cbEditor.__sourceEditorElt = seElt;
                cbEditor.__defineGetter__("value", function() {
                    if("__sourceEditor" in this) {
                        var se = this.__sourceEditor;
                        if(!se.__initialized)
                            return se.__value;
                        return se.getText().replace(/\r\n?|\n\r?/g, "\n");
                    }
                    return this.textbox.value;
                });
                cbEditor.__defineSetter__("value", function(v) {
                    if("__sourceEditor" in this) {
                        var se = this.__sourceEditor;
                        if(!se.__initialized) {
                            var _this = this;
                            se.__onLoadCallbacks.push(function() {
                                _this.value = v;
                            });
                            return se.__value = v;
                        }
                        return se.setText(v.replace(/\r\n?|\n\r?/g, "\n"));
                    }
                    return this.textbox.value = v;
                });
                cbEditor.selectLine = function(lineNumber) {
                    if("__sourceEditor" in this) {
                        var se = this.__sourceEditor;
                        if(!se.__initialized) {
                            var _this = this, args = arguments;
                            se.__onLoadCallbacks.push(function() {
                                _this.selectLine.apply(_this, args);
                            });
                            return undefined;
                        }
                        if(se.__isCodeMirror) {
                            //se.focus();
                            //se.setCursor({ line: lineNumber - 1, ch: 0 });
                            //~ todo: optimize
                            var val = this.value;
                            var lines = val.split("\n");
                            var line = Math.min(lineNumber - 1, lines.length);
                            var ch = lines[line].length;
                            se.focus();
                            return se.setSelection({ line: line, ch: 0 }, { line: line, ch: ch });
                        }
                        else {
                            var selStart = se.getLineStart(lineNumber - 1);
                            var selEnd = se.getLineEnd(lineNumber - 1, false);
                            se.focus();
                            return se.setSelection(selStart, selEnd);
                        }
                    }
                    return this.__proto__.selectLine.apply(this, arguments);
                };

                // For edit_button() from chrome://custombuttons/content/editExternal.js
                seElt.__cbEditor = cbEditor;
                seElt.__defineGetter__("localName", function() {
                    return "cbeditor";
                });
                seElt.__defineGetter__("value", function() {
                    return this.__cbEditor.value;
                });
                seElt.__defineSetter__("value", function(val) {
                    this.__cbEditor.value = val;
                });

                se.__initialized = false;
                se.__onLoadCallbacks = [];
                se.__value = code;
                var onTextChanged = se.__onTextChanged = function() {
                    window.editor.changed = true;
                };
                var isLoaded = reason == this.REASON_WINDOW_LOADED;
                function done() {
                    se.__initialized = true;
                    se.__onLoadCallbacks.forEach(function(fn) {
                        try {
                            fn();
                        }
                        catch(e) {
                            Components.utils.reportError(e);
                        }
                    });
                    delete se.__onLoadCallbacks;
                    delete se.__value;
                }
                if(isCodeMirror) {
                    se.appendTo(seElt).then(function() {
                        try {
                            se.setupAutoCompletion();
                        }
                        catch(e) {
                            Components.utils.reportError(e);
                        }
                        if("setFontSize" in se) try {
                            se.setFontSize(options.codeMirror.fontSize);
                        }
                        catch(e) {
                            Components.utils.reportError(e);
                        }
                        window.setTimeout(function() {
                            window.editor.changed = false; // Strange...
                            window.setTimeout(function() { // Workaround for unexpected onTextChanged() calls
                                if(window.editor.changed && cbEditor.value == code)
                                    window.editor.changed = false;
                            }, 100);
                            se.on("change", onTextChanged);
                            if(isLoaded) {
                                if("clearHistory" in se)
                                    se.clearHistory();
                                else {
                                    var seGlobal = Components.utils.getGlobalForObject(SourceEditor.prototype);
                                    // Note: this is resource://app/modules/devtools/gDevTools.jsm scope in Firefox 34+
                                    var cm = seGlobal.editors.get(se);
                                    cm.clearHistory();
                                }
                            }
                        }, isFrame ? 50 : 15); // Oh, magic delays...
                        done();

                        // See resource:///modules/devtools/sourceeditor/editor.js
                        // doc.defaultView.controllers.insertControllerAt(0, controller(this, doc.defaultView));
                        var controllers = window.controllers; // nsIControllers
                        var controller = se.__cmdController = controllers.getControllerAt(0);
                        if("__cmdControllers" in tabs)
                            tabs.__cmdControllers.push(controller);
                        else {
                            tabs.__cmdControllers = [controller];
                            var onSelect = tabs.__onSelect = function() {
                                var seElt = tabs.selectedPanel;
                                if(!seElt || !("__sourceEditor" in seElt))
                                    return;
                                var se = seElt.__sourceEditor;
                                var curController = se.__cmdController;
                                tabs.__cmdControllers.forEach(function(controller) {
                                    try {
                                        if(controller == curController)
                                            controllers.insertControllerAt(0, controller);
                                        else
                                            controllers.removeController(controller);
                                    }
                                    catch(e) {
                                    }
                                });
                            };
                            tabs.addEventListener("select", onSelect, false);
                            window.setTimeout(onSelect, 0); // Activate controller from selected tab
                        }
                    });
                }
                else {
                    var opts = {
                        mode: isCSS
                            ? SourceEditor.MODES.CSS
                            : SourceEditor.MODES.JAVASCRIPT,
                        showLineNumbers: true,
                        initialText: code,
                        placeholderText: code, // For backward compatibility
                        contextMenu: "sourceEditorContext"
                    };
                    var optsOvr = options.orion;
                    for(var opt in optsOvr) if(optsOvr.hasOwnProperty(opt))
                        opts[opt] = optsOvr[opt];
                    se.init(seElt, opts, function callback() {
                        done();
                        isLoaded && se.resetUndo && se.resetUndo();
                        se.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onTextChanged);

                        // Hack to use selected editor
                        var controller = se.ui._controller;
                        controller.__defineGetter__("_editor", function() {
                            var seElt = tabs.selectedPanel;
                            var se = seElt && seElt.__sourceEditor
                                || document.getElementsByTagName("cbeditor")[0].__sourceEditor;
                            return se;
                        });
                        controller.__defineSetter__("_editor", function() {});
                    });
                }
            }, this);
            // Force select correct panel (prevent bugs, if selected "Button settings" tab)
            tabs.selectedPanel = selectedPanel.__sourceEditorElt || selectedPanel;

            var origExecCmd = window.editor.execute_oncommand_code;
            window.editor.execute_oncommand_code = function() {
                var cd = document.commandDispatcher;
                var cdFake = {
                    __proto__: cd,
                    get focusedElement() {
                        var selectedTab = tabs.selectedTab;
                        if(selectedTab && selectedTab.id == "code-tab")
                            return document.getElementById("code").textbox.inputField;
                        return cd.focusedElement;
                    }
                };
                document.__defineGetter__("commandDispatcher", function() {
                    return cdFake;
                });
                try {
                    var ret = origExecCmd.apply(this, arguments);
                }
                catch(e) {
                    Components.utils.reportError(e);
                }
                // document.hasOwnProperty("commandDispatcher") == false, so we cat just delete our fake property
                delete document.commandDispatcher;
                return ret;
            };

            window.addEventListener("load", function ensureObserversAdded() {
                window.removeEventListener("load", ensureObserversAdded, false);
                window.setTimeout(function() { window.editor.removeObservers(); }, 0);
                window.setTimeout(function() { window.editor.addObservers();    }, 0);
            }, false);
            // Fix for Ctrl+S hotkey (catched by CodeMirror)
            var hke = this.handleKeyEvent;
            window.addEventListener("keydown",  hke, true);
            window.addEventListener("keypress", hke, true);
            window.addEventListener("keyup",    hke, true);
            
             if(this.platformVersion >= 68) (function(events, win) {
                var listener = function(e) {
                    if(e.type == "unload") return destroy();
                    var sheets = win.document.styleSheets;
                    for(var ind = sheets.length - 1; ind; ind--) {
                        var sheet = sheets.item(ind);
                        if(sheet.href != "resource://devtools/client/themes/common.css")
                            continue;
                        destroy();
                        for(let ind = 0, len = sheet.cssRules.length; ind < len; ind++) {
                            var rule = sheet.cssRules.item(ind);
                            if(rule.selectorText && rule.selectorText == "::selection")
                                return sheet.deleteRule(ind);
                        }
                    }
                }
                var destroy = function() {
                    events.forEach(function(type) {
                        win.removeEventListener(type, listener, false);
                    });
                }
                events.forEach(function(type) {
                    win.addEventListener(type, listener, false);
                });
            })(["mousedown", "keydown", "unload"], window);
            
        },
        insertCommandsController: function(se) {
            this.insertCommandsController = insertCommandsController;
            return insertCommandsController(se);
            // devtools/client/sourceeditor/editor-commands-controller in Pale Moon/Basilisk
            function createController(ed) {
                return {
                    supportsCommand: function (cmd) {
                        switch (cmd) {
                            case "cmd_find":
                            case "cmd_findAgain":
                            case "cmd_gotoLine":
                            case "cmd_undo":
                            case "cmd_redo":
                            case "cmd_delete":
                            case "cmd_selectAll":
                                return true;
                        }

                        return false;
                    },

                    isCommandEnabled: function (cmd) {
                        let cm = ed.codeMirror;

                        switch (cmd) {
                            case "cmd_find":
                            case "cmd_gotoLine":
                            case "cmd_selectAll":
                                return true;
                            case "cmd_findAgain":
                                return cm.state.search != null && cm.state.search.query != null;
                            case "cmd_undo":
                                return ed.canUndo();
                            case "cmd_redo":
                                return ed.canRedo();
                            case "cmd_delete":
                                return ed.somethingSelected();
                        }

                        return false;
                    },

                    doCommand: function (cmd) {
                        let cm = ed.codeMirror;

                        let map = {
                            "cmd_selectAll": "selectAll",
                            "cmd_find": "find",
                            "cmd_undo": "undo",
                            "cmd_redo": "redo",
                            "cmd_delete": "delCharAfter",
                            "cmd_findAgain": "findNext"
                        };

                        if (map[cmd]) {
                            cm.execCommand(map[cmd]);
                            return;
                        }

                        if (cmd == "cmd_gotoLine") {
                            ed.jumpToLine();
                        }
                    },

                    onEvent: function () {}
                };
            }
            function insertCommandsController(sourceEditor) {
                let input = sourceEditor.codeMirror.getInputField();
                let controller = createController(sourceEditor);
                input.controllers.insertControllerAt(0, controller);
            }
        },
        destroyWindow: function(window, reason, isFrame) {
            if(reason == this.REASON_WINDOW_CLOSED)
                window.removeEventListener("DOMContentLoaded", this, false); // Window can be closed before DOMContentLoaded
            if(this.isBrowserWindow(window)) {
                this.destroyBrowserWindow(window, reason);
                return;
            }
            if(!this.isEditorWindow(window) || !("SourceEditor" in window))
                return;
            _log("destroyWindow(): isFrame: " + isFrame);
            var document = window.document;
            if(isFrame)
                window.removeEventListener("unload", this, false);

            var tabs = document.getElementById("custombuttons-editbutton-tabbox");
            if("__onSelect" in tabs) {
                tabs.removeEventListener("select", tabs.__onSelect, false);
                delete tabs.__onSelect;
                delete tabs.__cmdControllers;
            }

            Array.prototype.slice.call(document.getElementsByTagName("cbeditor")).forEach(function(cbEditor) {
                if(!("__sourceEditor" in cbEditor))
                    return;
                var se = cbEditor.__sourceEditor;
                var isCodeMirror = se.__isCodeMirror;
                if(isCodeMirror)
                    se.off("change", se.__onTextChanged);
                else
                    se.removeEventListener(window.SourceEditor.EVENTS.TEXT_CHANGED, se.__onTextChanged);
                delete se.__onTextChanged;
                if(reason == this.REASON_SHUTDOWN) {
                    var val = cbEditor.value;
                    delete cbEditor.value;
                    delete cbEditor.selectLine;

                    var seElt = cbEditor.__sourceEditorElt;
                    seElt.parentNode.insertBefore(cbEditor, seElt);
                    seElt.parentNode.removeChild(seElt);
                    delete cbEditor.__sourceEditorElt;
                    delete cbEditor.__sourceEditor;
                    delete seElt.__sourceEditor;
                    delete seElt.__cbEditor;

                    cbEditor.value = val;
                    window.setTimeout(function() {
                        cbEditor.removeAttribute("collapsed");
                    }, 0);
                }
                se.destroy();
                if("__cmdController" in se) {
                    try {
                        window.controllers.removeController(se.__cmdController);
                    }
                    catch(e) {
                    }
                    delete se.__cmdController;
                }
            }, this);

            if(reason == this.REASON_SHUTDOWN) {
                delete window.editor.execute_oncommand_code;
                [
                    "sourceEditorPopupset",
                    "editMenuCommands",
                    "sourceEditorCommands",
                    "sourceEditorKeys",
                    "editMenuKeys"
                ].forEach(function(id) {
                    var node = document.getElementById(id);
                    node && node.parentNode.removeChild(node);
                });
                [
                    "closeWindow", "canQuitApplication", "goQuitApplication", "goUpdateCommand", "goDoCommand",
                    "goSetCommandEnabled", "goSetMenuValue", "goSetAccessKey", "goOnEvent", "visitLink",
                    "setTooltipText", "NS_ASSERT",
                    "goUpdateGlobalEditMenuItems", "goUpdateUndoEditMenuItems", "goUpdatePasteMenuItems"
                ].forEach(function(p) {
                    delete window[p];
                });
                for(var child = document.documentElement; child = child.previousSibling; ) {
                    if(
                        child.nodeType == child.PROCESSING_INSTRUCTION_NODE
                        && child.data.indexOf("://devtools/") != -1
                    ) {
                        setTimeout(function(child) {
                            child.parentNode.removeChild(child);
                        }, 0, child);
                    }
                }
                delete window.SourceEditor;
            }
            var hke = this.handleKeyEvent;
            window.removeEventListener("keydown",  hke, true);
            window.removeEventListener("keypress", hke, true);
            window.removeEventListener("keyup",    hke, true);
            //~ todo: we have one not removed controller!
            //LOG("getControllerCount(): " + window.controllers.getControllerCount());
        },
        initBrowserWindow: function(window, reason) {
            _log("initBrowserWindow()");
            window.addEventListener("DOMContentLoaded", this, false);
            Array.prototype.forEach.call(window.frames, function(frame) {
                this.initWindow(frame, reason, true);
            }, this);
        },
        destroyBrowserWindow: function(window, reason) {
            _log("destroyBrowserWindow()");
            window.removeEventListener("DOMContentLoaded", this, false);
            Array.prototype.forEach.call(window.frames, function(frame) {
                this.destroyWindow(frame, reason, true);
            }, this);
        },
        isEditorWindow: function(window) {
            return window.location.href.substr(0, 41) == "chrome://custombuttons/content/editor.xul";
        },
        isBrowserWindow: function(window) {
            var loc = window.location.href;
            return loc == "chrome://browser/content/browser.xhtml"
                || loc == "chrome://navigator/content/navigator.xul";
        },
        observe: function(subject, topic, data) {
            if(topic == "quit-application-granted")
                this.destroy();
            else if(topic == "domwindowopened")
                subject.addEventListener("DOMContentLoaded", this, false);
            else if(topic == "domwindowclosed")
                this.destroyWindow(subject, this.REASON_WINDOW_CLOSED);
        },
        handleEvent: function(e) {
            switch(e.type) {
                case "DOMContentLoaded":
                    var window = e.target.defaultView || e.target;
                    window.removeEventListener(e.type, this, false);
                    var isFrame = window != e.currentTarget;
                    this.initWindow(window, this.REASON_WINDOW_LOADED, isFrame);
                break;
                case "unload":
                    //var window = e.currentTarget;
                    var window = e.target.defaultView || e.target;
                    window.removeEventListener(e.type, this, false);
                    this.destroyWindow(window, this.REASON_WINDOW_CLOSED, true);
            }
        },
        handleKeyEvent: function(e) {
            if(
                (e.keyCode == e.DOM_VK_S || String.fromCharCode(e.charCode).toUpperCase() == "S")
                && e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey
            ) {
                e.preventDefault();
                e.stopPropagation();
                if(e.type == "keydown") {
                    var window = e.currentTarget;
                    window.editor.updateButton();
                }
            }
        },
        loadOverlays: function() {
            this.runGenerator(this.loadOverlaysGen, this, arguments);
        },

        get loadOverlaysGen() {
            var fn = this._loadOverlaysGen.toString()
                .replace(/__yield/g, "yield");
            try {
                new Function("function test() { yield 0; }");
            }
            catch(e) { // Firefox 58+: SyntaxError: yield expression is only valid in generators
                fn = fn.replace("function", "function*"); // Firefox 26+
            }
            delete this.loadOverlaysGen;
            return this.loadOverlaysGen = eval("(" + fn + ")");
        },
        _loadOverlaysGen: function loadOverlaysGen(window, callback/*, overlayData1, ...*/) {
            var gen = loadOverlaysGen.__generator;
            for(var i = 2, l = arguments.length; i < l; ++i) {
                var overlayData = arguments[i];
                this.loadOverlay(window, overlayData[0], overlayData[1], function() {
                    gen.next();
                });
                __yield(0);
            }
            callback();
            __yield(0);
        },
        loadOverlay: function(window, uri, check, callback) {
            var document = window.document;
            var stopWait = Date.now() + 4500;
            window.setTimeout(function load() {
                _log("loadOverlay(): " + uri);
                var tryAgain = Date.now() + 800;
                try {
                    document.loadOverlay(uri, null);
                }
                catch(e) {
                    window.setTimeout(callback, 0);
                    return;
                }
                window.setTimeout(function ensureLoaded() {
                    if(check(window))
                        window.setTimeout(callback, 0);
                    else if(Date.now() > stopWait)
                        return;
                    else if(Date.now() > tryAgain)
                        window.setTimeout(load, 0);
                    else
                        window.setTimeout(ensureLoaded, 50);
                }, 50);
            }, 0);
        },
        runGenerator: function(genFunc, context, args) {
            var gen = genFunc.apply(context, args);
            genFunc.__generator = gen;
            gen.next();
        },
        parseXULFromString: function(xul) {
            xul = xul.replace(/>\s+</g, "><");
            try {
                return new DOMParser().parseFromString(xul, "application/xml").documentElement;
            }
            catch(e) {
                var dummy = document.createElement("dummy");
                dummy.innerHTML = xul.trimLeft();
                return dummy.firstChild;
            }
        }
    };
    storage.set(watcherId, watcher);
    setTimeout(function() {
        watcher.init(watcher.REASON_STARTUP);
    }, 50);
}
function destructor(reason) {
    if(reason == "update" || reason == "delete") {
        watcher.destroy(watcher.REASON_SHUTDOWN);
        storage.set(watcherId, null);
    }
}
if(
    typeof addDestructor == "function" // Custom Buttons 0.0.5.6pre4+
    && addDestructor != ("addDestructor" in window && window.addDestructor)
)
    addDestructor(destructor, this);
else
    this.onDestroy = destructor;

function ts() {
    var d = new Date();
    var ms = d.getMilliseconds();
    return d.toTimeString().replace(/^.*\d+:(\d+:\d+).*$/, "$1") + ":" + "000".substr(("" + ms).length) + ms + " ";
}
function _log(s) {
    Services.console.logStringMessage("[Custom Buttons :: Source Editor] " + ts() + s);
}


при редактировании во вкладке выдает
скрытый текст
ecurityError: The operation is insecure. button.js:219
    initWindow chrome://custombuttons-context/content/button.js?windowId=Firefox&id=custombuttons-button57@init line 1 > Function:219
    handleEvent chrome://custombuttons-context/content/button.js?windowId=Firefox&id=custombuttons-button57@init line 1 > Function:835

Просто при редактировании работает, но выдает
SecurityError: The operation is insecure. button.js:219
    initWindow chrome://custombuttons-context/content/button.js?windowId=Firefox&id=custombuttons-button57@init line 1 > Function:219
    handleEvent chrome://custombuttons-context/content/button.js?windowId=Firefox&id=custombuttons-button57@init line 1 > Function:835
И последннее в кнопке
скрытый текст

Выделить код

Код:

// https://github.com/Infocatcher/Custom_Buttons/tree/master/Add_New_Buttons_After_This_Button

// Add New Buttons After This Button button for Custom Buttons
// (c) Infocatcher 2012, 2014
// version 0.1.2 - 2014-01-25 

var cbs = custombuttons.cbService;
var windowId = cbs.getWindowId(document.documentURI);
var notificationPrefix = cbs.getNotificationPrefix(windowId);

this.toggleEnabled = function() {
    this.checked = !this.checked;
    if("persist" in document)
        document.persist(this.id, "checked");
    else // Firefox 63+
        Services.xulStore.persist(this, "checked");
};
this.setAttribute("oncommand", "this.toggleEnabled();");

var observer = {
    button: this,
    observe: function(button, topic, data) {
        if(topic != notificationPrefix + "installButton")
            return;
        if(!this.button.checked)
            return;
        var toolbar = this.button.parentNode;
        toolbar.insertBefore(button, this.button.nextSibling);
        custombuttons.persistCurrentSets(toolbar.id, this.button.id, button.id || button.getAttribute("id"));
    }
};
var os = Components.classes["@mozilla.org/observer-service;1"]
    .getService (Components.interfaces.nsIObserverService);
os.addObserver(observer, notificationPrefix + "installButton", false);
var hasObserver = true;

this.onDestroy = function(reason) {
    if(hasObserver) {
        hasObserver = false;
        os.removeObserver(observer, notificationPrefix + "installButton");
    }
    if(reason == "delete" && this.checked)
        this.toggleEnabled();
};


не создается кнопка рядом с текущей
Не могли бы Вы глянуть?

Andrey_Krropotkin пишет

CB_Editor_Toggle_on_Top

Эта та кнопка, которой я пользуюсь сам, и там много чего уже накопилось.
Всё собирался написать Infocatcher'у, но так и не собрался.
А теперь уже страшно даже и подумать, там целый трактат нужен,
и далеко не всё и обосновать бы смог.
Давай скопирую у себя из 73, но следует понимать, что это будет
гораздо хуже, чем если бы правил сам Автор, так что просто на всякий случай.

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

Выделить код

Код:

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

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

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

// Hotkey: Ctrl+T

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

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

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

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

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

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

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

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

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

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


Остальными двумя не пользуюсь, но, похоже, здесь это пресловутое западло.
Задело многое, в своё время даже их дражайший Firefox Screenshots под раздачу угодил.
Вобщем, парочка не слишком оптимальных фиксов, чтоб проверить
для CB_Source_Editor

Выделить код

Код:

var ps = this.parseXULFromString(psXUL);

            if(isFrame && "parseFromSafeString" in window.DOMParser.prototype)
                ps = window.MozXULElement.parseXULToFragment(ps.outerHTML);

            document.documentElement.appendChild(ps);


для Add_New_Buttons_After_This_Button

Выделить код

Код:

//toolbar.insertBefore(button, this.button.nextSibling);
        toolbar.insertBefore(
            custombuttons.cbCloneNode(button),
            this.button.nextSibling
        );

Dumby да все работает, спасибо.

Dumby
О! Спасибо!
Начал потихоньку внедрять: https://github.com/Infocatcher/Custom_B … 6e8a3ad252, пока только Toggle on Top (и то частично).

19-12-2019 00:11:24
P.S. И отдельное спасибо за оживленный DOMi – попробовать успел, но не отписался...

Infocatcher
Насчёт Source Editor'а и Firefox 73.
В нелокализированной версии покромсали editMenuOverlay.dtd
и там вместо <popupset>'а образуется <parsererror>, длиннющий такой,
больше двадцати тысяч пикселей.

По всему коду лазать не стал, записал всё про контекстное меню
отдельным блоком в if, а существующее задвинул в else.
Получилось не очень, «Найти ещё раз» находит не следующее,
а перескакивает через одно.

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

Выделить код

Код:

var SourceEditor = window.SourceEditor;

            if(this.platformVersion >= 73) {
                var psXUL = '<popupset id="sourceEditorPopupset">\
                    <linkset>\
                        <html:link rel="localization" href="toolkit/global/textActions.ftl" />\
                    </linkset>\
                    <menupopup id="sourceEditorContext"\
                        onpopupshowing="popupHandler(event.target)"\
                        oncommand="popupHandler(event.target)">\
\
                        <menuitem id="menu_undo" data-l10n-id="text-action-undo" />\
                        <menuitem id="menu_redo" data-l10n-id="text-action-redo" />\
                        <menuseparator/>\
                        <menuitem id="menu_cut" data-l10n-id="text-action-cut" />\
                        <menuitem id="menu_copy" data-l10n-id="text-action-copy" />\
                        <menuitem id="menu_paste" data-l10n-id="text-action-paste" />\
                        <menuitem id="menu_delete" data-l10n-id="text-action-delete" />\
                        <menuseparator/>\
                        <menuitem id="menu_selectAll" data-l10n-id="text-action-select-all" />\
                        <menuseparator/>\
                        <menuitem id="menu_find" label="&findCmd.label;" accesskey="&findCmd.accesskey;" />\
                        <menuitem id="menu_findAgain" label="&findAgainCmd.label;" accesskey="&findAgainCmd.accesskey;" />\
                        <menuseparator/>\
                        <menuitem id="se-menu-gotoLine" label="&gotoLineCmd.label;"\
                            accesskey="&gotoLineCmd.accesskey;" key="key_gotoLine" />\
                    </menupopup>\
                </popupset>';
                var ps = window.MozXULElement.parseXULToFragment(psXUL, [
                    "chrome://global/locale/editMenuOverlay.dtd",
                    "chrome://devtools/locale/sourceeditor.dtd"
                ]);
                if(!this.popupHandler) {
                    var commands = {module: {}};
                    ps.querySelectorAll("menuitem").forEach(function(menuitem) {
                        commands[menuitem.id] = menuitem.id.replace(/^(se-)?menu./, "cmd_");
                    });
                    Services.scriptloader.loadSubScript(
                        "resource://devtools/client/shared/sourceeditor/editor-commands-controller.js", commands
                    );
                    var createController = function(editor) {
                        var res = editor.popupCmdController = commands.createController(editor);
                        res.docShell = editor.container.contentWindow.docShell;
                        return res;
                    }
                    var getObj = function(cmd, controller) {
                        return controller.supportsCommand(cmd) ? controller: controller.docShell;
                    }
                    var setDisabled = function(menuitem) {
                        var cmd = commands[menuitem.id];
                        menuitem.disabled = !getObj(cmd, this).isCommandEnabled(cmd);
                    }
                    this.popupHandler = function(node) {
                        var ed = node.ownerDocument.activeElement.contentWindow.editor;
                        var controller = ed.popupCmdController || createController(ed);
                        var cmd = commands[node.id];
                        if(cmd) getObj(cmd, controller).doCommand(cmd);
                        else node.menuitems.forEach(setDisabled, controller);
                    }
                }
                var popup = ps.querySelector("menupopup");
                popup.menuitems = popup.querySelectorAll("menuitem");
                popup.popupHandler = this.popupHandler;

            } else {
                // See view-source:chrome://browser/content/devtools/scratchpad.xul
                // + view-source:chrome://browser/content/devtools/source-editor-overlay.xul
                var psXUL = (isCodeMirror

                // .......
            }
            document.documentElement.appendChild(ps);


Но я не об этом.

Лисе сделали XUL content type лоботомию, и теперь
документ CB-редактора это XMLDocument :usch:.
Скрипты не исполняются и кустомэлементщина окно игнорирует.
Приходится подгружать скриптлоадером вручную.

И вот здесь нарисовалась какая-то нестыковка.
При попытке открыть окно редактора, браузер зависает, грузит процессор,
и Windows дописывает ему в заголовок своё «(Не отвечает)».
Причём, если открыть во вкладке, то такого не происходит.

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

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

Выделить код

Код:

//initWindow: function(window, reason, isFrame) {
        initWindow: function(window, reason, isFrame, again) {
            if(this.isBrowserWindow(window)) {
                this.initBrowserWindow(window, reason);
                return;
            }
            if(!this.isEditorWindow(window))
                return;
            if(!again && !isFrame && window.document.contentType == "text/xml") {
                window.setTimeout(function(_this) {
                    _this.initWindow(window, reason, isFrame, true);
                }, 0, this);
                return;
            }
            _log("initWindow(): isFrame: " + isFrame);


P.S. Да, DOMi тоже ведь .xul
dom_inspector-7.0.6-fx-paxmod.xpi
dom_inspector-7.0.6-fx-bootstrap.xpi

Dumby пишет

При попытке открыть окно редактора, браузер зависает, грузит процессор,
и Windows дописывает ему в заголовок своё «(Не отвечает)».
Причём, если открыть во вкладке, то такого не происходит.

Упс.
Я попробовал поиграться, еще помогает

Выделить код

Код:

if(!again && !isFrame && window.document.contentType == "text/xml") { // Firefox 73+
                //window.setTimeout(function(_this) {
                //    _this.initWindow(window, reason, isFrame, true);
                //}, 0, this);
                Services.tm.mainThread.dispatch(function() {
                    this.initWindow(window, reason, isFrame, true);
                }.bind(this), Components.interfaces.nsIThread.DISPATCH_NORMAL);
                return;
            }

Но на глаз все равно есть задержка (хотя она и от самого CodeMirror'а).
И еще перестает зависать после замены "DOMContentLoaded" -> "load", так что я пока такую распорку воткнул.


А с меню надо бы еще посмотреть, куда и как доотламывать будут. Хотя, наверное, это еще локализации не обновились. :sick:
Меня особенно удивили постоянные перепиливания бедного Eyedropper'а без каких бы то ни было приростов в возможностях: https://github.com/Infocatcher/Custom_B … 1358-L1397 А вы, друзья, как ни садитесь...

Infocatcher на 72 обновил кнопки с вашей страницы:
1. Undo Close Tabs - работает нормально, но показывает ошибку для
var label = document.getAnonymousElementByAttribute(tip, "class", "tooltip-label");  - document.getAnonymousElementByAttribute is not a function
2. Check for Addons Updates (использую в составе Toggle Restartless Add-ons) - постоянно крутится и выдает ошибку inProgress is null для btn.tooltipText = inProgress.getAttribute("value");
3. Source Editor - version 0.1.0a10 - 2019-12-25 работает нормально, но показывает ошибку - InvalidAccessError: A parameter or an operation is not supported by the underlying object для  for(var j = 0, len = sheet.cssRules.length; j < len; ++j)

Andrey_Krropotkin пишет

3. Source Editor - version 0.1.0a10 - 2019-12-25 работает нормально, но показывает ошибку - InvalidAccessError: A parameter or an operation is not supported by the underlying object для  for(var j = 0, len = sheet.cssRules.length; j < len; ++j)

Там таймаут надо поднимать.
Или вот, ещё вариант. Вроде работает (как идея).

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

Выделить код

Код:

css("resource://devtools/client/themes/variables.css");
                        css("resource://devtools/client/themes/common.css");
                        css("chrome://devtools/skin/tooltips.css");
                        if(this.platformVersion >= 68) {
                            var eKey = "styleSheetChangeEventsEnabled";
                            var notVal = !document[eKey];
                            if(notVal)
                                document[eKey] = true;
                            document.addEventListener("StyleSheetApplicableStateChanged", function change(e) {
                                var sheet = e.stylesheet;
                                if(sheet.href != "resource://devtools/client/themes/common.css")
                                    return;
                                document.removeEventListener(e.type, change);
                                if(notVal && document[eKey])
                                    document[eKey] = false;
                                for(var i = 0, len = sheet.cssRules.length; i < len; ++i)
                                    if(sheet.cssRules[i].selectorText == "::selection") {
                                        sheet.deleteRule(i);
                                        break;
                                    }
                            });
                        }

Andrey_Krropotkin пишет

1. Undo Close Tabs - работает нормально, но показывает ошибку для
var label = document.getAnonymousElementByAttribute(tip, "class", "tooltip-label");  - document.getAnonymousElementByAttribute is not a function

Решаемая в этом месте кода проблема с тултипом,
наверно уже давно потеряла актуальность после этого:
Bug 1461798 - Migrate <tooltip> away from XBL

Может так

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

Выделить код

Код:

//if(this.appVersion >= 61) {
        if(this.appVersion <= 63 && this.appVersion >= 61) {

Andrey_Krropotkin пишет

1. Undo Close Tabs - работает нормально, но показывает ошибку для
var label = document.getAnonymousElementByAttribute(tip, "class", "tooltip-label");  - document.getAnonymousElementByAttribute is not a function

(Долго я писал, да...)
Вроде, распорка эта больше и не требуется... отключил: https://github.com/Infocatcher/Custom_B … 63b9ee2fe2

Andrey_Krropotkin пишет

2. Check for Addons Updates (использую в составе Toggle Restartless Add-ons) - постоянно крутится и выдает ошибку inProgress is null для btn.tooltipText = inProgress.getAttribute("value");

Там все печально, в браузере браузер (вкладочный) с браузером (с содержимым). Я начал делать, но все ссылки на элементы управления в менеджере дополнений отломались:

Какой-то неработающий черновик

Выделить код

Код:

--- a/checkForAddonsUpdates.js
+++ b/checkForAddonsUpdates.js
@@ -169,8 +169,6 @@
     }
 
     progressIcon.loading();
-    var inProgress = $("updates-progress");
-    btn.tooltipText = inProgress.getAttribute("value");
 
     var origIcon = tab.image;
     tab.image = image;
@@ -180,12 +178,36 @@
     if(!updEnabled)
         Services.prefs.setBoolPref(updEnabledPref, true);
 
-    var notFound = $("updates-noneFound");
-    var updated = $("updates-installed");
+    var fu = $("cmd_findAllUpdates");
+    if(!fu) { // Firefox 72+
+        var win = doc.defaultView;
+        var vb = doc.getElementById("html-view-browser");
+        if(!vb) {
+            win.setTimeout(processAddonsTab, 20, win);
+            return;
+        }
+        var vbDoc = vb.contentDocument;
+        fu = vbDoc.querySelector('[action="check-for-updates"]');
+    }
+
+    var notFound = $("updates-noneFound"); //~fixme
+    var updated = $("updates-installed"); //~fixme
     // Avoid getting false results from the past update check (may not be required for "noneFound")
-    notFound.hidden = updated.hidden = true;
+    //~fixme notFound.hidden = updated.hidden = true;
 
-    $("cmd_findAllUpdates").doCommand();
+    //fu.doCommand();
+    fu.click();
+
+    var inProgress = $("updates-progress");
+    if(!inProgress) { // Firefox 72+
+        var um = vbDoc.getElementById("updates-message");
+        inProgress = um.shadowRoot.querySelector('[data-l10n-id="addon-updates-updating"]');
+        if(!inProgress) {
+            win.setTimeout(processAddonsTab, 5, win);
+            return;
+        }
+    }
+    btn.tooltipText = inProgress.getAttribute("value") || inProgress.textContent;
 
     var waitTimer = setInterval(function() {
         if(!doc.defaultView || doc.defaultView.closed) {

Dumby пишет

Или вот, ещё вариант. Вроде работает (как идея).

работает без ошибок

Infocatcher пишет

Вроде, распорка эта больше и не требуется

работает без ошибок

Infocatcher пишет

Какой-то неработающий черновик

если сначала нажимаю на кнопку выдает ошибки
1. TypeError: el.closest(...) is null дляview-source:chrome://mozapps/content/extensions/aboutaddons.js в строке
function getTelemetryViewName(el) {
  return el.closest("[current-view]").getAttribute("current-view");
}
2. TypeError: autoUpdate is nul для var autoUpdateChecked = autoUpdate.getAttribute("checked") == "true"; и зацикливается
При перезагрузке открывается вкладка Управление дополнениями - доступные обновления

Если сначала открываю вкладку Управление дополнениями и затем нажимаю на кнопку, то находит обновления, но все равно кнопка и вкладка зацикливаются и выдают только вторую ошибку, если после этого закрываю вкладку, то выскакивает сообщение "Tab with addon-manager was closed" и тогда кнопка перестает работать

Не знаю поможет Вам или нет - вот на www.camp-firefox.de нашел работающий кусок кода

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

Выделить код

Код:

let frameScript = function() {
      addEventListener('pageshow', function onPageshow(event) {
        let document = event.target;
        if (document.URL != 'about:addons')
          return;
        removeEventListener('pageshow', onPageshow);
        content.setTimeout(function() {
          content.getHtmlBrowser().contentDocument.querySelector('[action="check-for-updates"]').click();
          let item = document.getElementById('category-availableUpdates');
          item.click();
          let categories = item.parentNode;
          categories.addEventListener('mousedown', function onMousedown(event) {
            if (event.target != item && event.target.parentNode != item) {
              item.hidden = true;
              categories.removeEventListener('mousedown', onMousedown);
            };
          });
        }, 0);
      });
    };

    let frameScriptURI = 'data:,(' + frameScript.toString() + ')()';
    let window = event.target.ownerGlobal;
    window.openTrustedLinkIn('about:addons', 'tab');
    window.gBrowser.selectedBrowser.messageManager.loadFrameScript(frameScriptURI, true);

Хочу добавить про Toggle on Top, раз обсуждалось.
Это для btnPos: 1 (at end of tabs).

В Firefox 72 переписали использование [align="right"],
и кнопка съехала в противоположную сторону, влево.

Причём, переписали неправильно, затем исправили,
и даже в бету пришлось вмешиваться.

Таким образом, наверно так

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

Выделить код

Код:

case 1:
                    //box.setAttribute("align", "right");
                    if(this.platformVersion >= 72)
                        box.setAttribute("pack", "end");
                    else
                        box.setAttribute("align", "right");

Dumby пишет

Хочу добавить про Toggle on Top, раз обсуждалось.
Это для btnPos: 1 (at end of tabs).

В Firefox 72 переписали использование [align="right"],
и кнопка съехала в противоположную сторону, влево.

Спасибо! Обновил.
У меня причем ощущение, что я проверял (на какой-то бэта-версии) – и было нормально. :sick:

Dumby пишет

Причём, переписали неправильно, затем исправили,
и даже в бету пришлось вмешиваться.

Чик-чик – и в продакшн! ©

12-01-2020 23:35:00
А в 73.0b3 новая напасть: упорно лезет нулевая высота у свежедобавленной кнопки даже после волшебного пинка таймаута:

Выделить код

Код:

tabs.parentNode.insertBefore(box, tabs);
                    LOG("xxx " + window.getComputedStyle(btn, null).height);
                    LOG("xxx " + btn.getBoundingClientRect().height);
                    setTimeout(function() {
                        LOG("xxx " + window.getComputedStyle(btn, null).height);
                        LOG("xxx " + btn.getBoundingClientRect().height);
                    }, 5);
                    box.style.marginBottom = -(btn.boxObject || btn.getBoundingClientRect()).height + "px";

Какой-то обложенный распорками Check for Addons Updates:
https://github.com/Infocatcher/Custom_B … Updates.js (изменения)

Infocatcher пишет

Check for Addons Updates

А почему в этой теме, а не в [CB]Check for Addons Updates | Форум Mozilla Россия ?

voqabuhe
Хм, да, там будет уместнее. Просто изначальный вопрос оказался в этой теме.

Infocatcher что то я не нашел тему про "Edit_Custom_Button_in_Tab" у меня на 74 она до сих пор работает. Поэтому задам здесь. Если есть отдельная тема - то покажите. Вопрос в следующем. Код работает нормально, но выскивает ошибка в консоли не на что не влияющая -
[Exception... "Component returned failure code: 0x805e0006 [nsIWebNavigation.loadURI]"  nsresult: "0x805e0006 (<unknown>)"  location: "JS frame :: chrome://browser/content/browser.js :: _loadURI :: line 1513"  data: no]  - browser.js:1513:29 при редактировании во вкладке. Может посмотрите? Сейчас код у меня такой

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

Выделить код

Код:

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

// Edit Custom Button in Tab button for Custom Buttons
// (code for "initialization" section)

// (c) Infocatcher 2012-2014
// version 0.1.8.3 - 2014-01-12

// Note:
// In Firefox 3.6 and older:
// - Force enables "Save size and position of editor windows separately for each custom button"
//   option for editor in tab (because doesn't work without this)
// - tab with editor can't be closed sometimes using OK/Cancel buttons

var editInTabLabel = (function() {
    var locale = (function() {
        if("Services" in window && "locale" in Services) {
            var locales = Services.locale.requestedLocales // Firefox 64+
                || Services.locale.getRequestedLocales && Services.locale.getRequestedLocales();
            if(locales)
                return locales[0];
        }
        var prefs = "Services" in window && Services.prefs
            || Components.classes["@mozilla.org/preferences-service;1"]
                .getService(Components.interfaces.nsIPrefBranch);
        function pref(name, type) {
            return prefs.getPrefType(name) != prefs.PREF_INVALID ? prefs["get" + type + "Pref"](name) : undefined;
        }
        if(!pref("intl.locale.matchOS", "Bool")) { // Also see https://bugzilla.mozilla.org/show_bug.cgi?id=1414390
            var locale = pref("general.useragent.locale", "Char");
            if(locale && locale.substr(0, 9) != "chrome://")
                return locale;
        }
        return Components.classes["@mozilla.org/chrome/chrome-registry;1"]
            .getService(Components.interfaces.nsIXULChromeRegistry)
            .getSelectedLocale("global");
    })().match(/^[a-z]*/)[0];
    if(locale == "ru")
        return "Редактировать во вкладке…";
    return "Edit button in tab…";
})();

const editorBaseUri = "chrome://custombuttons/content/editor.xul";
const cbIdTabAttr = "custombuttons-editInTab-id";

const editId = "custombuttons-contextpopup-edit";
const editInTabId = editId + "InTab";
var editInTab = document.getElementById(editInTabId);
if(editInTab)
    editInTab.parentNode.removeChild(editInTab);
var editItem = document.getElementById(editId);
editInTab = editItem.cloneNode(true);
editInTab.id = editInTabId;
editInTab.setAttribute("cb_id", editInTabId);
editInTab.setAttribute("label", editInTabLabel);
editInTab.setAttribute("oncommand", "editCustomButtonInTab();");
if(!Object.create) // Firefox 3.6 and older
    editInTab.removeAttribute("observes");
editInTab.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAitJREFUeNrEk0tIVVEUhr99zrnHc1+SkYQVFWo20G6SgkFRDorEILFJDoogkoLoXfOopEZCYIFlIITlRIUiC5UmFZVk1igwCAozfD9uat17z97tc5XK0pGDFiz289/rXz//FkopFhMGiwyr5GoHHgkpZUnCde9LqXwwHyuR3HVNIzZpGrubbLtjfHwYyzvywIYpHp0o386ajAAD3/R1MRfugZeFoGcgbm9pedo+7VVVCMvTwKt8tGwrfXGb5mfRJFj8VV0Ki7hpk9t5gW35lYx9GcSZGFOGSjJQvuXpYd4PTLPUYU6m+Q1SgzbBkEPey0tUHjlJfU8j0c8j+Aa/ahFn2x2cdknVgLBfpx5DyTQIBGx8+oHjras4vX8DtfWNFG0q4gmPyas9VqgZqEbNAEvPUnW1sCMI+QVBPXc02Ar5ONiURW55NsHus1TkjNDW2srHdYcormo/Z2kG+0p35BPTfaaHrVnBBAmt75QwKK3LYuPetdDbDxMTdHc2ELn4gfWj32l+MFQhdla1KVdKpCvxBPXAcWUSlQEaYgeIlK2GvmENHuHOmxVcW3mLoDuqn09gGjNiF8xnEPWw+DUFpTA0BS+uc/tdDodrnhf+445dV9p/LaQw+SH8nBfVRBMpKi0zm5LIEupq7lF541Vhy6lIV3XGTRw5qdWXM078bRSBK3zs6b3MWxEgplxSPnWS2e8kwXfPbO4SanIefy4cf7bWtdAl8d9/408BBgBVmNFVzOyEfgAAAABJRU5ErkJggg==");
editItem.parentNode.insertBefore(editInTab, editItem.nextSibling);

Array.prototype.filter.call( // Process already cloned menu items
    document.getElementsByAttribute("observes", editItem.getAttribute("observes")),
    function(mi) {
        var id = mi.id || "";
        return mi != editItem
            && id.substr(0, editId.length) == editId
            && id.substr(0, editInTabId.length) != editInTabId;
    }
).forEach(function(editItem, i) {
    var clone = editInTab.cloneNode(true);
    clone.id += "-cloned-" + i;
    editItem.parentNode.insertBefore(clone, editItem.nextSibling);
});

// Process #custombuttons-contextpopup-sub
const editIdSub = editId + "-sub";
var editItemSub = document.getElementById(editIdSub);
if(editItemSub) {
    var clone = editInTab.cloneNode(true);
    if(editItemSub.hasAttribute("observes"))
        clone.setAttribute("observes", editItemSub.getAttribute("observes"));
    else
        clone.removeAttribute("observes");
    clone.id += "-sub";
    editItemSub.parentNode.insertBefore(clone, editItemSub.nextSibling);
}

window.editCustomButtonInTab = function(btn, newTab) { // Should be global to work in cloned menus
    if(!btn)
        btn = custombuttons.popupNode;
    if(!btn)
        return;
    var btnId = btn.id;
    var link = custombuttons.makeButtonLink("edit", btnId);
    var cbService = "cbICustomButtonsService" in Components.interfaces
        ? Components.classes["@xsms.nm.ru/custombuttons/cbservice;1"]
            .getService(Components.interfaces.cbICustomButtonsService)
        : Components.classes["@xsms.nm.ru/custombuttons/cbservice;1"] // Custom Buttons 0.0.5.9+
            .getService(Components.interfaces.nsISupports)
            .wrappedJSObject;
    var param = cbService.getButtonParameters(link);
    var editorUriFull = editorBaseUri
        + "?window=" + cbService.getWindowId(document.documentURI)
        + "&id=" + btnId;
    var editorUri = cbService.mode & 64 /*CB_MODE_SAVE_EDITOR_SIZE_SEPARATELY*/
        || !Object.create // Firefox 3.6 and older
        ? editorUriFull
        : editorBaseUri;

    // Search for already opened tab
    var rawParam = unwrap(param);
    var isSeaMonkey = "Services" in window && Services.appinfo.name == "SeaMonkey";
    var ws = Components.classes["@mozilla.org/appshell/window-mediator;1"]
        .getService(Components.interfaces.nsIWindowMediator)
        .getEnumerator(isSeaMonkey ? null : "navigator:browser");
    while(ws.hasMoreElements()) {
        let win = ws.getNext();
        if(isSeaMonkey && win.location.href != "chrome://navigator/content/navigator.xul")
            continue;
        let gBrowser = win.gBrowser;
        let tabs = gBrowser.tabs || gBrowser.tabContainer.childNodes;
        for(let i = 0, l = tabs.length; i < l; ++i) {
            let tab = tabs[i];
            if(tab == newTab)
                continue;
            let browser = tab.linkedBrowser;
            if(!browser)
                continue;
            let loc = browser.currentURI.spec;
            if(loc.substr(0, editorBaseUriLength) != editorBaseUri)
                continue;
            let isSameEditor = loc == editorUriFull
                || tab.getAttribute(cbIdTabAttr) == btnId;
            let win = browser.contentWindow; // Will be null for unloaded tab
            if(!isSameEditor && win) {
                let rawWin = unwrap(win);
                let winParam = "arguments" in rawWin && rawWin.arguments.length
                    ? unwrap(rawWin.arguments[0])
                    : rawWin.editor && rawWin.editor.param;
                isSameEditor = winParam && winParam.buttonLink == link;
            }
            if(isSameEditor) {
                gBrowser.selectedTab = tab;
                win && win.focus();
                newTab && setTimeout(function() {
                    gBrowser.removeTab(newTab);
                }, 0);
                return;
            }
        }
    }

    // Or open new tab
    var tab = newTab;
    if(!tab) {
        tab = gBrowser.selectedTab = gBrowser.addTab(editorUri, {
            triggeringPrincipal: "Services" in window // Firefox 63+
                && Services.scriptSecurityManager
                && Services.scriptSecurityManager.getSystemPrincipal()
        });
        initSessionStore();
        tab.setAttribute(cbIdTabAttr, btn.id);
    }

    var browser = tab.linkedBrowser;
    browser.addEventListener("DOMContentLoaded", function load(e) {
        var doc = e.target;
        if(doc.location != editorUri)
            return;
        browser.removeEventListener(e.type, load, false);

        var win = doc.defaultView;
        win.arguments = [param];

        var iconLink = doc.createElementNS("http://www.w3.org/1999/xhtml", "link");
        iconLink.rel = "shortcut icon";
        //iconLink.href = "chrome://custombuttons-context/content/icons/default/custombuttonsEditor.ico";
        iconLink.href = getStdImage(rawParam.image);
        iconLink.style.display = "none";
        doc.documentElement.insertBefore(iconLink, doc.documentElement.firstChild);

        var alreadyAsked = false;
        function checkUnsaved(e) {
            if(alreadyAsked)
                return;
            var dlg = unwrap(doc).documentElement;
            if(
                "_fireButtonEvent" in dlg
                    ? !dlg._fireButtonEvent("cancel")
                    : !dlg.cancelDialog()
            )
                e.preventDefault();
        }
        function onDialogCancel(e) {
            alreadyAsked = true;
            // win.setTimeout shouldn't fire while confirmation dialog from the same window are opened
            win.setTimeout(function() {
                alreadyAsked = false;
            }, 100);
        }
        function destroy(e) {
            win.removeEventListener("dialogcancel", onDialogCancel, false);
            win.removeEventListener("beforeunload", checkUnsaved, false);
            win.removeEventListener("unload", destroy, false);
        }
        win.addEventListener("dialogcancel", onDialogCancel, false);
        win.addEventListener("beforeunload", checkUnsaved, false);
        win.addEventListener("unload", destroy, false);
    }, false);
};
function unwrap(o) {
    return o.wrappedJSObject || o; // Firefox 3.6 and older
}
function getStdImage(iid) {
    if(/^custombuttons-stdicon-(\d)$/.test(iid)) switch(+RegExp.$1) {
        // chrome://custombuttons/skin/custombuttons.css
        // toolbarbutton[cb-stdicon="custombuttons-stdicon-*"] { ... }
        case 1: return "chrome://custombuttons/skin/button.png";
        case 2: return "chrome://custombuttons/skin/stdicons/rbutton.png";
        case 3: return "chrome://custombuttons/skin/stdicons/gbutton.png";
        case 4: return "chrome://custombuttons/skin/stdicons/bbutton.png";
    }
    return iid || "chrome://custombuttons/skin/button.png";
}

function initSessionStore() {
    initSessionStore = function() {};
    var ss = "nsISessionStore" in Components.interfaces
        ? (
            Components.classes["@mozilla.org/browser/sessionstore;1"]
            || Components.classes["@mozilla.org/suite/sessionstore;1"]
        ).getService(Components.interfaces.nsISessionStore)
        : SessionStore; // Firefox 61+ https://bugzilla.mozilla.org/show_bug.cgi?id=1450559
    ss.persistTabAttribute(cbIdTabAttr);
}
function checkTab(tab) {
    var cbId = tab.getAttribute(cbIdTabAttr);
    if(!cbId)
        return;
    initSessionStore();
    let btn = document.getElementById(cbId);
    if(btn)
        editCustomButtonInTab(btn, tab);
}
const editorBaseUriLength = editorBaseUri.length;
// We can't use only SSTabRestoring: user can reload tab with editor
addEventListener("DOMContentLoaded", function(e) {
    var doc = e.target;
    if(doc.location.href.substr(0, editorBaseUriLength) != editorBaseUri)
        return;
    var tabs = gBrowser.tabs || gBrowser.tabContainer.childNodes;
    for(var i = 0, l = tabs.length; i < l; ++i) {
        let tab = tabs[i];
        let browser = tab.linkedBrowser;
        if(browser && browser.contentDocument == doc) {
            checkTab(tab);
            break;
        }
    }
}, true, document.getElementById("appcontent")); // Firefox 60+, gBrowser isn't a DOM node anymore 
checkTab(gBrowser.selectedTab);

function destructor(reason) {
    if(reason == "update" || reason == "delete") {
        Array.prototype.slice.call(document.getElementsByAttribute("cb_id", editInTabId)).forEach(function(btn) {
            btn.parentNode.removeChild(btn);
        });
        delete window.editCustomButtonInTab;
    }
}
if(
    typeof addDestructor == "function" // Custom Buttons 0.0.5.6pre4+
    && addDestructor != ("addDestructor" in window && window.addDestructor)
)
    addDestructor(destructor, this);
else
    this.onDestroy = destructor;

Andrey_Krropotkin
Edit_Custom_Button_in_Tab здесь совершенно нипричём.

Это из-за Custom Buttons в моём исполнении.
По совершенно посторонним groupbox'ным причинам
захотелось посмотреть, насколько плохо это смотрится на Linux.

С превеликим трудом и приключениями поставил в виртуалку
Mint 19.3 Mate, и оказалось(!), что там, в отличие от Windows,
.xul адреса не просто грузятся ущербно, а вообще не грузятся.
Вместо загрузки выскакивает знаменитый диалог unknownContentType.xhtml

Поэтому было принято решение делать так:
nsIContentPolicy.shouldLoad() <— REJECT_REQUEST,
xhtml-override —> browsingContext.loadURI()

Таким образом, данная ошибка это побочный эффект.
Увы, могу только лишь предложить просто смириться с этим.

Infocatcher
В Firefox 99 завезли баг:
Bug 1753836 - MouseEvent.screenX/Y coordinate space is weird.


STR:
Открываем about:preferences, меняем зум на больше-меньше 100%,
запускаем AI, наводим на элемент страницы.


AR:
AI-тултип позиционируется не там, где должен, а со смещением.


Это в лучшем случае.
Если он расположится так, что на него попадёт указатель мыши,
то код это подхватит, и пойдёт отображение атрибутов элемента самого тултипа,
что приведёт к дёрганью и прочим глюкам.


Похоже, себе и нам, навязана необходимость некого перерасчёта.
Вот, такая добавка, вроде, помогает

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

Выделить код

Код:

…
		mousemoveHandler: function(e) {
			var tt = this.context.tt;

			if(!this._hasData) {
				this.mouseoverHandler(e);
				return;
			}

			var x, y;
			if(e) {
				x = e.screenX;
				y = e.screenY;

				// ▼▼▼▼▼
				if(this.fxVersion >= 99) {
					var k = e.view.devicePixelRatio / tt.ownerGlobal.devicePixelRatio;
					x *= k;
					y *= k;
				}

Dumby
Не было печали, апдейтов накачали… :sick:
Спасибо, обновил: https://github.com/Infocatcher/Custom_Buttons/commit/0865ba7

Infocatcher
Для Firefox 109 переключили настройку этого бага.
Теперь это затрагивает и релиз, и пора что-то с этим делать.


Проявляется в том, что Attributes Inspector больше не показывает margin, border, padding,
и, иногда, не показывает размеры (или показывает неправильно).


Такое происходит во всех окнах, кроме того, в котором запущен код,
то есть, контент, сайдбар, любые другие окна брузера и не браузера.


Насколько я вижу, Element и XULElement используются только для оператора instanceof,
поэтому сделал себе так (последняя строка обозначает место, перед которой добавлен остальной код).

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

Выделить код

Код:

//...
	var omit = this.eventHandler.fxVersion < 106;
	var defElm = function(name) {
		var elm = window[name];
		if(omit) return elm;

		var res = {};
		res[Symbol.hasInstance] = elm.isInstance.bind(elm);
		return res;
	}
	var Element = defElm("Element");
	var XULElement = defElm("XULElement");

	this.setAllListeners(ael);


Метод isInstance() довольно старый, Firefox 59+

Infocatcher
Да, ещё упустил упомянуть, что в Firefox 110+
удалили классический contract id для промпт-сервиса.

Infocatcher
Ещё, в Firefox 111+, поломали Ctrl+Shift+C копирование с тултипа.
Bug 1776879 - Investigate if we could get rid of text/unicode, but use `text/plain` directly for plain text for Clipboard and DnD

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

Выделить код

Код:

/*
			this.setClipboardData({
				"text/unicode": text.replace(/\r\n?|\n/g, this.lineBreak),
				"text/html":    html.replace(/\r\n?|\n/g, this.lineBreak)
			}, sourceWindow);
*/
			var data = {
				"text/html": html.replace(/\r\n?|\n/g, this.lineBreak)
			};
			data["text/" + (this.fxVersion >= 111 ? "plain" : "unicode")]
				= text.replace(/\r\n?|\n/g, this.lineBreak);
			this.setClipboardData(data, sourceWindow);