Написание Firefox дополнения в боевых условиях, или как писался 4PDA Инспектор
Внимание! Содержимое данной статьи устарело. Сейчас дополнения создаются с помощью WebExtensions.
В этой статье мы разберем написание Firefox дополнения и затронем некоторые интересные темы на примере 4PDA-Инспектора. Суть дополнения в мониторинге форума 4pda.ru. Сразу скажу, что на некоторых неинтересных или слишком мелких вещах мы останавливаться не будем, а полный код расширения будет доступен в конце каждой части.
v.0.1. Начальные настройки и базовый функционал
Цель: парсинг количества избранных тем с новыми ответами и вывод информации на кнопку дополнения в панели навигации.
Парсинг – это синтаксический анализ сайтов, который автоматически производится парсером – специальной программой или скриптом. Характер парсинга определяется заданием получить определенную информацию со страниц сайта, параметры анализа заранее задаются.© webeffector.ru
Создание ничего не делающего дополнения мы разобрали в прошлой статье (вот в этой), из него и эволюционировал 4PDA Инспектор. Первым делом были изменены значения в install.rdf и chrome.manifest. Пакет был назван "inspector" (впоследствии пришлось изменить это название на "4pdainspector", так как "inspector" использовался другим расширением, потому во избежание конфликтов настоятельно рекомендуется использовать уникальные названия), id дополнения стало 4pda_inspector@coddism.com. Также с помощью манифеста был добавлен стиль в customizeToolbar (настройка панелей инструментов):
style chrome://global/content/customizeToolbar.xul chrome://4pdainspector/content/css/main.css
Файл стилей main.css тоже довольно мал и содержит 2 стиля для одной кнопки разных размеров:
# inspector-button toolbar [ iconsize="small" ] # inspector-button
Перейдем к overlay.xul. Первым делом избавляемся от мусора myfirstextension в виде панели в статус баре. Вторым делом мы добавляем кнопку на панель инструментов.
< toolbarpalette id = "BrowserToolbarPalette" > <toolbarbutton id = "inspector-button" label = "4PDA Инспектор" tooltiptext = "4PDA Инспектор">'inspectorToolbar.buttonClick()' /> </toolbarpalette>
Здесь toolbarpalette – это и есть сама панель инструментов, внутрь неё мы и добавляем нашу кнопку toolbarbutton . Параметр label — это заголовок кнопки, tooltiptext — всплывающий при наведении текст, oncommand — действие при клике.
Третьим делом укажем пути к js-файлам:
<script type = 'text/javascript' src = 'chrome://inspector/content/js/utils.js' ></script> <script type = 'text/javascript' src = 'chrome://inspector/content/js/contentscript.js' ></script> <script type = 'text/javascript' src = 'chrome://inspector/content/js/toolbar.js' ></script>
Utils.js создан для хранения класса отладки, в нем всего одна функция:
var utils = ;
С её помощью мы пишем в лог — всегда полезная функция. Для просмотра лога понадобится консоль ошибок Console² (скачать можно на addons.mozilla.org).
Полное описание компонента nsIConsoleService можно прочитать здесь.
Toolbar.js хранит объект inspectorToolbar, в котором находится функция клика по кнопке buttonClick().
var inspectorToolbar = ;
Здесь мы просто открываем новую вкладку со списком избранных тем, адрес которого хранится в inspectorContentScript.favUrl. Далее разберем основной скрипт дополнения — contentscript.js. Для начала полный его код:
updateTimer: 0, winobj: null , favUrl: 'http://4pda.ru/forum/index.php?autocom=favtopics' ,
init: function () , 5000); >,
getNewCount: function () //запрос кода страницы
req.open( "GET" , inspectorContentScript.favUrl, true ); req.send( null ); >,
getFavCount: function (text) // парсинг количества непрочитанных избранных тем ,
printCount: function (count, mesCount) // вывод количества на кнопку ,
printLogout: function () // вывод информации о неавторизованности >;
inspectorContentScript.init(); //запуск механизма
- init() запускаем таймер.
- getNewCount() Забираем содержимое страницы избранных тем. Запросы на удаленный сервер позволяет производить объект XMLHttpRequest. Присваиваем объекту функции req.onreadystatechange для удачного выполнения и req.onerror для обработки ошибки. Адрес, тип (в нашем случае GET и inspectorContentScript.favUrl ) запроса указываются в req.open, также в нем указываем асинхронность запроса третьим параметром true. При удачном запросе — readyState 4 (Interactive) и req.status 200 (HTTP/1.0 200 OK) — парсим из текста ответа req.responseText количество непрочитанных тем (getFavCount) и выводим количество на кнопке (printCount), в ином случае вызываем printLogout — делаем кнопку серой и устанавливаем всплывающий текст «4PDA - Не в сети».
- getFavCount() Темы с новыми ответами сопровождаются на форуме картинкой http://s.4pda.ru/forum/style_images/1/newpost.gif, следовательно, чтобы узнать количество необходимых нам тем, мы должны подсчитать количество этих картинок. Парсим результат запроса регуляркой /(http:\/\/s.4pda.ru\/forum\/style_images\/1\/newpost.gif)/ig и запрашиваем длину получившегося массива — одно из самых простых реализаций в данном дополнении
- printCount() Вывод полученной информации на кнопке. Почти вся работа здесь — это работа с html canvas. Именно для этого мы создавали скрытый html:canvas в overlay.xul. Задаем стиль текста, подстраиваем под него прямоугольную область и выводим её в правом нижнем углу. 24х24px - стандартные размеры иконки в Firefox для Linux, для остальных систем стандартным является 16х16px, универсализация будет через несколько версий.
Вот и всё, количество тем с новыми ответами высвечивается в панели — первая версия готова.
Скачать 4pda_inspector v.0.1: zip, xpi
v.0.2.x Парсинг и вывод количества новых сообщений
Цель: парсинг количества новых сообщений и вывод его на кнопку дополнения в панели навигации.
Количество непрочитанных тем — это, конечно, хорошо, но количество новых сообщений — еще лучше.
Количество новых сообщений отображается вот в этой ссылке, оттуда это количество и возьмем. Эта ссылка отображается на всех страницах форума, в том числе и на странице со списком избранных тем, поэтому будем парсить тот же самый текст.
if (req.responseText)
Напишем функцию getMesCount.
getMesCount: function (text)
Ищем ссылку http://4pda.ru/forum/index.php?act=Msg&CODE=01 и берем в ней цифры после двоеточия. Если нет ссылки, то пользователь не авторизован и надо вызвать printLogout. Осталось добавить вывод количества сообщений на кнопке, для этого добавим несколько строк в функцию printCount:
var w = ctx.measureText(mesCount).width;ctx.fillStyle = "#d62f2f" ;ctx.fillRect(0, y, w+2, h+5);ctx.fillStyle = "#fff" ;ctx.fillText(mesCount, 1, y+1);
Всё, количество сообщений выводится в левом нижнем углу кнопки.
Скачать 4pda_inspector v.0.2.1: zip, xpi
v.0.3.x Настраиваем настройки
Цель: сделать настройку интервала обновления.
Показ информации — это уже неплохо, но 5 секунд для обновления кому-то (примерно всем) может показаться слишком частым — необходимо дать пользователям выбор. Приступим.
Первым делом нужно задать настройки по умолчанию. Чтобы это сделать, нужно создать js-файл (его название, в принципе, не важно, лишь бы на латинице; обычно его называют defaults.js) в папке defaults/preferences. Создадим inspector.js и запишем в него настройку:
pref( "extensions.4pda-inspector.interval" , 5);
Принимаются настройки трех типов: int, string, bool. Настройки должны (но не обязаны) быть именованы как extensions.*уникальное_имя_дополнения*.*название_настройки*.
Далее нужно создать сам интерфейс окна настроек - создадим settings.xul. Название может быть любым, но опять же на латинице и иметь расширение xul =).
<? xml version ="1.0" ? > <? xml-stylesheet href ="chrome://inspector/content/css/settings.css" type ="text/css" ? > < prefwindow id ="settings-window" buttons ="accept,cancel" title ="Настройки - 4PDA Инспектор" xmlns:html ="http://www.w3.org/1999/xhtml" xmlns ="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" >
< prefpane id ="inspector_pane" label ="Telefum Settings" > < preferences > < preference id ="pref_interval" name ="extensions.4pda-inspector.interval" type ="int" /> </ preferences > < hbox > < vbox > < image src ='chrome://inspector/content/icons/icon_128.png' width ='128' height ='128' align ="left" /> </ vbox > < vbox class ="inspector_allSettingsBox" > < html:div class ="inspector_allSettingsBoxHeading" > Общие настройки: </ html:div > < vbox class ="inspector_settingsBox" > < label value ="Интервал обновления (сек.):" /> < textbox preference ="pref_interval" id ="inspector_intervalInput" type ="number" min ="1" /> </ vbox > </ vbox > </ hbox > </ prefpane > </ prefwindow >
В плане xul-вёрстки ничего сложного — логически она почти ничем не отличается от html, подробно о xul-элементах можно узнать в документации по этой ссылке. Остановлюсь лишь на привязке настроек к элементам. Как не сложно заметить, name совпадает с названием настройки.
< preference id ="pref_interval" name ="extensions.4pda-inspector.interval" type ="int" />
И устанавливаем настройку в элемент с помощью атрибута preference по указанному id:
< textbox preference ="pref_interval" id ="inspector_intervalInput" type ="number" min ="1" />
Textbox - это текстовое поле наподобие <input type='text'>, установленный type='number' делает его числовым и добавляет updown, минимальное число устанавливаем в 1 — интервал обновления 0 секунд как-то неразумен.
Чтобы добавить ссылку (кнопку?) на настройки в списке управления дополнениями (открывается по ctrl+shift+a, адресу about:addons и через меню), необходимо добавить строчку в install.rdf:
< em:optionsURL > chrome://inspector/content/settings.xul </ em:optionsURL >
При применении настроек (в Windows и Linux нужно нажать на «ОК», в MacOS просто закрыть окно) настройки сохранятся без каких-либо дополнительных скриптов. Все настройки браузера и всех дополнений доступны по адресу about:config. Список дополняется текстовым полем для поиска, что заметно упрощает нахождение необходимых настроек.
Вывод и сохранение настроек - это круто, но их нужно как-нибудь задействовать, иначе зачем они? Создаем content/js/default.js, добавляем его в overlay.xul
< script type ='text/javascript' src ='chrome://inspector/content/js/default.js' ></ script >
В сам default.js пишем:
interval: 5000, //интервал по умолчанию, надо же чему-нибудь присвоить
prefs: , //объект, куда запишутся все настройки
getPrefs: function () //пишем настройки в prefs
То есть, храним в объекте inspectorDefaultStorage все настройки и методы их добывания. Значение умножаем на 1000, т. к. интервал в setTimeout задается в миллисекундах.
Осталось изменить метод newIteration() в объекте inspectorContentScript:
newIteration: function ()
На том и закончим.
Скачать 4pda_inspector v.0.3.0.2: zip, xpi
v.0.4.x Всплываем и показываем
Как бы то ни было, но в рамках 24x24 px (и, тем более, в 16х16 px) много информации не покажешь. Оповещение о новых ответах в темах это хорошо, но удобно же было бы сразу просмотреть список тем и при желании перейти на какие-нибудь из них. Или, кликнув на кнопку, открыть сразу все эти темы в новых вкладках. Это и есть задача для версии 0.4.x.
Добавим всплывающую панель добавив в overflow.xul её верстку:
< toolbox id ="navigator-toolbox" > < panel type ="arrow" id ="inspectorPanel" align ="left" > < vbox > < vbox id ="inspectorPanel_themesList" > </ vbox >
< vbox id ="inspector_middleVBox" > < label value ="Перейти к списку избранных тем" id ="inspector_goToFavs" /> < label value ="Открыть все непрочитанные темы в новых вкладках" id ="inspector_openAllFavs" /> </ vbox >
< hbox id ="inspector_messagesHBox" > < label value ="Новых сообщений: " /> < label value ="0" id ="inspector_unreadMessageCount" /> </ hbox > < hbox class ="inspector_messagesHBox" > < label value ="Настройки" id ="inspector_openSettings" /> </ hbox > </ vbox > </ panel > </ toolbox >
Дадим этой панели id "inspectorPanel" и событие по скрытию панели (onpopuphiding) inspectorToolbar.hadePanel(). Элемент vbox создан для списка тем. О назначении остальных элементов можно узнать из их заголовков.
Подробнее об элементе panel можно узнать здесь.
Далее toolbar.js. Он изменился полностью, если не считать названия.
winobj: null , panel: null , list: null , unreadThemes: [],
link_favTopics: 'http://4pda.ru/forum/index.php?autocom=favtopics' , link_messages: 'http://4pda.ru/forum/index.php?act=Msg&CODE=01' ,
init: function () ,
// клик по кнопке в панели инструментов buttonClick: function (parent) ,
// сбор информации по непрочитанным темам parseThemes: function ()