Пишем свой плагин для браузера Mozilla Firefox

—

от автора

Итак, после обновления Firefox до 19 версии, полностью отвалился горячо любимый плагин Яндекс.Бар. Не забуду напомнить, что Яндекс.Бар был заменен Яндекс.Элементами, которые понравились чуть больше, чем никому, поэтому и получили своё заслуженные 2 бала из 5ти.

Почему не понравились? Заменили адресную строку, стало неудобно просматривать почту, заменили закладки и убрали корректор адресной строки (под предлогом установки Punto Switcher, который может и хорош для обычного работника, но никак не для программиста. Поэтому и был удален почти сразу же, как установлен. Да и если можно было бы настроить, то всё равно желание пропало).

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

Первым делом решено было не создавать свой велосипед и воскресить Яндекс.Бар, который не хотел работать в 19 версии браузера. В интернете подсказали, что плагин — это обычный zip архив. Открыли, посмотрели, ужаснулись и закрыли. Воскресить не удалось, даже при всем желании.

Тогда заходим в центр разработчика: builder.addons.mozilla.org/. Я предпочел орудовать в веб-редакторе, хоть местами он иногда и не очень гладко работал. Посмотрев на другие плагины, позаимствовав код и немного поняв весь смысл сея устройства, началось сначала всё со стенобитной машины и закончилось надфилем.

Билдер включает в себя 3 раздела: это раздел со скриптами (Lib), раздел с загружаемым контентом (картинки, стили и скрипты) и раздел с готовыми библиотеками (Libraries)

Кстати, вот документация: addons.mozilla.org/en-US/developers/docs/sdk/latest/, добротно написанная.
Старт плагина начинается с загрузки файла main.js.
Вызывается функция: exports.main.

Пример файла main.js:

const tabs = require("tabs");  exports.main = function (options) {          tabs.on("ready", function(tab){       tab.attach({         contentScript: "document.addEventListener('click', function(e) { \ 				var target = e.target; \ 				if(target.tagName == 'A') { \                     var mail_to = target.href.match(/^mailto:(.*)/i); \ 					if(mail_to != null) { \ 						e.preventDefault(); \ 						var form = document.createElement('form'); \ 						form.setAttribute('action','http://mail.yandex.ru/neo2/#compose/mailto=' + mail_to[1]); \ 						form.setAttribute('target','_blank'); \ 						document.getElementsByTagName('body')[0].appendChild(form); \ 						form.submit(); \ 						form.parentNode.removeChild(form); \ 					} \ 				} \ 			}, false);"       });     }); } 

Что же за магия происходит в этом коде?

Первым делом подключается модуль tabs.
В данном случае он служит для того, чтобы можно было добавлять свой JavaScript код в страницу браузера.
Т.е. что у нас: при событии документа onready происходит добавление любого JavaScript кода в тело документа. В данном примере добавляется обработчик ссылок, у которых адрес начинается с mailto.

Ладно, давайте что-нибудь посложнее сделаем. Добавим-ка свою кнопку в верхний бар!
Опять же, не будем строить велосипеды, а с чистой совестью возьмем уже готовую библиотеку Toolbar Button Complete.

В ней же есть пример добавления кнопки в бар браузера. Я думаю, не стоит его сюда вываливать, т.к. там многоватенько кода.
Итак, кнопка есть, иконку поставили, всё вроде хорошо, но не очень. Как же у нас в Яндекс.Баре было? Ах да, напротив иконки еще и счетчик непрочитанных сообщений был.
Тут я разузнал несколько путей добавления счетчика:

  • универсальный, но более легкий (с помощью стилей)
  • не слишком универсальный, но не такой простой, как первый (с помощью canvas)

Второй способ, правда, нашелся методом тыка в интернет. Но я взял первый.
Нам известно, что верхний бар — это такой же набор элементов со своими классами, идентификаторами, свойствами и способами работы с ними.

Методом тыка типа:

for(var val in document.getElementById('yandex-menu')) {    console.log(val); } 

