четверг, 11 ноября 2010 г.

Рецепт быстрого приготовления расширений под популярные браузеры

0147 (1)

Возникла задача написание простенького расширения под все (по возможности) популярные браузеры. Деятельность расширения заключается во внедрении javascript`а в тело документа.

Доктор прописал инъекции javascript`а


Казалось бы все просто, выполняем в строке браузера код:

  1. javascript: var s = document.createElement('script');
  2. s.type='text/javascript';
  3. document.body.appendChild(s);
  4. s.src='script.js';
  5. void(0);
Но что делать если ваш скрипт должен обрабатывать все страницы чужого сайта. Не заставлять же пользователя постоянно click`ать по закладке с кодом инъекции после каждого перехода на новую страницу. Наивные поиски простого решения не увенчались успехом:
  • Это был перехват всех ссылок на страничке и добавление своего кода «javascript:”, разумеется код выполнялся в рамках открытой страницы и потом переходил на новую «чистую” страницу через (location.href).
  • Вариант с setTimeout был вообще в порядке бреда… выполение отваливалось после начала загрузки новой страницы.
Написание расширений под множество браузеров меня пугала больше всего, но что делать, начал вспоминать наиболее популярные:
  • IE 7+ (на 6-ку решил просто забить, много затрат ради небольшой аудитории пользователей – уж простите меня)
  • Firefox 1.5+
  • Chrome 4+
  • Opera 9+ (в итоге оказалось extension`ы можно писать только под Opera 11)
  • Safari 3+
«С Firefox и Chrome проблем не должно возникнуть” – подумал я.

Google Chrome


За час был написал extension для Chrome – масса документации на официальном сайте, масса примеров. В общем рай для разработчика.

Примеры и Руководство

фрагмент из background.html

  1. function onRequest(tabId, changeInfo, tab) {
  2.         if (changeInfo.status == 'complete'){
  3.                 chrome.tabs.executescript(tabId, { code:"код без javascript:" });
  4.         };
  5. };
  6. chrome.tabs.onUpdated.addListener(onRequest);

Все получилось просто и со вкусом. Напрямую вставить в тело документа <script> не получилось, гугл ругался на чужой домен в адресе скрипта, разбираться было не когда, и executescript сильно упростил реализацию.

Расширение собирается средствами Google Chrome:

image

”Как все здорово пошло” – подумал я. «Такими темпами я завтра для всех браузеров напишу”…

Mozilla Firefox


Хорошо обстоят дела с документацией, с примерами правда похуже чем у Chrome, но есть даже online builder addon`ов (делает пустышку zip, который необходимо «собрать” в xpi добавлением своих исходников).

Правда вначале смутил very basic extension на главной который не работал. Который через пару дней убрали со страницы слава богу.

Документация, Addon Builder

Синтаксис тоже вполне логичный и стандартный, фрагмент из chrome/content/js/main.js

  1. var test = {
  2.         onload: function(aEvent){
  3.                 var doc = aEvent.originalTarget;
  4.                 if (doc instanceof HTMLDocument && doc.location.href != "about:blank" && !doc.defaultView.frameElement) {
  5.                         var s = doc.createElement('script');
  6.                         s.type = 'text/javascript';
  7.                         doc.body.appendChild(s);
  8.                         s.src = 'script.js';
  9.                 }
  10.         }
  11. }
  12. window.addEventListener("load", function(){
  13.         var appcontent = document.getElementById("appcontent");         if (appcontent) {
  14.                 appcontent.addEventListener("DOMContentLoaded", test.onload, true);
  15.         }
  16. }, false);

Safari


Upd: Спасибо Tails за информацию.

Документация и примеры

Подробная пошаговая инструкция

Главное нужно получить Сертефикат разработчика Safari, для этого нужно получить AppleID и вступить в группу разработчиков на apple.com

Microsoft Internet Explorer


Вот тут конечное я дня 2 убил на изучение спецификации по COM объектам и поиска рабочих примеров addon`ов. В итоге нашел на одном китайском сайте рабочий пример с привязкой к событиям браузера.

Привязываемся к событию DISPID_DOCUMENTCOMPLETE, и в случае возникновения выполняем код:

  1. VARIANT vFlags = { 0 };
  2. VariantInit(&vFlags);
  3. vFlags.vt = VT_I4;
  4. vFlags.intVal |= navNoReadFromCache;
  5. BSTR testUrl = SysAllocString(L"javascript: код инъекции");
  6. mWebBrowser2->Navigate(testUrl, &vFlags, NULL, NULL, NULL);

Но радость была бы не полной без такого замечательного бага. У IE проблема с обновлением по F5 и событием DocumentComplete.

Оно просто не вызывается при обновлении странички через F5
Для решения проблемы с F5, вешаем hook и по нажатию выполняем код:

  1. CComBSTR url;
  2. mWebBrowser2->get_LocationURL(&url);
  3. VARIANT v;
  4. v.vt=VT_I4;
  5. v.lVal= (navNoHistory);
  6. mWebBrowser2->Navigate(url, &v, NULL, NULL, NULL);

Который вызовет DISPID_DOCUMENTCOMPLETE «искусственным» путем.

Opera


Тут оказалось все очень просто и красиво, правда есть баги в работе addon`ов, но Opera 11 все-таки еще alpha.

Статьи с примерами

фрагмент из includes/base.js

  1. window.addEventListener('load', function(event) {
  2.         var currentDocument = (window.document) ? window.document : false;
  3.         opera.extension.onmessage = function(event) {
  4.                 if (currentDocument) {
  5.                         var s = currentDocument.createElement('script');
  6.                         s.type='text/javascript';
  7.                         currentDocument.body.appendChild(s);
  8.                         s.src='http://our_site/script.js';
  9.                 }
  10.         };
  11. }, false);

фрагмент из background.js

  1. window.addEventListener("load", function() {
  2.         function myEvent() {
  3.                 var tab = opera.extension.tabs.getFocused();
  4.                 if (tab) {
  5.                         tab.postMessage('go');
  6.                 }
  7.         }
  8.         opera.extension.onconnect = myEvent;
  9. }, false);

Из обнаруженных мною проблем:
  • Из javascript`а делаем back history и событие onconnect не срабатывает.
  • Из функции в onmessage нельзя обратиться к window.document, может оно конечное так и задумано, но не логично.
На мой взгляд ребята из opera хорошо постарались над api.
А вот под 9 и 10 я не нашел простого (простота установки конечным пользователем) решения реализации addon`а.

Upd: Под Opera также возможна установка UserJS через свой installer (админских прав не нужно), установка происходит через файл opera.ini который расположен в профиле пользователя, раздел [User Prefs] > «User Javascript File»

Примеры скриптов  И еще

Фрагмент из userjs.js
  1. addEventListener('load', function(e){
  2.     if (!document.body) {
  3.         return;
  4.     }
  5.     var s = document.createElement('script');
  6.     s.type = 'text/javascript';
  7.     document.body.appendChild(s);
  8.     s.src = 'userjs.js';
  9. }, false);



Источник: Хабрахабр - Web-разработка
Оригинальная страница: Рецепт быстрого приготовления расширений под популярные браузеры

Комментариев нет:

Отправить комментарий