Написание Firefox дополнения в боевых условиях, или как писался 4PDA Инспектор

Написание 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(); //запуск механизма

  1. init() запускаем таймер.
  2. 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 - Не в сети».
  3. getFavCount() Темы с новыми ответами сопровождаются на форуме картинкой http://s.4pda.ru/forum/style_images/1/newpost.gif, следовательно, чтобы узнать количество необходимых нам тем, мы должны подсчитать количество этих картинок. Парсим результат запроса регуляркой /(http:\/\/s.4pda.ru\/forum\/style_images\/1\/newpost.gif)/ig и запрашиваем длину получившегося массива — одно из самых простых реализаций в данном дополнении
  4. 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 ()

📎📎📎📎📎📎📎📎📎📎