XUL/XPCOM (JavaScript)

Материал из MediaWiki
Перейти к навигации Перейти к поиску

Принцип написания XPCOM компонента на JavaScript схож с принципом написания оного на C++. Однако, здесь в более явном виде используются понятия модуля и фабрики. Алгоритм действий прост:

  • создать XPIDL описание интерфейса, сгенерировать .xpt библиотеку;
  • создать JavaScript реализацию интерфейса, фабрики, модуля;
  • положить .xpt библиотеку и .js реализацию в каталог components нашего проекта.

Итак, приступим.

Подготовка

Подготовка включает в себя все разделы вплоть до «Генерация заголовка и xpt библиотеки» включительно в статье XUL/XPCOM. Тут ничего не поменялось, кроме разве что GUID.

Пишем JavaScript XPCOM

Создаём файл IMyComponent.js с таким содержимым:


const Ci = Components.interfaces;

// константы, аналогично как в C++
const CLASS_ID    = Components.ID("b058921b-cdc2-43b3-8515-e2b9d6133263");
const CLASS_NAME  = "Simple JavaScript Componennt";
const CONTRACT_ID = "@mydomain.com/XPCOMSample/MyComponent;1";
const INTERFACE   = Ci.IMyComponent;

// Собственно наш компонент
function MyComponent() {}

MyComponent.prototype =
{
    // Метод сложения двух чисел, описанный в .idl файле
    Add: function(a, b)
    {
        return a + b;
    },

    // Мы также должны реализовать QueryInterface, чтобы
    // проверить правильность использования объекта. Это означает, что
    // мы не сможем использовать этот объект как указатель на интерфейс, который в нём
    // не реализован. Методы AddRef() и Release() из nsISupports не реализованы.
    QueryInterface: function(aIID)
    {
        // Мы знаем только о базовом интерфейсе nsISupports и нашем IMyComponent,
        // иначе выбрасываем исключение.
        if(!aIID.equals(INTERFACE) && !aIID.equals(Ci.nsISupports))
            throw Components.results.NS_ERROR_NO_INTERFACE;

        return this;
    }
};

// Наша фабрика, создаёт и возвращает синглетон MyComponent.
// Частично реализует интерфейс nsIFactory.
var MyComponentFactory =
{
    singleton: null,

    // Вызывается из скрипта для создания объекта XPCOM компонента
    createInstance: function(aOuter, aIID)
    {
        if(aOuter != null)
            throw Components.results.NS_ERROR_NO_AGGREGATION;

        // объект уже создан?
        if(this.singleton == null)
            this.singleton = new MyComponent();

        return this.singleton.QueryInterface(aIID);
    }
};

// Модуль, реализует интерфейс nsIModule, позволяет работать с нашей фабрикой
var MyComponentModule =
{
    registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
    {
        aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType);
    },

    unregisterSelf: function(aCompMgr, aLocation, aType)
    {
        aCompMgr = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
        aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
    },

    getClassObject: function(aCompMgr, aCID, aIID)
    {
        // Абсолютно неправильное использование объекта модуля.
        // В принципе, такого не должно возникать.
        if(!aIID.equals(Components.interfaces.nsIFactory))
            throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

        // Запрос нашего интерфейса, которому однозначно сопоставлена
        // фабрика.
        if(aCID.equals(CLASS_ID))
            return MyComponentFactory;

        // Мы не смогли обработать запрос объекта, и ничего не знаем о запрошенном
        // интерфейсе.
        throw Components.results.NS_ERROR_NO_INTERFACE;
    },

    canUnload: function(aCompMgr) { return true; }
};

// Главный загрузчик, должен возвращать объект нашего модуля
function NSGetModule(aCompMgr, aFileSpec) { return MyComponentModule; }

Запуск

Копируем .xpt библиотеку и .js файл в каталог components нашего XUL проекта, и создаём .xul файл абсолютно идентичный таковому из статьи «Пишем XPCOM компонент на C++». Запуск и результат также должны совпадать с результатами из статьи «Пишем XPCOM компонент на C++» — вы должны увидеть окно с текстом 3 + 4 = 7 внутри.

Анализ

Как Вы видите, единственное отличие от предыдущей статьи в том, что XPCOM компонент реализован не на C++, а на JavaScript. Естественно, такие компоненты проще писать и сопровождать.

Ссылки