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

Итак, мне необходимо было написать приложение, которое бы подключалось к Mozilla и FF и в выбранный пользователем момент выполняло определенные действия.
Представьте, у Вас есть какая-то база (приложение написанное на Delphi или С++ или вообще с помощь MFC -  в дальнейшем назовем это приложение Главным),  пользователь кликает мышью по какому-то полю в Mozilla и в базу заносится информация записанная в этом поле, тип поля и так далее…плюс - информация о страничке. Основная проблема! – при этом Mozilla не получает управления, то есть – управление у Главного приложения!
И следующий шаг (более легкий): в дальнейшем, когда пользователь переключается между страничками, автоматически заполняются все поля, которые сохранены в базе.
Все примеры которые я смотрела были написаны с помощью JS… причем я не сильно понимаю – зачем делать на джаве скрипт диалоговые окна – если можно написать компоненту…. Ладно, это уже пожелание авторов…

С чего я начала: http://xul.ru/- хороший сайт – но совершенно не то, что мне надо… полазив по http://xulplanet.com/(основной помощник) и сайту  Mozilla – поняла, что плагины совершенно не для меня – Плагин выполняется только для определенной странички.
Значит, надо писать расширение: взяла пример http://www.borngeek.com/firefox/tutorial/(есть и переведенный вариантhttp://www.toolbar.net.ru/1.html), помучила этот пример…
Не буду приводить здесь строгую схему создания xpi файла – так как она очень хорошо описана в этих ссылках. Но получилось у меня в результате такое:
Myhook.xpi ->> install.rdf
                      install.js
                      [chrome]->> myhook.jar->>[content]->> actions.js
                                                                              contents.rdf
                                                                              myhook.js
                                                                              myhook.xul

Myhook.xpi – это переименованный zip файл;
install.rdf и install.js взятые из примера в них только изменены - имя расширения, автор, GUID и имя jar файла.
myhook.jar – делаю с помощью батника jar.bat:

Выделить код

Код:

" [путь] \bin\jar.exe" -cf0 myhook.jar *.*

contents.rdf –  измены только пути и имена;
myhook.xul – прописаны два скрипта:

Выделить код

Код:

<script type="application/x-javascript" src="actions.js" />
<script type="application/x-javascript" src="myhook.js" />

myhook.js (все содержимое файла)

Выделить код

Код:

var actions = new actions();

window.addEventListener("load", formInitialize, true);

function formInitialize() {
   try {
       window.addEventListener("load", actions.doLoad,true); 
      // перехватываем события загрузки новой страницы
   } catch(e) {
   } 			      
}

actions.js (все содержимое файла):

Выделить код

Код:

function actions() {
	
   this.doLoad = doLoad;

   function doLoad() {
        alert('Load page…');
   }
}

Вот и получилось расширение, которое каждый раз при загрузке странички выдает сообщение «Load page…». Кстати, иногда выдает и по несколько раз на страничку или бывает, что и не выдает – если загрузка никак не может закончиться…

Но это расширение не может решить проблему обмена информации с Главным приложением. Поэтому я начала искать возможность добавить какой-то элемент – способный вызвать функции главной программы.

Выход нашла в создании компоненты: http://www.iosart.com/firefox/xpcom/. Снова же описание очень хорошее, а главное все работает.

Итак, копируем пример, рассматриваем его со всех сторон и экспериментируем. Общая схема создания и добавления компоненты в Mozilla такова:

1.  Скачаем [gecko-sdk]
2.  Скачаем пример. Архив xpcom-sample.zip  содержит такие файлы:
[url]IMyComponent.h
     IMyComponent.idl
     IMyComponent.xpt
     MyComponent.mak
     MyComponent.dsp
     MyComponent.cpp
     MyComponentModule.cpp[/url]
Makefile – меня не интересовал так как с проектом я работала на Visual Studio 6.0
3.  Переименовываем все файлы, например, на MyNewComponent. Чтобы поменять имя проекта, откройте MyNewComponent.dsp в текстовом редакторе и замените все MyComponent на MyNewComponent, то же самое надо сделать в MyNewComponent.mak.
3.  В директорию [gecko-sdk]-[idl] перекопируем IMyNewComponent.idl файл.
4.  Если необходимое меняем содержимое IMyNewComponent.idl файла (перечисляем все функции, которые хотим вызывать из своего actions.js файла):