было обнаружено, что методы в точности совпадают с теми, что мы обычно используем при работе с элементами сайта. Но замечу, что по стандарту браузер не знает, что такое ни document, ни window в расширениях (да и еще есть отличия).

Пример решения:

var wuntils = require('sdk/window/utils'); var window = wuntils.getMostRecentBrowserWindow(); var document = window.document; 

Замечу, что разработка билдера не стоит на месте и если раньше способ получения активного окна был таким:

var winUtils = require("window-utils"); for (window in winUtils.windowIterator()) {    if ("chrome://browser/content/browser.xul" != window.location)  return;    console.log("An open window! " + window.location); } 

то сейчас всё намного легче (пример я выше привел).

Чтож, немного рассказав о особенностях, вернусь к добавлению счетчика для кнопки.
Умные люди подсказали, что по стандарту стиль поля labelу кнопки равен display: none;, поэтому как-то нужно было внедрить свой css код в бар. Решение, как оказалось, не сложное (советую завернуть в файл, который будет инклюдится по мере надобности):

const { Cc, Ci } = require('chrome'); const { when: unload } = require('sdk/system/unload');   var ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);   /* Helper that registers style sheets and remembers to unregister on unload */ exports.addXULStylesheet = function addXULStylesheet(url) {     var uri = newURI(url); 	var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); 	 	sss.loadAndRegisterSheet(uri, sss.USER_SHEET); 	unload(function () { 		if (sss.sheetRegistered(uri, sss.USER_SHEET)) { 			sss.unregisterSheet(uri, sss.USER_SHEET); 		} 	});          return sss; };   function newURI(uriStr, base) { 	try { 		var baseURI = base ? ios.newURI(base, null, null) : null; 		return ios.newURI(uriStr, null, baseURI); 	} 	catch (e) { 		if (e.result === chrome.Cr.NS_ERROR_MALFORMED_URI) { 			throw new Error("malformed URI: " + uriStr); 		} else if (e.result === chrome.Cr.NS_ERROR_FAILURE || 			e.result === chrome.Cr.NS_ERROR_ILLEGAL_VALUE) { 			throw new Error("invalid URI: " + uriStr); 		} 	} 	return null; } 

И в функцию exprorts.main добавляем что-то вроде (хотя добавлять можете куда угодно):

stylesheet.addXULStylesheet(data.url("stylesheet.css")); 

не забыв создать в контенте файл stylesheet.css.

У меня файл содержит примерно следующее:

#yandex-mail {     min-width: 16px; } #yandex-mail .toolbarbutton-text {      float: right !important;     display: inline-block !important;     font-size: 13px;     padding-left:20px;     background: url(data:image/png;base64,iVB.............OCYII=) no-repeat left center; } #yandex-mail .toolbarbutton-icon {      display: none; } 

Почему мы скрываем иконку и добавляем фон? Всё потому, что если этого не сделать, то блоки всегда отображаются как display: block, какие бы значения я не выставлял (кстати, может кто знает по этой теме?) Поэтому и приходится так хитрить.

Также столкнулся с вопросом загрузки контента с других сайтов и парсинг xml.
С первым быстро разобрался, далеко ходить не надо: Request
А вот со вторым пришлось повозиться.

Как мы знаем, получить dom xml документа можно с помощью нескольких функций:

  • XMLHttpRequest — отпал, т.к. выдало ошибку кроссдоменного запроса (может я не так что-то делал?)
  • DOMParser — но тут тоже пришлось повозиться

В чем собственно возня: как и с получением window, так и тут:

var {Cc, Ci} = require("chrome"); var parser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser); var dom = parser.parseFromString(xmlPrepare (text), "application/xml"); 

Вот так создание плагинов для Firefox ничем не отличается от создания плагинов для jQuery 🙂

Кстати, конечное творение на сей день: CustomYandexBar, пока находится на проверке. Исходники, в них много чего полезного.

Если кому-нибудь не понравится, что использую «их» картинки, бренд или т.п. — пишите.

ссылка на оригинал статьи http://habrahabr.ru/post/171517/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *