Pull to refresh

Как написать компонент к Firefox на C++

Reading time5 min
Views3.5K
Как написать на C++ компонент для Firefox, так, чтобы его потом можно было использовать из яваскриптового extension или даже из обычной веб-страницы.

Предыстория


Мне нужно было сделать тулбар для IE и Firefox. Осложнялось всё тем, что использовался некий нестандартный бинарный протокол, реализовала который C-дллька. Использовать её из C# (на котором я писал тулбар для IE) не составило никакой проблемы, однако для FF пришлось писать Wrapper — XPCOM-компонент, к которому я мог бы обратиться из javascript, который передавал бы данные в dll и возвращал бы результат.
XPCOM — это аналог Microsoftовского COM'а от Мозиллы, который от COM отличается практически ничем. Вообще, не очень понятно, почему Mozilla вместо хорошо описанного и привычного COM решили изобрести свой велосипед, но так уж вышло, и нам, разработчикам, придётся принимать всё так, как оно есть.
Open Source — это прекрасно, но до тех пор, пока не столкнёшься с документацией и отсутствием работающих примеров. Примеров XPCOM-компонентов я, кстати, нашёл в интернете аж 4, ни один из которых не заработал. Кроме того, самый свежий из них датировался 2006ым годом, в описаниях значилась старинная версия XULRunner'а, а в обсуждении к примеру были вопросы о совместимости с «новым Firefox 1.3» и несколько десятков вопросов «how to get it work?».