Выделить код

Код:

#include "nsISupports.idl"
#include "nsIAccessibleDocument.idl"


[scriptable, uuid([свой GUID])]
interface IMyNewComponent : nsISupports
{

  long OnLoadPage();
  long SetBrWindow(in nsIAccessibleDocument window);

};

5.  Пишем два батника или выполняем в командной строке:

Выделить код

Код:

xpidl.exe -m typelib IMyNewComponent.idl

– получим   IMyNewComponent.xpi файл

Выделить код

Код:

xpidl.exe -m header IMyNewComponent.idl

– получим   IMyNewComponent.h файл   

6. Копируем IMyNewComponent.idl и IMyNewComponent.h файлы себе в проект.
7. Добавляем в MyNewComponent.сpp тело функций описанных в IMyNewComponent.h
8. Открываем проект. Вызовем диалоговое окно  Settings. В С/С++ в категории Preprocessor изменяем путь «Additional include directories» к [gecko-sdk].
9. Компилируем проект.

Установка для FF:
Добавляем в схему еще одну директорию [components]
                Myhook.xpi ->> install.rdf
                                      install.js
                                      [chrome]       ->> myhook.jar
                                      [components] ->> MyNewComponent.dll
                                                               IMyNewComponent.xpi

Установка для Mozilla:
1.    Переписываем MyNewComponent.dll и IMyNewComponent.xpi в директорию [components], которая находится в установленной Mozilla.
2.    Выполняем regxpcom.exe
3.    И удаляем xpti.dat и compreg.dat
4.    Запускаем  Mozilla

Теперь, когда компонента зарегистрирована, вызовем ее метод в actions.js:

Выделить код

Код:

function actions() {
	
   this.doLoad = doLoad;

   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
   const cid = "@mydomain.com/XPCOMSample/MyNewComponent;1";
   obj = Components.classes[cid].createInstance();
   obj = obj.QueryInterface(Components.interfaces.IMyNewComponent);

  function doLoad() {
        alert('Load page…');
   }
}

Конечно, можно было бы добавить метод вызова в какую-то функцию. Но тогда компонента будет инициализироваться каждый раз, а мне необходимо было постоянно обращаться к этой dll. 

Теперь будем каждый раз при вызове doLoad передавать

Выделить код

Код:

function doLoad() {
    const accServiceID = '@mozilla.org/accessibilityService;1';
    const accServiceIF = Components.interfaces.nsIAccessibilityService;
    const accService = Components.classes[accServiceID].getService(accServiceIF);
    var   acc = accService.getAccessibleFor(window.content.document);
    var dom_window = acc.QueryInterface(Components.interfaces.nsIAccessibleDocument);

    obj.SetBrWindow(dom_window);
}

Где функция SetBrWindow записывает dom_window(элемент компоненты nsIAccessibleDocument) в глобальную переменную MyNewComponent.dll g_dom_window. Теперь в нашей компоненте хранится информация об окне, а соответственно мы легко можем получить содержимое документа. Выбор компоненты nsIAccessibleDocument связан с тем, что из нее можно получить HWND окна браузера:

Выделить код

Код:

HWND* DocWindow = new HWND;
   rv = g_ dom_window->GetWindowHandle((void**)DocWindow);

Но всегда ли мы имеем доступ к текущему документу? Как узнать Главному приложению, с каким именно окном нужно работать, если открыто запущенно несколько окон Mozilla? И что если в одном окне Mozilla  загружено несколько страничек и пользователь переходит с одной на другую?
Добавим в  myhook.js перехват еще двух событий:

Выделить код

Код:

function formInitialize() {
   try {
       window.addEventListener("load", actions.doLoad,true); 
      // перехватываем события загрузки новой страницы
       window.addEventListener("click", actions.doClick, false);
     //пользователь перешел на другую страничка
       window.addEventListener("focus", actions.doActivate, true);
     //пользователь перешел на другое загруженное приложение FF или Mozilla.
   } catch(e) {
   } 			      
}

И в каждой новой функции выполняются те же действия, как и в doLoad. Теперь осталась только научить компоненту обмениваться информацией с главным приложением.
Содержание MyNewComponent.h файла:

Выделить код

Код:

class MyNewComponent : public IMyNewComponent
{
public:
  NS_DECL_ISUPPORTS
  NS_DECL_IMYNEWCOMPONENT

 MyNewComponent ();
  virtual ~ MyNewComponent();

  bool InitWindow();
  bool InitMemory();

};

Функции InitWindow() и InitMemory() вызываются в конструкторе класса. В InitWindow создается обычное API окно с обработчиком событий:

Выделить код

Код:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

В InitMemory инициализируется глобальная переменная pShared. В выделенной Shared Memory будут храниться данные общие для двух приложений. То есть, когда мы из g_dom_window получим необходимую информацию, то запишем ее в структуру, на которую указывает pShared.

Теперь нам необходимо определять момент, когда Главному приложению требуется получить из странички информацию о содержании полей. Зарегистрируем новое для Window сообщение в компоненте и в Главном приложении.

Выделить код

Код:

UINT WM_SETSHARED_DATA = RegisterWindowMessage("SharedParamsMozilla");

Обработаем такие сообщения в WndProc:

Выделить код

Код:

if (message == WM_SETSHARED_DATA) PostInfo();
            // выполняем функцию заполнения структуры, на которую указывает pShared,  
           //необходимыми данными 
	if (message == WM_CREATE) pShared->hWindowComponent = hWnd;
          // запоминаем в HWND созданного нами окна.

А в Главном приложении  пошлем зарегистрированное сообщение WM_SETSHARED_DATA для окна созданного в компоненте:

Выделить код

Код:

WPARAM wParam = 0;
 LPARAM lParam = 0; 
 SendMessage(pShared->hWindowComponent,WM_SETSHARED_DATA,wParam,lParam);

После этого осталось только использовать данные записанные в структуре.

На последок несколько замечаний:
1.    Конечно, вместо Shared Memory можно использовать WM_COPYDATA.
2.    Отлавливать событие focus надо еще и из-за того, что необходимо записывать в  pShared->hWindowComponent окно из текущей длл.
3.    Чтобы избежать вопросов в отношении того, что в Mozilla есть реализованный интерфейс запоминания содержания полей на страничках (по крайней мере паролей), скажу, что по ряду причин он не устраивал моих заказчиков. 
4.    Осталась проблема с написание Инсталляции – как сделать так, чтобы можно было не требовать у пользователя самостоятельной установки расширений?
5.    Каждый раз при запуске очередного окна Mozilla создается новое окно в функции InitWindow(), которое и отвечает дальше за обмен информацией. Предыдущие созданные окна теряются. С этим вопросом еще предстоит разобраться.

Aerina
Не сильно я в этом разбираюсь, но ваша работа достойна уважения :)
Может в Документацию разместить?

Угу, в Wiki бы это.

lcraFTl пишет

Aerina
Может в Документацию разместить?

Это уже по Вашему усмотрению

Дайте девушке доступ в Wiki, она это заслужила ;)

Дайте девушке доступ в Wiki, она это заслужила wink

Это точно, потому что я не хочу туда всё это вносить, слишком долго. :)

Athathoth пишет

Дайте девушке доступ в Wiki, она это заслужила ;)

Я, вообще-то думал, Unghost сразу присвоил ей нужные права, после того, как "отметился" в этой теме. Вопрос, правда, в другом: захочет ли она писать (сама) в Wiki ? :)

Anton

Я, вообще-то думал, Unghost сразу присвоил ей нужные права, после того, как "отметился" в этой теме.

Гм, я просто размышлял стоит ли давать права новичку. 17 постов у человека.
Сейчас права дал.

Гм, я просто размышлял стоит ли давать права новичку. 17 постов у человека.
Сейчас права дал.

Постараюсь не подвести... подтвердить, что ли, оказанное доверие...

Aerina, Отличная работа!

А где расширения обычно хранят свои настройки? Есть какое-то правило по этому поводу?

KML пишет

А где расширения обычно хранят свои настройки? Есть какое-то правило по этому поводу?