Поехали!


  1. Вначале разберёмся с environment.
    Запускаем в командной строке subst o: d:\myworkfolder\_firefox — в эту папку мы будем складывать все нужные файлы.
    Можно даже записать эту команду в bat-файл.
  2. Скачиваем последний XULRunner (сейчас это 1.9.0.6) — releases.mozilla.org/pub/mozilla.org/xulrunner/releases
    Нам нужен SDK. Скачиваем и распаковываем его в O:\xulrunner-sdk
  3. Создаём O:\console.bat следующего содержания:
    set path=%path%;O:\xulrunner-sdk\bin;O:\xulrunner-sdk\sdk\bin
    cmd.exe

    (на всякий случай, вдруг потребуется что-нибудь сделать руками)
  4. Создаём папку O:\dll-src, и в ней — IDemo.idl
    #include "nsISupports.idl"
    #include "nsrootidl.idl"

    [scriptable, uuid(CB934085-D019-47d5-A6F0-623885873281)]

    interface IDemo : nsISupports
    {
    long func1(in long inP, out long outP);

    long func2(in wstring inP, out wstring outP);
    };


  5. Там же создаём два бат-файла, build-pre.bat и build-post.bat. Второй оставляем пустым, а в первый пишем:
    set path=%path%;O:\xulrunner-sdk\bin;O:\xulrunner-sdk\sdk\bin
    xpidl -I O:\xulrunner-sdk\idl -m header IDemo.idl
    xpidl -I O:\xulrunner-sdk\idl -m typelib IDemo.idl

    По нашему idl-файлы с помощью xpidl будет автоматически генерироваться хедер и xpt-описание интерфейса (аналог майрософтовского .tlb файла)
  6. Создаём новый проект Win32 / Dll library / empty

  7. Запускаем build-pre.bat и переносим получившийся IDemo.h в наш проект.
  8. Создаём в VS файл Demo.h и копируем из IDemo.h наш темплейт. Приводим его в человеческий вид:
    #ifndef _DEMO_H_
    #define _DEMO_H_

    #include "IDemo.h"

    #define DEMO_CONTRACTID "@demo.com/XPCOMDemo/Demo;1"
    #define DEMO_CLASSNAME "XPCOM Demo LOL"
    #define DEMO_CID {0xcb934086, 0xd019, 0x47d5, { 0xa6, 0xf0, 0x62, 0x38, 0x85, 0x87, 0x32, 0x81 }}

    class Demo : public IDemo
    {
    public:
    NS_DECL_ISUPPORTS
    NS_DECL_IDEMO

    Demo();

    virtual ~Demo();
    };

    #endif


  9. Создаём в VS файл Demo.cpp и копируем туда закомментированную часть из IDemo.h — там уже есть шаблон реализации. Меняем название класса.
    #include "Demo.h"
    #include <nsMemory.h>
    #include <nsStringAPI.h>

    NS_IMPL_ISUPPORTS1(Demo, IDemo)

    Demo::Demo() {
    }

    Demo::~Demo() {

    }
    NS_IMETHODIMP Demo::Func1(PRInt32 inP,PRInt32 *outP,PRInt32 *_retval) {
    if (inP>100) {
    *_retval = 1;
    *outP = 0;
    } else {
    *_retval = 0;
    *outP = inP*2;
    }
    return NS_OK;
    }
    NS_IMETHODIMP Demo::Func2(const PRUnichar *inP,PRUnichar **outP,PRInt32 *_retval) {
    const wchar_t *msg = L"привет";
    *outP = (PRUnichar *) nsMemory::Clone(msg, (wcslen(msg)+1)*sizeof(wchar_t));

    *_retval = 0;
    return NS_OK;
    }

  10. Создаём в VS файл DemoModule.cpp:
    Это — наш главный файл и точка входа в наше приложение. Тут мы отдаём ядру XPCOM'а те компоненты, которые реализует наш модуль.
    #include "nsIGenericFactory.h"
    #include "Demo.h"

    NS_GENERIC_FACTORY_CONSTRUCTOR(Demo)

    static nsModuleComponentInfo components[] =

    {
    {
    DEMO_CLASSNAME,
    DEMO_CID,
    DEMO_CONTRACTID,
    DemoConstructor,

    }
    };

    NS_IMPL_NSGETMODULE("DemoModule", components)



  11. Открываем свойства проекта
    C/C++ — General — Additional Include Directories пишем O:\xulrunner-sdk\sdk\include;O:\xulrunner-sdk\include
    C/C++ — Preprocessor — Preprocessor Definitions добавляем ;XPCOM_GLUE
    C/C++ — Advanced — Force Includes пишем mozilla-config.h
    Linker — Additional Library Directories пишем O:\xulrunner-sdk\sdk\lib
    Linker — Input — Additional Dependencies — пишем xpcom.lib nspr4.lib xpcomglue_s.lib
    Build events — Pre-build event — Command Line — пишем там O:\dll-src\build-pre.bat
    Build events — Post-build event — Command Line — пишем там O:\dll-src\build-post.bat
  12. Пришла пора создавать ярлык для файрфокса.
    Делаем его и в свойствах пишем: «C:\Program Files\Mozilla Firefox\firefox.exe» -no-remote -P dev
    Запускаем. Открылся Profile Manager — создаём профиль dev и указываем вручную папку для него: C:\Documents and settings\test\Application Data\Mozilla\Firefox\Profiles\dev1 (создаём через кнопку «создать»).
    Файрфокс запускается, и когда запустится, мы его закрываем
  13. Теперь редактируем build-post.bat.
    set path=%path%;O:\xulrunner-sdk\bin;O:\xulrunner-sdk\sdk\bin

    del /f "C:\Documents and Settings\test\Application Data\Mozilla\Firefox\Profiles\dev\xpti.dat"
    del /f "C:\Documents and Settings\test\Application Data\Mozilla\Firefox\Profiles\dev\compreg.dat"

    copy /Y IDemo.xpt "C:\Program Files\Mozilla Firefox\components\"
    copy /Y debug\Demo.dll "C:\Program Files\Mozilla Firefox\components\"

  14. Билдим проект!
  15. Создаём файлик O:\demo.html
    (код приведён частично)
    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
    obj = Components.classes["@demo.com/XPCOMDemo/Demo;1"].createInstance(); //помните это имя? оно есть в Demo.h

    myobject = obj.QueryInterface(Components.interfaces.IDemo);
    var x = {};

    var res = myobject.func1(10,x);
    alert('func1(10,x) returned ' + res + '. x is '+x.value);

    res = myobject.func2("asd",x);
    alert('func2("ads",x) returned ' + res + '. x is '+x.value);

  16. Запускаем файрфокс через наш ярлык и открываем file://o:\demo.html
  17. Жмём на кнопку, соглашаемся с вопросом про UNSAFE, смотрим результаты.
    Вопрос про UNSAFE возникает на строчке с «PrivilegeManager.enablePrivilege» — эта строчка просит позволения работать с XPCOM-компонентами и, конечно, в веб-страницах содержаться не должна.
    Если же мы делаем Firefox Extension — какую-нибудь панельку инструментов — то нам работать с XPCOM уже позволено. Поэтому в коде расширения этой строчки быть не должно, никакого предупреждения не возникает и всё работает молча.
  18. Ура! Работает!


PS1. Все файлы проекта можно скачать по этой ссылке (82 кб)
PS2. У нас есть «интерфейс» (или несколько) и есть «компонент», который этот интерфейс реализует. Каждый из них имеет свой UID. Для генерации UID нам понадобится Guidgen из VS, обычно она лежит в «C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\guidgen.exe». Чтобы сгенерировать другие UID'ы, отличные от тех, которые приведены в моём примере, нужно запустить guidgen, выбирать там третий вариант, скопировать его и руками вставить в IDemo.idl. Потом сгенерировать ещё ID и скопировать в уже в Demo.h. Это нужно будет делать, если вы не будете останавливаться на Demo-компоненте и будете делать свой компонент.
PS3. idl-файлы для XPCOM отличаются от idl-файлов для COM, поэтому microsoft'овские утилиты tlbimp и midl не понимают эти idl-файлы. Если вы решите добавить IDemo.idl в проект, не забудьте установить в его свойствах «не компилировать».
Tags:
Hubs:
Total votes 36: ↑35 and ↓1+34
Comments17

Articles