Настройки? какие настройки Вы имеете ввиду?
Если это внутрение настройки расширения - то там где пожелает производитель ("нарисует" диалоговое окно или еще как нибудь...), а так в ФФ все что мы можем сделать с расширением - это добавить его и удалить.

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

Aerina, есть одно замечание и одно предложение:
- файл install.js отсутствует в примере http://www.borngeek.com/firefox/tutorial/, во всяком случае в архиве http://www.borngeek.com/firefox/tutorial/gbltutorial.zip его нет.
- есть предложение добавить файл файл работающего примера, хотя бы того, который при загрузке странички выдает сообщение «Load page…». :)
Спасибо.

HouseF пишет

Aerina, есть одно замечание и одно предложение:
- файл install.js отсутствует в примере http://www.borngeek.com/firefox/tutorial/, во всяком случае в архиве http://www.borngeek.com/firefox/tutorial/gbltutorial.zip его нет.
- есть предложение добавить файл файл работающего примера, хотя бы того, который при загрузке странички выдает сообщение «Load page…». :)
Спасибо.

Да. согласна. Вообще install.js - мне нужен был для установки расширения в Mozilla Suite, а в FF - можно и без него.:) Исправлю это в статье. И когда добавлять текст в "документацию" - не забуду про пример.

Спасибо за замечание.

Aerina

Makefile – меня не интересовал так как с проектом я работала на Visual Studio 6.0

Я попробовал собрать проект при помощи cygwin, но он ругается на потроха из gecko-sdk:

Выделить код

Код:

$ make
c++ -Wall -Os -o -mno-cygwin MyComponent.so -include mozilla-config.h  -DXPCOM_G
LUE -I ./gecko-sdk/include  -L ./gecko-sdk/lib -lxpcomglue -lnspr4 -lplds4
 -fno-rtti -fno-exceptions -shared    MyComponent.cpp MyComponentModule.cpp
c++: MyComponent.so: No such file or directory
In file included from ./gecko-sdk/include/nsISupports.h:122,
                 from IMyComponent.h:10,
                 from MyComponent.h:4,
                 from MyComponent.cpp:1:
./gecko-sdk/include/nsISupportsUtils.h:207: error: an explicit specialization must be preceded by 'template <>'
In file included from ./gecko-sdk/include/nsISupports.h:122,
                 from ./gecko-sdk/include/nsIFactory.h:10,
                 from ./gecko-sdk/include/nsIGenericFactory.h:41,
                 from MyComponentModule.cpp:1:
./gecko-sdk/include/nsISupportsUtils.h:207: error: an explicit specialization must be preceded by 'template <>'
make: *** [build] Error 1

Кому-нибудь это удавалось при помощи cygwin? Мне нужно собирать это бесплатной утилитой.

Aerina

Постараюсь не подвести... подтвердить, что ли, оказанное доверие...

Если не побоишся, и всетаки решишь написать статью, то для этих целей рекомендую плагин WikiCode :). Позволяет через контекстное меню вставлять теги wiki. Это расширение написал старейший участник этого форума, Viper. Обсуждается здесь.

Можно еще с помощью кнопок быстрого форматирования размечать текст:

quickbuttons.png

Для начала стоит поиграться в Песочнице :). Там можно написать чтото в Wiki формате и посмотреть как это будет выглядеть.

По мере возникновения вопросов по синтаксису, всегда можно чтото уточнить в Синтаксисе форматирования.

Ну и конечно можно почитать Правила редактирования WiKi, хотя и не обязательно ;).

Вообще писать чтото в Wiki не сложнее чем отвечать на форумах, это только для начала кажется сложным.


ЗЫ: Форумчане, не сочтите за оффтопик, хотел девушке помочь с Wiki.

Не сильно разбираюсь в разработке плагинов, но мое скромное имхо, за такую работу нужно девушке поставить памятник :). Aerina, ты молодец!

а у KML аватар из игрушки *Рыбалка 2*:lol:

KML, флеймить не нужно...

Ragnaar

e-travel Спасибочки!
Постараюсь, как можно быстрее разобраться с статьей... Уже начала читать Помощь для редактирования Wiki.
  Исправлю ошибки, те что уже знаю и те, что найду. И добавлю примерчик (работающий:-)))

Aerina
Если не напишешь, ничего страшного. Главное не спеши :).

Если писать, то наверно в раздел Инициативы для разработчиков. Можно назвать Разработка расширений для Mozilla. Или так как эта тема называется.

e-travel пишет

Aerina
Если не напишешь, ничего страшного. Главное не спеши :).

Если писать, то наверно в раздел Инициативы для разработчиков. Можно назвать Разработка расширений для Mozilla. Или так как эта тема называется.

Да я тоже думала в этот раздел... но наверное "Разработка расширений для Mozilla"  - не очень подойдет... (так как и компонента тоже не последнюю работу выполняет...) может лучше что-то типа "Как написать приложение взаимодействующее с Mozilla?"... ? но как-то слишком тяжело звучит. или "Разработка расширения и компоненты для обмена информацией с другими  приложениями".
Короче, название наверное самый тяжелый вопрос на настоящий момент. Написать расширение было легче:cool:

Дайте девушке доступ в Wiki, она это заслужила wink

Это точно, потому что я не хочу туда всё это вносить, слишком долго.

Ага, хитрые какие. Cами не хочем писать, а девушку заставляем ;).
Мое скромное имхо, для начала в Wiki такие большие статьи писать лучше не стоит.

Лучше с чегото попроще начать. Поэтому я решил помочь. ВотЪ:

  Статья Aerina в Wiki

Aerina, ты там подправь оформление так, как тебе больше нравится.

e-travel пишет

Дайте девушке доступ в Wiki, она это заслужила wink

Это точно, потому что я не хочу туда всё это вносить, слишком долго.

Ага, хитрые какие. Cами не хочем писать, а девушку заставляем ;).
Мое скромное имхо, для начала в Wiki такие большие статьи писать лучше не стоит.

Лучше с чегото попроще начать. Поэтому я решил помочь. ВотЪ:

  Статья Aerina в Wiki

Aerina, ты там подправь оформление так, как тебе больше нравится.

Ай, спасибо большое!

Вот это настоящая помощь!

e-travel
Вот-вот я уже наконец-то занялась праведным делом!

И у меня возникло несколько вопросов:

А можно как-то добавить не страничку, а Архивчик с исходниками?.. как говорится по прозьбам трудящегося HouseF

И общеобразовательный: А как добавить новую страничку? достаточно написать имя, а потом она сама создаться при нажатии на кнопку "Редактирование"... (могу конечно попробывать в песочнице, но там же одна страничка... - не знаю следует ли еще добавлять)

А... вот думала насчет названия. Хотела поменять на "Разработка приложения для работы с Mozilla", но что-то так стало жалко свое уже привычное. Но это как-то серьезней звучит.. Стоит ли менять?

Aerina
Если устроишь голосование - то я за "привычное" (ну, может быть, c не большим, не принципиальным изменением).

Aerina
Для архивчика с исходниками есть Uploads. Или мне пришли, выложу на ftp.mozilla.ru.

Итак, немного подправила текст и добавила пример расширения, которое выводит сообщение о загрузке страницы.

Название пока решила не менять.

Объясните, пожалуйста, как idl-файл с содержимым:

Выделить код

Код:

#include "nsISupports.idl"
#include "nsIAccessibleDocument.idl"
...

корректно пропустить через xpidl. Я, как описано, скачал Gecko SDK (версия 1.8.*), но nsIAccessibleDocument.idl файла не нашел.
ОЧЕНЬ ПРОШУ ПОМОЩИ!

СУВ, Kuvaldis

Без файла - никак.
Вот nsIAccessibleDocument.idl для 1.8.0: http://mxr.mozilla.org/mozilla1.8.0/source/accessible/public/nsIAccessibleDocument.idl
Но в 1.9 он, кажется, изменился: http://www.oxymoronical.com/experiments/apidocs/compare/platform/1.9.0.0/1.8.1.0

Интерфейс nsIAccessibleDocument @status UNDER_REVIEW, а, насколько я помню, в Gecko SDK только замороженные интерфейсы, чтобы можно было бинарник использовать с любой более поздней версией Mozilla.

Спасибо :)