В состав платформы Mozilla входит объектная библиотека, содержащая более тысячи объектов, которые могут использоваться разработчиком при создании собственных приложений. Многие из этих объектов не имеют никакого отношения к графическому интерфейсу пользователя. Задача этой лекции – рассказать, для каких целей разработчик может использовать различные группы объектов платформы.
Тысяча примеров скриптов – слишком много для одной лекции. Здесь мы
можем лишь познакомиться с основными типами объектов, а также
предложить читателю некоторые рекомендации и ориентиры для дальнейшего
освоения библиотеки. Если вы займетесь разработкой на платформе
Mozilla, в дополнение к материалу этой лекции вам придется читать
определения
Объектная библиотека Mozilla состоит, главным образом, из
компонентов XPCOM. Без этих компонентов возможности разработчика
приложений были бы ограничены работой с документами XML, будь то HTML
или XUL. Механизмы взаимодействия приложений с внешним миром
сводились бы к таким технологиям, как URL, HTTP,
Набор компонентов Mozilla сравним с любой современной объектно-
ориентированной библиотекой или стандартной библиотекой языка третьего
поколения. Подобно языкам C++ и Java, платформа Mozilla использует
концепцию
Это "почти" связано с тем, что Mozilla пока достигла лишь версий 1.x. С относительной новизной платформы связана некоторая ограниченность круга объектов, доступных разработчику. Вместо того чтобы предложить широкий диапазон объектов низкого уровня, Mozilla содержит некоторое количество объектов низкого и среднего уровней, а также ряд объектов очень высокого уровня, рассчитанных на приложения определенного типа. Поскольку первоначально платформа была разработана как средство создания браузера и других приложений для работы с Internet, специализированные объекты, созданные для решения этой задачи, присутствуют в библиотеке на всех уровнях абстракции. В отличие, например, от библиотеки классов Java, набор компонентов Mozilla не был с самого начала спроектирован, как мощная универсальная библиотека. Тем не менее, тысяча компонентов – весьма мощный ресурс, который по объему приближается к обширной библиотеке модулей Perl.
Еще одна нетипичная особенность библиотеки Mozilla состоит в том,
что многие ее объекты рассчитаны на работу с сетью. Первоначальные
приложения Mozilla – навигатор,
Однако даже если разрабатываемое приложение не является браузером,
Как показано на схеме в начале лекции, компоненты XPCOM являются основой прикладной части Mozilla. Технологии XPCOM и XPConnect используют различные вспомогательные файлы, прежде всего реестр (простую базу данных, сходную с системным реестром Microsoft Windows) и библиотеки типов (описания компонентов). Настройки Mozilla и информация о сертификатах также хранятся отдельно от компонентов. С точки зрения разработчика, наиболее интересной частью архитектуры XPCOM являются отдельно хранимые файлы XPIDL, которые содержат описания всех интерфейсов XPCOM в форме, удобном для чтения.
Эта лекция начинается с изложения нескольких концепций, особенно важных для понимания программного окружения, в котором взаимодействуют компоненты XPCOM. Затем мы переходим к обсуждению типичных задач программирования и решений, предлагаемых платформой Mozilla. Сначала рассматриваются более общие задачи, а затем специфичные для отдельных групп приложений. Затем обсуждается платформа в целом и ее система защиты. Практический раздел лекции содержит множество примеров, демонстрирующих использование объектов для работы с JavaScript.
Обширная библиотека компонентов XPCOM, входящая в состав платформы Mozilla, – особый мир, где действуют свои законы и правила. Чтобы эффективно использовать эту библиотеку, полезно познакомиться со сложившейся терминологией и принятыми соглашениями в этой области.
Фактически платформа Mozilla образована множеством различных программ, написанных на разных языках, и разработчику часто приходится обращаться к исходным текстам, чтобы изучить те или иные возможности платформы. В большей части исходного кода Mozilla и документации используется определенный стиль кодирования, к которому надо привыкнуть. Помимо документации, разработчик может обратиться к следующим источникам:
Из перечисленных источников основным являются определения XPIDL – это необходимый минимум для "выживания" на платформе Mozilla. URL этих файлов можно найти во введении к книге.
Стиль кодирования для платформы Mozilla подразумевает использование
определенных соглашений об именовании. Это особенно важно для кода на
JavaScript, поскольку этот язык обладает лишь слабыми механизмами
Ниже приведены некоторые примеры соглашений об именовании, применяемых в исходном коде Mozilla.
Для определения характера интерфейсов XPCOM используются префиксы.
Наиболее распространенным является префикс nsI (от " n et s cape I nterface"), который применяется для обозначения интерфейсов,
предназначенных для использования разработчиками приложений.
Существует также ряд более специфичных префиксов, указывающих на связь
интерфейса с определенным приложением или технологией, например imgI, inI, jsdI и mozI (от image, inspector, JavaScript ns (без I) не предназначены для
использования разработчиками приложений. Как правило, эти объекты
применяются в системном коде платформы.
Для атрибутов и методов интерфейсов используются определенные соглашения об использовании прописных букв.
ALL_UPPERCASE_STYLE ).initCapStyle ).initCapStyle ).InitCapStyle ).В любом случае, прописные и строчные буквы используются одинаковым
образом в XPIDL и в JavaScript. В системном коде платформы (C/C++)
имена методов транслируются из initCap в InitCap. Имена интерфейсов
иногда используются в той же форме, что и в XPIDL, а иногда
записываются только прописными буквами ( ALL_CAPS ).
В коде Mozilla часто используются однобуквенные префиксы для имен переменных – атрибутов интерфейсов и аргументов методов. Эта нотация широко используется в связках XBL, системном коде на C/C++, интерфейсах XPIDL и прикладном коде на JavaScript. В последнем случае эти префиксы могут использоваться несколько бессистемно. Основные однобуквенные префиксы, используемые в коде Mozilla, представлены в таблице 16.1.
| Префикс | Частота использования | Значение |
|---|---|---|
a |
Регулярно | aVar – временная переменная либо аргумент функции или метода. Как правило, она используется для хранения значения или объекта, подлежащих обработке. Пример – aFile |
e |
В некоторых случаях | eVar – значение, чаще всего константа, которое используется в качестве одного из элементов eTuesday (элемент для вторника в |
g |
В некоторых случаях | gVar – глобальная переменная; глобальная в контексте текущего окна (JavaScript) или полностью глобальная (C/C++). Пример – gMenuControllers |
k |
В некоторых случаях | kVar – значение ключа (key), одно из фиксированного набора значений, которые может принимать некоторая переменная. Этот префикс сходен с префиксом e, однако в данном случае значения переменной часто являются побитовыми масками или строками, а не последовательными целыми, начиная с единицы. Пример – kMimeType |
m |
Регулярно | – член (свойство или mLength. |
n |
В некоторых случаях | nVar – как правило, содержит сумму или итог каких-либо вычислений. Это обычная переменная |
s,i,b,f,r,p |
Редко | Эти префиксы используются в системном коде платформы (C/C++), где они означают строку, целое, булево значение (Boolean), f может означать, например, файл или папку (folder) |
Еще один используемый префикс – PR, что означает
Платформа Mozilla использует разнообразные подходы к разделению программ на части или фрагменты. Практически любой термин для "части" или "фрагмента", используемый в разработке ПО, применяется в терминологии платформы Mozilla. Ниже приведены эти термины и их корректное использование в контексте Mozilla:
Связка (
Класс. Единственные классы в Mozilla – классы компонентов XPCOM. На
основе каждого класса может быть создано ноль или более объектов.
JavaScript 2.0 (
Компонент представляет собой сущность, имеющую уникальный
идентификатор в системе XPCOM. Компонентом может быть класс с CID
(идентификатором компонента) и соответствующим ContractID (вида @mozilla.org/test;1 ) или интерфейс с (идентификатором интерфейса).
Иногда настоящими компонентами считают только классы.
Интерфейс – набор точек доступа к объекту. Интерфейсы XPCOM являются единственным примером интерфейсов в Mozilla. О каждом объекте, который предоставляет точки доступа, соответствующие описанию интерфейса, говорят, что он реализует этот интерфейс. Каждый объект XPCOM и связка XBL реализует ноль или более интерфейсов XPCOM. Объекты JavaScript также могут реализовывать интерфейсы XPCOM.
Библиотека. В состав платформы Mozilla входит несколько динамически подключаемых библиотек, но вряд ли они представляют особенный интерес для разработчика приложений. Иногда библиотеками называют скрипты или группы скриптов на JavaScript, которые могут предоставлять полезную функциональность другим программам. Библиотеки типов представляют собой файлы данных, определяющие интерфейсы XPCOM. Они создаются в момент компиляции платформы и автоматически используются механизмом XPConnect при обращении к интерфейсам.
Модуль. Компоненты XPCOM, входящие в состав платформы, сгруппированы в модули, но этот факт значим только для разработчиков самой платформы. Модули не имеют практического смысла для разработчика приложения, если только он не создает новый модуль XPCOM.
Объект. Mozilla содержит объекты XPCOM и объекты JavaScript. Объект
XPCOM является экземпляром определенного класса XPCOM и реализует один
или несколько
Пакет – группа взаимосвязанных файлов, установленная в
Прототип – объект JavaScript, используемый в качестве основы для создания нового объекта JavaScript.
Система XPCOM обеспечивает доступ скриптов JavaScript к другим
программным окружениям, внешним по отношению к этим скриптам. Эти
внешние окружения имеют собственные
Из скриптов JavaScript доступны пять внешних систем типов:
Базовые типы платформы, реализованные в составе NSPR. Это – переносимые типы C/C++, лежащие в основе платформы Mozilla.
Типы данных RDF. Типы в документах
Типы данных схемы XML (XML schema). Mozilla способна выполнять
XML RPC XDR. Mozilla поддерживает сетевой протокол RPC-через-XML,
включая типы данных
Java.
Из пяти перечисленных систем
nsISupportsPrimitive, например nsISupportsPRInt32.nsIRDFLiteral, nsIRDFDate и nsIRDFInt, основанные на nsIRDFNode.nsISchemaSimpleType и nsISchemaBuiltinType.nsIXmlRpcClient содержит метод-фабрику, который порождает необходимые объекты типов NSPR.В дополнение к этим внешним
В этом разделе описано решение общих задач программирования на платформе Mozilla.
Платформа Mozilla, запущенная из командной строки, запоминает аргументы, переданные исполняемому файлу. В ОС Microsoft Windows платформа не запоминает аргументы командной строки, указанные при запуске последующих приложений, которые используют тот же экземпляр платформы.
Используйте эти компонент и интерфейс, чтобы получить доступ к
@mozilla.org/appShell/commandLineService;1 nsICmdLineService
Интерфейс nsICmdLineService поддерживает свойство argc, содержащее
количество аргументов, но не argv, которое могло бы содержать строки,
образующие аргументы. Свойство argc содержит количество пар
аргумент-значение, а не количество строк, разделенных пробелами (традиционная
практика и в UNIX, и в Windows). Поскольку свойство argv или
аналогичное ему не поддерживается, вам придется угадывать имена
параметров, используя метод getCmdLineValue() для получения их
значений. Типичный вызов этого метода выглядит следующим образом:
var url = cls.getCmdLineValue("-chrome");
Метод возвращает значение аргумента, а если аргумент с указанным
именем не был использован при запуске, возвращается значение null.
Этот интерфейс также содержит метод-фабрику getHandlerForParam(),
который возвращает объект, имеющий интерфейс nsICmdLineHandler. Такой
объект представляет собой структуру данных, доступную только для
чтения, и содержащую конфигурационную информацию для обработчика
командной строки, например значения по умолчанию. Каждый существующий
обработчик добавляет новые аргументы командной строки, доступные
платформе. При необходимости такие обработчики могут создаваться при
помощи JavaScript.
Средствами JavaScript невозможно получить исходную копию командной строки.
Язык JavaScript предоставляет простые массивы и объекты, которых
достаточно для большинства несложных задач. За пределами JavaScript
платформа Mozilla поддерживает обширную
Помимо
Объекты-коллекции XPCOM, которые могут применяться сами по себе, перечислены в таблице 16.2. Хотя во многих ситуациях их использование не рекомендуется, они заслуживают упоминания.
| Интерфейс | Реализован в | Описание |
|---|---|---|
nsIArray |
@mozilla.org/array;1 |
Доступный только для чтения массив JavaScript, реализованный средствами XPCOM |
nsIMutableArray |
@mozilla.org/array;1 |
Добавляет методы для модификации содержимого nsIArray |
nsICollection |
@mozilla.org/supports-array;1 |
Добавляет простой интерфейс коллекции к |
nsIDictionary |
@mozilla.org/ |
Простая коллекция, состоящая из пар ключ-значение (отображение, ассоциативный массив), реализованная на JavaScript; может быть полезна |
nsIProperties |
@mozilla.org/properties;1 |
Простая коллекция, состоящая из пар ключ-значение, реализованная на C/C++ |
Сами по себе эти коллекции не слишком полезны, однако для работы с
ними существуют специальные интерфейсы – курсоры или
nsIEnumerator nsIBidirectionalEnumerator
Перечислитель – курсор для перебора данной коллекции, допускающий
лишь nsISupports.
Более сложная разновидность курсора называется nsI{некая_строка} и
редко бывают нужны сами по себе. Однако их можно использовать в
качестве образцов при проектировании сложных структур данных и
способов доступа к ним. Иногда
Стандарт nsIDOMNodeIterator. Он может быть полезен при
работе со структурами данных
Платформа Mozilla содержит не слишком много реализаций алгоритмов
общего характера. В JavaScript доступны регулярные выражения и метод
для Array.sort(). Возможности сортировки,
применяемые в шаблонах XUL, недоступны за их пределами.
Платформа Mozilla в определенной степени поддерживает работу с
базами данных, но эта поддержка развивается медленно. При сборке
интегрированного пакета Mozilla с параметрами по умолчанию доступна
лишь минимальная поддержка работы с базами данных; получение
дополнительных возможностей требует дополнительных усилий. Базы
данных, поддерживаемые платформой, можно условно разделить на пять
групп: неструктурированные файлы,
В таблице 16.3. перечислены поддерживаемые Mozilla базы данных, основанные на неструктурированных файлах.
Две последние строки таблицы требуют пояснений.
| Формат файла | Поддержка в приложениях | Рассматривается в разделе |
|---|---|---|
| Произвольные файлы | Чтение/запись | "Файлы и папки", "Передача данных" |
| Документы |
Только чтение | Лекция 3 "Статическое содержимое" |
| Файлы свойств | Только чтение | Лекция 5 "Скрипты" – пример с интерфейсом nsIStringBundle |
| Настройки | Чтение/отложенная запись | "Настройки" |
| Документы XML | Чтение или запись | "Web-скрипты" |
| Документы |
Чтение/синхронизация кэша с файлом | "Источники данных" |
| Реестр Mozilla | Чтение/запись | Лекция 17 "Развертывание" |
| Недоступна | См. в тексте | |
| Недоступна | См. в тексте |
Что касается реляционных СУБД, то в версии Mozilla, собираемой по умолчанию, их поддержка отсутствует. Однако она может быть добавлена. В таблице 16.4 представлена ситуация на момент выхода Mozilla 1.4 и прогноз на ближайшее будущее.
Наконец, платформа поддерживает ряд форматов файлов, разработанных
для конкретных приложений. Работа со всеми этими файлами
осуществляется опосредованно, с помощью интерфейсов высокого уровня.
Все эти файлы находятся в каталоге
Файлы закладок и cookies полностью переписываются при каждом
изменении. Файл
Mork, упоминаемая в таблице, представляет собой простую технологию
хранения данных в неструктурированном файле, основанную на
| СУБД/технология | Способ установки | Платформы | Примечания |
|---|---|---|---|
| Загружаемый пакет XPInstall | Кросс-платформенный | См. mysqlxpcom.mozdev.org | |
| Перекомпилировать Mozilla 1.5+ | Linux/UNIX | См. http://www.mozilla.org/projects/sql | |
| PostgreSQL | Перекомпилировать Mozilla 1.3+ | Linux/UNIX | См. http://www.mozilla.org/projects/sql |
| Protozilla | Загружаемый пакет XPInstall | Кросс-платформенный | Обеспечивает возможность добавлять к Mozilla поддержку сетевых протоколов, включая взаимодействие с базами данных. См. protozilla.mozdev.org |
| Web-интерфейс | Сделать доступным Web-сервер с базой данных | Кросс-платформенный | Стандартный метод доступа к базам данных – использовать запросы GET и POST протокола HTTP для работы с Web-сервером, взаимодействующим с сервером СУБД |
| Формат файла | Примечания |
|---|---|
| Файл cookies | |
| Файл закладок | |
| Использует Mork | |
| Файл информации о сообщениях конференций ( |
Использует Mork |
| Файл информации о подписке на конференции | Стандартный формат файла информации о конференциях |
| Файл информации о сообщениях электронной почты | Использует Mork |
| Файл почтового ящика (папки) электронной почты | Стандартный формат почтового ящика утилиты mail(1) UNIX |
| Mork | См. обсуждение в тексте |
Mork/in-memory-.
Еще один вид технологий, применяемых в Mozilla и сходных с базами
данных, – кэши. В частности, это кэш Web-документов, в котором
хранятся локальные копии документов, расположенных на удаленных
серверах, а также кэш быстрой загрузки XUL, в котором хранятся файлы
Значения переменных окружения текущего процесса могут быть получены по одному значению за запрос при помощи следующей пары из компонента и интерфейса:
@mozilla.org/process/util;1 interface nsIProcess
Интерфейс nsIProcess предоставляет метод getEnvironment(), который
возвращает значение переменной, имя которой было передано в виде
параметра. Передаваемые имена переменных конвертируются из
Тип операционной системы или версия Mozilla могут быть получены без
обращения к переменным окружения. Для этого достаточно
проанализировать значение свойства window.navigator.userAgent
property.
Для версий Mozilla, собранных с поддержкой отладки, переменная MOZILLA_FIVE_HOME должна содержать путь к каталогу, в котором
установлены
Не существует переменной окружения, которая указывала бы путь к
текущему или любому другому каталогу
Целый ряд переменных, имеющих отношение к отладке, устанавливается
при использовании версии Mozilla, собранной с поддержкой отладки (с
ключом --debug-enabled ). Их интерпретация весьма сложна, наиболее
надежным источником информации по этому вопросу является исходный код
Mozilla.
В этом разделе рассказано, каким образом можно находить файлы и
папки на том компьютере, где установлена платформа Mozilla. Здесь мы
используем термин папка, а не каталог, поскольку последний в контексте
данной лекции относится, прежде всего, к
Работа с файлами в Mozilla довольно сложна в силу ограничений, связанных с требованиями переносимости и соответствия стандартам WWW. Код, работающий с объектами XPCOM, представляющими файлы, должен быть переносим между платформами (как минимум, между UNIX, Microsoft Windows и Macintosh), а понятие файла или папки должно быть совместимо с понятием URL.
Требования переносимости влияют на работу с именами файлов и папок.
Платформа Mozilla не поддерживает концепцию
Ключевой абстракцией, предназначенной для решения этой проблемы,
является интерфейс nsIFile. Объекты, поддерживающие этот интерфейс,
часто используются в скриптах, но разработчики редко создают их
вручную или извлекают информацию о пути и имени файла, хранящуюся
внутри такого объекта. Это означает, что такие объекты редко создаются
при помощи стандартной пары XPCOM:
@mozilla.org/file/local;1 nsIFile
Вместо этого объекты с интерфейсом nsIFile создаются непрямым
образом, при помощи методов других интерфейсов. Для локальных файлов
существует специализированный вариант интерфейса nsIFile, которому
соответствует следующая пара:
@mozilla.org/file/local;1 nsILocalFile
Оба интерфейса могут представлять как файлы, так и папки. В существующем коде можно встретить и устаревший интерфейс для работы с файлами, который в настоящее время не рекомендован к использованию:
@mozilla.org/filespec;1 nsIFileSpec
Таким образом, разработчик приложения полагается на то, что объект,
представляющий файл, будет создан, инициализирован и возвращен методом
другого интерфейса. Существует целый ряд способов получения объектов nsIFile, представляющих нужные файлы:
parent интерфейса nsIFile. Для получения содержимого папки может использоваться свойство directoryEntries того же интерфейса.nsIFile при помощи этой строки.nsIFile был использован при создании потока, канала или другого объекта XPCOM, как правило, его можно получить в дальнейшем, используя методы этого другого объекта.Примеры использования таких методов приведены ниже в этом разделе.
Интерфейс nsIFile позволяет решить проблему переносимости операций
с файлами, однако остается вопрос интеграции файлов и URL. Для работы
с URL используются объекты с интерфейсом nsIURL, использование которых
описано в разделе "Web-скрипты". Для взаимного
преобразования файлов и URL используется следующая пара XPCOM:
@mozilla.org/network/protocol;1?name=file nsIFileProtocolHandler
Этот интерфейс поддерживает методы newFileURI() и getFileFromURLSpec(), которые и выполняют необходимые преобразования.
Интерфейс nsIIOService также поддерживает метод newFileURI().
Интерфейсы nsIFile и nsIURL позволяют получать строки,
представляющие фрагменты пути к файлу или URL. Выполнив определенные
операции над этими строками, приложение может создать объект nsIURL,
соответствующий объекту nsIFile, и наоборот.
Каталог файловой системы подробно описан в разделе "Конфигурация платформы", там же приведен и ряд примеров. Здесь мы приведем короткий пример, демонстрирующий поиск папки, используемой для создания временных файлов (см. листинг 16.1).
var Cc = Components.classes;
var Ci = Components.interfaces;
var dp = Cc["@mozilla.org/file/directory_service;1"];
dp = dp.createInstance(Ci.nsIDirectoryServiceProvider);
var folder = dp.getFile("TmpD", {});
Центральным элементом данного кода является использование
специального псевдонима TmpD для папки временных файлов. Такой подход
пригоден для поиска файлов и каталогов, которые имеют определенное
значение для платформы Mozilla, и для которых определены псевдонимы.
Таблицы существующих псевдонимов приведены ниже, в разделе
"Каталог файловой системы".
Создание диалогового окна для выбора файла пользователем с последующим созданием объекта nsILocalFile на основе выбранного файла показано в листинге 16.2.
var file;
var CcFP = Components.classes["@mozilla.org/filepicker;1"];
var CiFP = Components.interfaces.nsIFilePicker;
var fp = CcFP.createInstance(CiFP);
// используйте любые допустимые параметры для
инициализации объекта nsIFilePicker
fp.init(window, "File to Read", Picker.modeOpen);
if ( fp.show() != fp.returnCancel )
file = fp.file;
Как видно из приведенного кода, объект nsIFilePicker создает объект nsILocalFile, который можно получить, используя свойство file.
Если полученный объект соответствует папке, его можно преобразовать
к другой папке или файлу при помощи строки, представляющей
appendRelativePath(), который принимает в качестве аргумента
Приняв определенные меры предосторожности, можно построить сроку
nsIFile поддерживает ряд атрибутов и методов,
которые могут быть полезны при формировании переносимого пути.
Наконец, если приложение не должно быть переносимым или допускает
использование отдельных фрагментов кода для каждой из поддерживаемых
платформ, можно инициализировать nsILocalFile непосредственно при
помощи строки, представляющей путь, использовав для этого метод initWinPath(). При этом следует экранировать символы обратного слеша в
путях Microsoft Windows (\\) или использовать вместо них прямой слеш.
Отдельный код для различных платформ может иметь вид ряда операторов if, проверяющих текущую платформу.
Как уже было сказано, если приложение не должно быть переносимым
или допускает использование отдельных фрагментов кода для каждой из
поддерживаемых платформ, объект nsILocalFile может быть
инициализирован непосредственно при помощи строки. Пример
соответствующего кода приведен в листинге 16.3.
var file;
var CcLF = Components.classes["@mozilla.org/local/file;1"];
var CiLF = Components.interfaces.nsILocalFile;
var file = CcLF.createInstance(CiLF);
file.initWithPath("C:\\WINDOWS\NOTEPAD.EXE");
Литерал "C:" можно заменить переносимым объектом,
представляющим корневую папку файловой системы, которую можно получить
при помощи каталога файловой системы и псевдонима DrvD. Использование
в качестве разделителя прямого слеша (который поддерживают все
платформы, включая Microsoft Windows) также сделает этот фрагмент
более переносимым.
Локальный файл может быть также задан с помощью URL. Преобразовать URL в файловый объект можно следующим образом:
var conv = Cc["@mozilla.org/network/protocol;1?name=file"]; conv = conv.createInstance(Ci.nsIFileProtocolHandler); var url = ... // Существующий объект nsIURL var file = conv.getFileFromURLSpec(url);
URL, используемый в этом примере, должен иметь префикс file:. Путь
к файлу можно также получить, используя свойство filePath объекта nsIURL, например:
file.initWithPath(myURL.filePath.replace(/\|/,":"));
В данном случае объект myURL поддерживает интерфейс nsIURL. Замена
регулярного выражения при помощи метода replace() приводит фрагмент
URL вида "C|/test" к "C:/test". Нужно иметь в
виду, что сетевые пути в системе Microsoft Windows (пути
После того как файл найден и представлен соответствующим объектом, его читают, в него записывают данные или выполняют с ним другие действия.
В программном окружении JavaScript платформы Mozilla не
используются дескрипторы или идентификаторы (nsIPipe создает канал уровня приложения, который не является
традиционным каналом UNIX. Из скриптов невозможно создавать
именованные каналы (или
Вместо идентификаторов файлов Mozilla использует объекты. При этом
скрипту приходится работать, как минимум, с двумя объектами. Один из
них представляет используемый файл или папку – это может быть объект nsIFile или nsILocalFile. Этот объект –
Следующая пара XPCOM, основанная на тех же принципах, что и
nsIFile, обеспечивает доступ к содержимому локальных файлов .zip и
.
@mozilla.org/libjar/zip-reader;1 nsIZipReader
С помощью этого интерфейса могут также создаваться новые архивы Zip.
Конверторы потоков, описанные в разделе "Преобразование содержимого потоков", могут использоваться для работы с потоком сжатого содержимого в необработанном виде.
Не существует способа отправлять или перехватывать сигналы операционной системы из скриптов JavaScript. Чтобы компонент XPCOM мог перехватывать сигналы, он должен быть написан на Java или C/C++.
Интерфейс nsIThread может использоваться для управления выполнением
фрагмента кода, которое может быть прервано. При этом код, выполнение
которого должно быть прервано, не может быть написан на JavaScript.
Интерпретатор JavaScript платформы Mozilla выполняется в одном потоке
вычислений (thread), и не может прервать собственное выполнение. Из
этого следует, что прерывания, основанные на потоках вычислений,
неприменимы в приложениях, написанных исключительно на JavaScript.
В качестве замены прерываний могут использоваться технологии,
ориентированные на события (см. лекцию 6 "События"), и
Mozilla поддерживает ряд хорошо известных прикладных сетевых протоколов, например FTP. При этом Mozilla предполагает, что протоколом транспортного уровня является TCP/IP. Другие транспортные протоколы, например RS232, X.25 или TP4, могут использоваться, только если они "упакованы" в TCP/IP. Mozilla поддерживает следующие протоколы низкого уровня:
--enable-ipv6 .Как правило, приложения на платформе Mozilla не работают
непосредственно с сетевыми протоколами. Сетевые ресурсы
идентифицируются при помощи URL, и префикс метода доступа (например, http:), входящий в состав URL, определяет необходимый протокол. Как
правило, объект сетевого канала принимает URL, после чего поддержка
нужного протокола задействуется платформой автоматически, и с точки
зрения приложения все "просто работает". Тем не менее,
конкретные протоколы доступны в виде объектов, которые могут быть
созданы при помощи следующей пары XPCOM:
@mozilla.org/network/protocol;1?name={x} nsIProtocolHandler
В приведенном имени компонента { x } должно быть заменено на
идентификатор конкретного протокола, например ftp или http. Все
протоколы (точнее, window.Components.classes. Каждый из них
представлен отдельным компонентом.
С помощью настроек можно сконфигурировать Mozilla на уровне портов
IP, активизируя (открывая) или отключая (закрывая) конкретные порты.
Открытие порта имеет практический смысл лишь в том случае, когда
соответствующий порт открыт на уровне операционной системы. Открытие
дополнительных портов снижает уровень защищенности системы на уровне
приложений, и может быть рекомендовано лишь при использовании сетевого
экрана (файрволла). Получить доступ к полному набору сетевых настроек
Mozilla можно, введя в строке адреса браузера about:config. Имена
параметров, имеющих отношение к сети, начинаются с префикса network.
Разработчики приложений также имеют доступ к сокетам. Операционные
системы представляют соединение TCP/IP при помощи сокета, имеющего
дескриптор, аналогичный
Наконец, существует проект Protozilla, информация о котором
доступна на сайте http://www.mozdev.org, и который позволяет расширять
поддержку сетевых протоколов в Mozilla. С помощью расширения,
разработанного в рамках этого проекта, можно добавлять к Mozilla
поддержку новых протоколов, причем для этого достаточно
программирования только на JavaScript. Требования к этим протоколам
следующие: они должны быть реализованы поверх сокетов TCP/IP, терпимы
к небольшим задержкам, соответствующий код должен реализовывать
интерфейс nsIProtocolHandler и быть зарегистрирован как полноценный
компонент XPCOM.
Теперь мы переходим к обсуждению конкретных задач, возникающих при работе с сетью на низком уровне. Работа с сетью на уровне приложений описана в разделах "Передача данных" и "Web-скрипты".
Чтобы определить IP-адрес по заданному доменному имени, используйте следующую пару XPCOM:
@mozilla.org/network/dns-service;1 interface nsIDNSService
Объект, созданный таким образом, возвращает IP-адрес для заданного
имени домена или текущего узла в форме строки вида
"192.168.1.10". Разрешение доменных имен – медленная
операция. Xтобы работа приложения не приостанавливалась до завершения,
следует использовать метод , которому должен быть передан
слушатель с интерфейсом nsIDNSListener. В этом случае запрос будет
выполняться асинхронно. Реализуйте объект-слушатель на чистом
JavaScript.
Создание соединения с использованием сокета включает несколько этапов.
Для работы с сокетом вам, в конечном счете, понадобится создать
объект nsITransport. Получив этот объект, можно до некоторой степени
забыть, что вы работаете с сокетом, и использовать методы более
высокого уровня, описанные в разделе "Передача данных". В
целом, техника работы с сокетом, доступная разработчику приложений на
платформе Mozilla, отличается довольно высоким уровнем абстракции.
Например, ему недоступен API ioctl(2) для настройки параметров
сокета.
Создавая объект nsITransport, необходимо предусмотреть возможность
того, что между платформой Mozilla и удаленным компьютером, с которым
устанавливается соединение, находится nsIProxyInfo для адреса удаленного
компьютера. Объект nsIProxyInfo может быть создан при помощи методов newProxyInfo() или examineForProxy() следующей пары XPCOM:
@mozilla.org/network/protocol-proxy-service; nsIProtocolProxyService
Затем, используя полученный объект nsIProxyInfo или null, если вы
уверены в том, что nsITransport. Объект-фабрика создается
при помощи следующей пары XPCOM:
@mozilla.org/network/socket-transport-service;1 nsISocketTransportService
Затем нужно создать объект nsITransport, передав объект nsIProxyInfo методу createTransport() объекта-фабрики. Полученный
объект будет поддерживать интерфейс nsISocketTransport, представляющий
простой сокет TCP/IP. Если необходимо создать сокет , следует
использовать метод createTransportOfType() и указать в качестве типа " для протокола "socks4" для
протокола
Сокеты
В состав платформы Mozilla входят и другие интерфейсы для работы с сокетами, однако все они недоступны из JavaScript. Просматривая определения интерфейсов в файлах XPIDL, обращайте внимание на пометку [noscript] перед именем интерфейса. Она означает, что интерфейс недоступен из JavaScript.
В листинге 16.4 приведена
простая программа на Perl, которая может использоваться в качестве
сервера для тестирования соединений, установленных через сокет. Эта
программа принимает данные от всех клиентов, подключившихся к ней, и
направляет их в stdout. Программа не
поддерживает протокол и не возвращает клиентам никаких
данных.
use IO::Socket;
my ($server, $client, $host);
$server = IO::Socket::INET->new(
Proto => 'tcp', LocalPort => 80, Listen => SOMAXCONN, Reuse=> 1);
while ($server ($client = $server->accept()))
{
while ( <$client> ) { print; }
close $client;
}
Для работы этой программы необходима корректная настройка порта на уровне операционной системы.
Платформа Mozilla не поддерживает работу с сеансами FTP на низком
уровне. Элементарную операцию, доступную для разработчика, составляет
обращение к URL при помощи объекта nsIChannel. Это означает, что
каждый сеанс FTP состоит не более чем из четырех команд. На
open {hostname and port} //открыть сеанс
cd {directory} // перейти в нужный каталог
dir OR get {file} //получить содержание каталога или нужный файл
close //закрыть сеанс
Этот сеанс FTP осуществляется внутри платформы. Разработчик
приложений не получает информации о выполнении отдельных команд и не
может отдавать собственные команды. На практике это означает, что
единственный способ создания сеанса FTP, доступный разработчику
приложений, - запросить документ, в URL которого указан метод доступа ftp:. Эта процедура подробно описана в разделах "Загрузка
файлов" и "Каналы".
Если приложению необходимо перемещаться по иерархии каталогов FTP, потребуется несколько последовательных запросов. Как известно, URL может представлять не только отдельный файл, но и каталог FTP. При обращении к такому URL платформа возвращает содержимое каталога, правда, оформленное в виде HTML-документа. Проанализировав этот документ, можно получить список файлов и подкаталогов, находящихся в исходном каталоге, к которым, в свою очередь, можно сформировать запрос.
Тот же самый подход – сессия FTP, выглядящая как обращение к URL, – используется и при загрузке файлов на FTP-сервер. Подробнее об этом рассказано в разделе "Загрузка файлов".
Если ни один из предложенных методов не подходит для ваших целей, можно создать два сокета средствами JavaScript и, используя их, самостоятельно реализовать протокол FTP. При этом важно позаботиться о производительности приложения. Можно ожидать, что этот подход окажется почти столь же трудоемким, как и написание полноценного компонента XPCOM, реализующего протокол FTP, на C/C++.
Возможно, фрагмент кода, который предполагается запустить из
Простейший способ запустить отдельную программу – активизировать ее
с помощью
@mozilla.org/file/local;1 nsILocalFile
Затем следует связать полученный объект с каким-либо существующим
файлом (см. раздел "Файлы и папки"), после чего вызвать
метод этого объекта. Имейте в виду, что в системе UNIX
поведение метода определяется настройками среды GNOME, а не
значением переменной окружения PATH. Приложение, запущенное таким
образом, не зависит от процесса, в котором выполняется платформа
Mozilla, и не может быть остановлено средствами последней.
Более общий способ запуска процессов связан с использованием следующей пары XPCOM:
@mozilla.org/process/util;1 nsIProcess
Имейте в виду, что этот интерфейс до сих пор не реализован
полностью на всех платформах, поддерживаемых Mozilla. Чтобы
воспользоваться им, как и в предыдущем случае, нужно создать объект nsILocalFile и связать его с соответствующим исполняемым файлом.
Поскольку код для работы с процессами, как правило, зависит от
платформы, можно использовать для этого непереносимый метод initWithPath(). Передайте полученный объект методу init() объекта nsIProcess, а затем вызовите метод run(), чтобы создать процесс.
Пример вызова этого метода приведен ниже:
var blocking = true;
var argv = ["arg1","arg2"];
var result = {};
nsIProcess_object.run(blocking, argv, argv.length, result);
В процессе выполнения метода run() к объекту result, который
является обязательным аргументом метода, добавляется поле value,
которому в свою очередь присваивается значение 0 в случае успешного
blocking имеет значение true,
выполнение Mozilla будет приостановлено до завершения запущенного
процесса; при этом никакие окна Mozilla обновляться не будут. Если же
аргументу присвоено значение false, выполнение Mozilla будет
продолжено. В любом случае, по завершении запущенного процесса будет
установлено значение свойства exitValue объекта nsIProcess. Придется
поэкспериментировать, чтобы установить, какие значения соответствуют
нормальному
Работа с потоками вычислений более сложна. С точки зрения
разработчика приложений, отдельный поток вычислений представляет собой
всего лишь фрагмент кода, выполнение которого запланировано при помощи
метода window.. Строго говоря, в данном случае существует
лишь иллюзия отдельного потока – запланированное выполнение скрипта ни
при каких условиях не начнется раньше, чем завершится текущий
скрипт.
Это связано со способом реализации интерпретатора JavaScript в
составе платформы Mozilla. На низком уровне платформа поддерживает
отдельные потоки вычислений. Система потоков
Хотя интерпретатор не поддерживает истинные потоки вычислений, для
работы с потоками предусмотрен ряд интерфейсов. Фактически, они
позволяют организовывать код более аккуратно, чем при использовании
методов и . Последовательность действий по
созданию потока приведена в листинге 16.5:
var Cct = Components.classes["@mozilla.org/thread;1"];
var Cit = Components.interfaces.nsIThread;
var thread = { Run : function ()
{ alert(this.foo+" – выполняемый поток"); }
foo : "bar"
};
var mgr = Cct.createInstance(Cit);
mgr.init(thread, 0, Cit.PRIORITY_NORMAL, Cit.SCOPE_GLOBAL,
Cit.STATE_JOINABLE);
mgr.join();
alert("поток создан");
Объект, представляющий фрагмент исполняемого кода (в данном случае
– объект thread ), поддерживает интерфейс nsIRunnable. Он содержит
собственно исполняемый код (метод Run() ), а также данные, которые
могут потребоваться для выполнения этого кода. Объект mgr (менеджер
потока) содержит данные о конфигурации и состоянии потока вычислений.
С помощью вызова метода join() поток помещается в очередь на
выполнение или возобновление приостановленного выполнения
(приостановка и последующее возобновление выполнения невозможны для
кода, написанного для JavaScript). join() не эквивалентен методу , поскольку его вызов не приводит к немедленному выполнению
кода. Вместо этого код, представленный объектом, помещается в очередь,
и интерпретатор JavaScript дойдет до него не раньше, чем закончится
выполнение текущего скрипта. Поскольку интерпретатор выполняется в
одном потоке и не может быть приостановлен другим потоком, сообщение в
последней строке листинга всегда выдается раньше, чем сообщение из
кода в объекте thread.
Это означает, что ситуация конкуренции потоков (nsIRunnable.
Кроме того, скрипт JavaScript может создавать истинные потоки
вычислений, взаимодействуя с
В этом разделе описывается универсальная инфраструктура,
используемая для чтения, записи и передачи содержимого в приложениях
платформы Mozilla. В этом разделе также рассматривается обработка
фактов
Одна из важнейших функций прикладной части Mozilla – обработка содержимого Web-документов и других данных. Для этого инфраструктура платформы должна обеспечивать получение данных из внешних источников, а также передачу данных между различными составляющими платформы. В основе этой инфраструктуры лежит целый ряд концепций, относящихся к обработке и передаче содержимого и других данных.
В лекции 6 "События" описаны слушатели, наблюдатели и
Основные концепции, лежащие в основе инфраструктуры Mozilla для работы с содержимым, – файлы, папки, потоки данных, сеансы, каналы, транспорты, а также источники и приемники данных.
Такие понятия, как файл и каталог (папка) широко используются практически во всех операционных системах. Работа с ними описана в разделе "Общие приемы и методы программирования".
Поток
Сеанс представляет собой набор конфигурационной информации о выполняемом процессе, задании или деятельности. Такая конфигурационная информация используется, прежде всего, самим процессом. Однако при этом сеанс не является самим процессом, хотя его можно рассматривать в качестве контроллера последнего.
Примером сеанса может служить передача файла по FTP.
Канал передачи
Транспорт представляет собой элемент платформы, отвечающий за сетевое взаимодействие с использованием одного или нескольких протоколов. Так, если канал обеспечивает передачу данных на высоком уровне (получение документа, имеющего указанный URL), то на уровне транспорта реализована поддержка конкретных протоколов, например SMTP или TCP/IP.
Источники и приемники рассматриваются в следующем разделе.
Концепция источников и приемников занимает важное место в
архитектуре Mozilla. Обычно они используются для обработки XML-
документов. Источники и приемники образуют один из самых высоких
уровней обработки информации в составе платформы и, как правило,
выполняют преобразования, зависящие от
Источники и приемники (
Действительно, если раковина существует и работает, вода вытекает
из крана (источник) и попадает в
Именно такая ситуация характерна для работы с Mozilla. Для того чтобы загрузить содержимое документа в память, используется объект-приемник. Затем это содержимое или его часть можно извлечь при помощи источника данных.
Независимо от того, в каком порядке используются приемник и источник в каждом конкретном случае, источник всегда является производителем, а приемник – потребителем. С точки зрения разработчика приложения, если содержимое документа еще не находится в памяти платформы Mozilla (например, документ хранится в файле на диске), сначала необходимо создать приемник, чтобы загрузить это содержимое. На следующем этапе создается источник, с помощью которого различные части приложения могут получить доступ к загруженному содержимому. В некоторых случаях документ загружается в память автоматически, и тогда разработчику приходится создавать только источник.
В этой схеме есть одна сложность – при работе с документами в них могут вноситься изменения. Как правило, приемники используются для первоначальной загрузки содержимого в память и, таким образом, обеспечивают лишь одностороннюю обработку данных. Это означает, что ответственность за управление изменениями в документе лежит на источнике. Таким образом, источники не только обеспечивают доступ к содержимому документа, но и часто способны изменять его.
Mozilla не предоставляет
Источники и приемники служат для обработки содержимого на высоком уровне. Источники и приемники, входящие в состав платформы Mozilla, предназначены для различных целей.
Источники данных используются для обработки содержимого
Синтаксический анализатор (
Сериализатор (
Различные инструменты обработки данных, описанные в этом разделе, можно неофициально рассматривать как систему уровней или слоев, хотя эта система организована не так строго, как, например, стек сетевых протоколов. Уровни обработки данных показаны на рис.16.1.
(рис 16.1) Уровни обработки данныхНа рисунке приведена
На схеме не показаны многочисленные интерфейсы, представляющие собой конкретные реализации изображенных элементов, а также конкретные способы взаимодействия с остальной платформой.
Если нужный файл или другой источник информации доступен,
необходимо создать поток для работы с его содержимым. Потоки являются
основой архитектуры обработки данных в Mozilla, и существует множество
интерфейсов, ориентированных на работу с потоками. Среди них –
интерфейсы для создания, инициализации, преобразования потоков и
управления ими. Существуют специализированные разновидности потоков, в
частности, потоки с произвольным доступом и потоки, основанные на
строках. Практически для любой распространенной задачи можно найти
подходящий интерфейс-поток – просмотрите интерфейсы, содержащие в
названии слово
Чтобы продемонстрировать эту гибкость, в листинге 16.6 показаны три способа создания потока. Этот поток используется для чтения локального файла (последовательности байтов).
var Cc = Components.classes; var Ci = Components.interfaces; var mode_bits = 0x01; // from nsIFileChannel var perm_bits = 0; // from Unix/Posix open(2) var file_bits = 0; // from nsIFileInputStream var stream; var file = ... // см. листинг 16.3 или 16.2 // [1] Непосредственное создание stream = Cc["@mozilla.org/network/file-input-stream;1"]; stream = stream.createInstance(Ci.nsIFileInputStream); stream.init(file, mode_bits, perm_bits, file_bits); // [2] Создание на основе транспорта var trans = Cc["@mozilla.org/network/stream-transport-service;1"]; trans = trans.getService(Ci.nsIStreamTransportService); trans = trans.createInputTransport(stream,0,-1,true); var stream2 = trans.openInputStream(0,-1,0); // [3] Создание на основе канала var channel = Cc["@mozilla.org/network/local-file-channel;1"] channel = channel.createInstance(Ci.nsIFileChannel); channel.init(file, mode_bits, perm_bits); stream = channel.open(); // В любом случае, работа с потоком средствами JavaScript var s2 = Cc["@mozilla.org/scriptableinputstream;1"]; s2 = s2.createInstance(Ci.nsIScriptableInputStream); s2.init(stream); var bytes = 100; var content = null; content = s2.read(bytes);
В каждом из трех случаев в какой-то момент инициализации в качестве
аргумента передается ранее созданный объект nsILocalFile.
Пример 1. Файл читается или записывается непосредственно с использованием потока. Если не принять специальных мер, операции с файлом выполняются синхронно.
Пример 2. Это несколько необычный пример, поскольку в качестве
отправной точки для создания потока используется
Пример 3. Канал позволяет получить файл, не делая никаких предположений о механизме получения.
Из соображений эффективности JavaScript не позволяет
непосредственно читать данные из потоков или записывать в них. Вместо
этого необходимо создать специальный объект для выполнения операций
чтения и записи, передав ему поток. Пример создания такого объекта, а
также
Достаточно небольших изменений в примерах 1 и 2, чтобы создать
поток для записи вместо потока для чтения. В примере 3 такая замена
невозможна, поскольку каналы применяются только для чтения. При записи
данных предполагается, что поток вывода состоит из однобайтовых
символов (расширенная кодировка ASCII). При выводе любого содержимого,
состоящего из символов
Внутри платформы все строки представлены с помощью nsIBinaryInputStream ), как восьмибитные символы (вариант по
умолчанию) или как
Для преобразования содержимого потока используется следующая пара XPCOM:
@mozilla.org/intl/scriptableunicodeconverter;1 nsIScriptableUnicodeConverter
Mozilla также поддерживает множество компонентов с идентификатором контракта следующего вида:
@mozilla.org/streamconv;1?from={mime1}to={mime2}
Здесь mime1 и mime2 – типы nsIStreamConverter.
Такой объект получает входной поток и преобразует его содержимое,
создавая при этом новый поток, из которого могут читаться
| Исходный тип |
Тип |
|---|---|
application/http-index-format |
text/html |
application/mac-binhex40 |
*/* |
application/x-unknown-content-type |
*/* |
|
несжатое содержимое |
|
несжатое содержимое |
gzip |
несжатое содержимое |
message/rfc822 |
application/vnd.mozilla.xul+xml |
message/rfc822 |
*/* |
message/rfc822 |
text/html |
multipart/byteranges |
*/* |
multipart/ |
*/* |
text/ftp-dir |
application/http-index-format |
text/ |
application/http-index-format |
text/plain |
text/plain |
x- |
несжатое содержимое |
x-gzip |
несжатое содержимое |
Изучив описание XPIDL объекта nsIStreamConverter, можно понять,
каким образом такое преобразование может быть реализовано с
использованием двух объектов nsIStreamListener вместо двух полноценных
потоков. Такой подход позволяет конвертерам работать не только с
потоками ввода, но и с потоками любого типа.
Объекты XPCOM транспортного уровня отвечают за передачу содержимого изнутри платформы Mozilla и наоборот. Таким образом, транспорты являются более общим средством, чем потоки, которые предназначены для передачи информации внутри платформы или операций чтения/записи с локальными файлами. Если потоки, как правило, выполняют синхронные операции и работают непосредственно с указанным источником, транспорты могут выполнять как синхронные, так и асинхронные операции. При этом они могут буферизовать данные в промежутках между запросами пользователя.
Транспортные уровни, доступные в настоящее время, приведены в таблице 16.7.
| Реализация | Интерфейс |
|---|---|
@mozilla.org/network/ |
nsIStreamTransportService |
@mozilla.org/network/socket- |
nsISocketTransportService |
@mozilla.org/network/storage- |
nsITransport |
@mozilla.org/xmlextras/ |
nsISOAPTransport |
@mozilla.org/xmlextras/ |
nsISOAPTransport |
Пять транспортов, приведенных в таблице, предназначены для: всех
потоков, включая локальные файлы; сокетов; кэша браузера; транспорта
HTTP для запросов
Реализация является относительно новой (с
версии 1.3) и заменяет недоступную более реализацию file-.
Вам могут встретиться примеры кода, использующие старую реализацию.
Каналы представляют собой односторонний (только для чтения)
механизм получения содержимого указанного URL. Хотя в принципе канал
может использоваться и для
В обычных ситуациях разработчик приложений редко создает каналы
самостоятельно. Подобно объектам nsIFile и потокам, каналы чаще всего
создаются платформой при выполнении операций более высокого уровня.
Файл и поток образуют пару объектов, тесно связанных между собой, и
аналогичную пару образуют канал и URL. Эта аналогия не идеальна,
поскольку каналы создаются разработчиком вручную значительно реже, чем
потоки. Как правило, работа с каналами скрыта в глубине того или иного
протокола. Второе различие состоит в том, что канал представляет собой
усовершенствованную реализацию запроса (объект nsIRequest ), который, в
свою очередь, является модернизированным вариантом объекта-URL. Так
что, строго говоря, канал и URL не являются независимыми
объектами.
Как правило, работа с каналами начинается с обращения к следующей паре XPCOM:
@mozilla.org/network/io-service;1 nsIIOService
Этот компонент позволяет получить интерфейс nsIIOService при помощи
метода getService(). Применительно к транспортам данный интерфейс
фактически представляет собой службу имен для nsIIOService представляет собой отправную точку для
получения содержимого определенного URL.
С помощью интерфейса nsIIOService можно создавать объекты с
интерфейсами nsIURI и nsIURL. Каждый такой объект представляет
определенный URL подобно тому, как объект с интерфейсом nsIFile
представляет определенный файл. Указанные интерфейсы также
предоставляют объекты для управления протоколом, соответствующим
данной схеме или URL (protocol newChannelFromURI() интерфейса nsIIOService.
Имея доступ к объекту-каналу, можно получить с его помощью объект-
поток и приступить к обработке данных. Поток необходим для работы с
получаемым содержимым. Интерфейс nsIIOService содержит ряд полезных
методов, с помощью которых во многих случаях можно обойтись без
непосредственного обращения к объекту, управляющему протоколом.
При обращении к URL каналы выполняют целый ряд рутинных операций –
устанавливают соединение и получают содержимое, преобразуют его,
получают и хранят информацию о типе
| Интерфейс канала | |
|---|---|
nsIChannel |
Все типы, перечисленные ниже |
nsICachingChannel |
http: |
nsIDataChannel |
data: |
nsIEncodedChannel |
http: |
nsIFileChannel |
file: |
nsIFTPChannel |
ftp: |
nsIHttpChannel |
http: |
nsIImapMockChannel |
: |
nsIInputStreamChannel |
@mozilla.org/network/input- |
nsIJarChannel |
: |
nsIMultiPartChannel |
для внутреннего использования |
nsIResumableChannel |
ftp: ( http: пока не поддерживается) |
nsIUploadChannel |
file:, ftp:, http: |
nsIViewSourceChannel |
view-source: |
nsIWyciwygChannel |
wyciwyg: (не путать с ' ) |
Как видно из таблицы 16.8, большинство каналов соответствуют схемам
URL. Все каналы поддерживают базовую функциональность интерфейса nsIChannel, в первую очередь, – методы open() и asyncOpen(). Эти
методы возвращают поток или объект – слушатель потока. Другие
интерфейсы каналов лишь дополняют эту базовую функциональность с
учетом специфики конкретных протоколов. Их не следует рассматривать
как принципиально отличные каналы – скорее это расширения базового
канала.
Несколько интерфейсов, приведенных в таблице, заслуживают особого
упоминания. Так, nsIUploadChannel ориентирован на загрузку содержимого
с локальной машины на удаленный сервер. Вместо того чтобы возвращать
поток, он принимает его при инициализации и направляет данные потока
на сервер. Канал nsIResumableChannel используется для загрузки по
протоколу FTP, которая может приостанавливаться и возобновляться.
Аналогичная функциональность для протокола HTTP в классическом
браузере пока не поддерживается.
Еще один особый случай – канал для тривиальных
"протоколов". Канал может использоваться не только для
сложных сетевых протоколов, например FTP и HTTP, но и для простых
случаев передачи данных между диском и памятью (input-, приведенный в таблице, связан как раз с таким
использованием каналов.
Пример использования канала для доступа к локальному файлу приведен в листинге 16.6.
Источники данных обеспечивают поддержку работы с фактами,
необходимую для шаблонов XUL и хранилищ данных
Концепция источника данных воплощена в интерфейсе nsIRDFDataSource.
Этот интерфейс поддерживает все операции, необходимые для работы с
фактами
Отдельные факты могут быть сконструированы из простых объектов
XPCOM, основанных на интерфейсах nsIRDFResource и nsIRDFLiteral. Как
правило, читать из источника данных можно всегда, а записывать в него
– при определенных условиях. Источник данных предоставляет простую
функциональность, обеспечивающую добавление, удаление и изменение
данных, а также запросы к ним. В отличие от других механизмов
обработки данных, источники данных работают с логическими объектами
(фактами), а не с потоками байтов или символов.
Полезные интерфейсы XPCOM для работы с источниками данных можно разделить на три группы:
Вспомогательные средства и утилиты. Они необходимы для выполнения базовых операций.
Дополнительные расширения. Некоторые интерфейсы расширяют базовую
функциональность интерфейса nsIRDFDataSource для различных целей.
Поддержка содержимого. Существуют специализированные источники
данных для содержимого определенного типа. Эти источники
подразделяются на обычные, в которых данные берутся из файлов
Выбрав неверный источник данных из последней категории, можно
напрасно потратить часы, дни или даже недели на отладку программы.
Поэтому важно представлять себе поддерживаемые
Обратите внимание, что в именах методов интерфейса nsIRDFDataSource
слово source (источник) не относится к источникам данных. Source и target (цель) в этих именах означают субъект и объект факта
соответственно – см., например, метод GetSource().
При использовании шаблонов XUL доступ к объектам, имеющим интерфейс nsIRDFDataSource, обеспечивают объекты
Компоненты, используемые для непосредственной работы с window.Components.classes. Компоненты, приведенные в таблице, можно
разделить на следующие четыре группы.
| Имя компонента | Интерфейсы | Назначение |
|---|---|---|
@mozilla.org/ |
nsIRDFService |
Отправная точка; позволяет создавать источники данных nsIRDFDataSource, субъекты, объекты и предикаты nsIRDFNode |
@mozilla.org/ |
nsIRDFContainer |
Создает объекты-контейнеры <, <Seq> или <Alt> |
@mozilla.org/ |
nsIRDFContainerUtils |
Вспомогательные методы для работы с объектами-контейнерами |
@mozilla.org/ |
различные | Создание объектов, представляющих части факта |
@mozilla.org/ |
nsIExpatSink |
Превращает объекты XML, основанные на |
@mozilla.org/ |
nsIRDFXMLParser |
Превращает документ |
@mozilla.org/ |
nsIRDFXMLSerializer nsIRDFXMLSource |
Превращает хранилище фактов в документ |
@mozilla.org/ |
nsIRDFDelegateFactory |
Позволяет связать ресурс (элемент факта) с объектом- |
Первая группа из одного интерфейса представляет собой отправную
точку для работы с nsIRDFService позволяет
создавать объекты nsIRDFDataSource на основе :. Соответствующие URL перечислены в таблице 16.11. Объект,
представляющий подлежащее, дополнение или предикат факта, также может
быть создан при помощи этого интерфейса на основе строки JavaScript.
Для получения доступа к объекту, поддерживающему интерфейс nsIRDFService, используется метод getService(), а не createInstance().
Вторая группа компонентов, приведенных в таблице, предоставляет
интерфейсы-фабрики для создания контейнеров
imap mailbox news moz-abdirectory moz-abldapdirectory moz-abmdbdirectory moz-aboutlookdirectory
Третья группа компонентов предназначена для непосредственного
Наконец, применение последнего интерфейса в таблице 16.9, nsIRDFDelegateFactory, требует глубокого понимания архитектуры
платформы. Оно позволяет привязать какое-либо действие к созданию или
уничтожению ресурса, используемого в факте. Такой объект-
Mozilla предоставляет ряд возможностей для расширения функциональности источников данных. Расширение выражается скорее в большей гибкости источников, чем в доступе к каким-либо дополнительным данным. Эти возможности доступны при помощи ряда интерфейсов XPCOM, которые описаны в таблице 16.10.
| Имя интерфейса | Компоненты, реализующие интерфейс | Назначение |
|---|---|---|
nsIRDFDataSource |
Все источники данных, но см. ограничения в таблице 16.12 | Основные операции с хранилищем фактов, доступным с помощью источника данных |
nsIRDFCompositeDataSource |
@mozilla.org/ |
Составной источник данных, представляющий объединение фактов из одного или нескольких источников. Добавляемые факты добавляются в первый из этих источников |
nsIRDFInMemoryDataSource |
@mozilla.org./ |
Источник данных, основанный на хранилище фактов, которое не зависит от всех остальных фактов |
nsIRDFPurgeableDataSource |
@mozilla.org/ |
Позволяет удалить все факты в источнике данных |
nsIRDFPropagatableDataSource |
@mozilla.org/ |
Активизирует и отключает передачу событий, представляющих изменение фактов, любым наблюдателям |
nsIRDFRemoteDataSource |
@mozilla.org/autocompleteSession;1?type= |
Обеспечивает возможность синхронизации хранилища фактов, доступного с помощью источника данных, с исходным источником фактов.
Идентификатор контракта xml- преназначен для работы с произвольными файлами |
Система шаблонов XUL не только позволяет использовать составные
источники данных, но и поддерживает nsIRDFDataSource. Каждый метод этого
интерфейса просматривает отдельные источники данных, содержащиеся в
контейнере, и при необходимости поочередно вызывает соответствующие
методы каждого из них.
Источники данных, находящиеся в памяти, лежат в основе нескольких более сложных источников. Создание источника данных в памяти представляет собой естественную отправную точку в ситуации, когда содержание хранилища фактов не берется откуда-либо в готовом виде, а должно конструироваться динамически. Очистка такого источника от данных представляет собой его возвращение к исходному пустому состоянию. Отключение передачи событий, представляющих изменение фактов, несколько улучшает производительность источника.
Интерфейс nsIRDFRemoteDataSource предоставляет возможность
сохранить текущее состояние хранилища фактов в том месте, откуда были
взяты исходные данные, или загрузить данные оттуда в хранилище фактов.
Как правило, это место представляет собой файл , а загрузка – с помощью метода Refresh(). , поддерживается только для URL типа file:,а Refresh() – для
URL типов file: и http:.
Не все источники данных одинаковы. Они могут отличаться характером содержимого, а также способами доступа к нему. Эти различия – основная причина сложностей при использовании источников данных. С точки зрения разработчика приложений не всегда очевидно, какие именно факты доступны при помощи тех или иных источников данных, и каким образом их можно получить. Все источники данных основаны на компонентах XPCOM, имена которых имеют следующий вид:
@mozilla.org/rdf/datasource;1?name={arg}
Допустимые значения для {arg} приведены в крайнем левом столбце
таблицы 16.12.
Характер содержимого, которое доступно приложению при помощи
источника данных, очевиден только в случае обычных файлов
Внутренние источники данных, содержащие информацию о закладках,
истории просмотра страниц, поиске, а также некоторых типах внутренних
данных, используют файлы в папке
Такая "анонимность" содержимого представляет собой серьезную проблему для шаблонов XUL и скриптов, которые пытаются работать с внутренними источниками данных. В обоих случаях необходимо знать структуру данных заранее. К счастью, работа с внутренними источниками данных необходима лишь для узкого круга задач.
В разделе "Отладка" этой лекции приведены примеры кода,
позволяющего просматривать содержимое источников данных. В таблице
16.11 приведены субъекты верхнего уровня, а также часто используемые
предикаты для большинства внутренних источников данных. Специальное
значение означает отсутствие источника данных, а не пустой
источник.
Помимо специфики содержимого, для многих источников данных Mozilla
характерна ограниченная функциональность. Это означает, что, даже если
вам известна структура содержимого источника данных, функциональность
источника может оказаться недостаточной для того, чтобы получить
доступ к этому содержимому. В таких случаях, хотя соответствующий
объект XPCOM формально поддерживает интерфейс nsIRDFDataSource, многие
методы этого интерфейса не выполняют никаких действий, возвращая
исключение или сообщение об ошибке. Работа над этими компонентами –
источниками данных - еще не завершена.
Степень поддержки интерфейса nsIRDFDataSource для каждого из
существующих источников данных представлена в таблице 16.12. Данные
таблицы соответствуют версии Mozilla 1.4 и отражают лишь принципиальное наличие той или иной функции. Перед работой с
некоторыми редко используемыми источниками данных могут понадобиться
сложные подготовительные действия, которые не рассматриваются в этом
курсе.
Источники данных, не имеющие :, не могут быть
подключены к шаблону XUL при помощи атрибутов XML. Однако они могут
быть подключены к шаблону при помощи скрипта. Источники данных,
которые не зарегистрированы в XPCOM при параметрах сборки по
умолчанию, не могут быть использованы ни из XUL, ни из XML. Составной
источник данных
: |
Предикаты, используемые в качестве контейнера для фактов | |
|---|---|---|
|
moz-abdirectory:// |
http://home.netscape.com/NC-rdf#child и http://home.netscape.com/NC-rdf#CardChild |
|
NC:BookmarksRoot
NC:PersonalToolbarFolder
|
Использует контейнеры |
|
Различные (например, NC:BrowserCharsetMenuRoot ) |
Использует контейнеры |
|
NC:FilesRoot |
http://home.netscape.com/NC-rdf#child |
|
NC:HistoryRoot
NC:HistoryByDate
|
http://home.netscape.com/NC-rdf#child |
|
URL индекса | |
|
NC:SearchEngineRoot
NC:LastSearchRoot
NC:SearchResultsSitesRoot
NC:FilterSearchUrlRoot
NC:FilterSearchSitesRoot
SearchCategoryRoot
LastSearchMode
|
http://home.netscape.com/NC-rdf#child |
|
||
|
Нет; используйте любой |
Контейнеры не используются; каждый |
|
Корневым является любой find: |
Контейнеры не используются; каждый |
|
Корневой |
http://home.netscape.com/NC-rdf#child |
|
msgaccounts:/ |
|
|
Корневой |
|
|
NC:smtpservers |
http://home.netscape.com/NC-rdf#child |
|
Корневой |
http://home.netscape.com/NC-rdf#child |
|
NC:WindowMediatorRoot |
Использует контейнеры |
| Имя, используемое в идентификаторе контракта | Есть ли URL с префиксом :? |
Зарегистрирован ли в XPCOM при параметрах сборки по умолчанию? | Интерфейс XPCOM для определенного |
Поддержка |
Поддержка ArcLabelsOut() |
Поддержка GetAllResources() |
Поддержка команд |
|---|---|---|---|---|---|---|---|
addressdirectory |
v | v | v | v | v | v | |
|
v | v | v | v | v | v | |
|
v | v | v | v | v | v | |
files |
v | v | v | ||||
|
v | v | v | v | v | ||
httpindex |
v | v | v | v | v | v | v |
internetsearch |
v | v | v | v | v | ||
ispdefaults |
v | v | |||||
local- |
v | v | v | v | v | ||
localsearch |
v | v | v | ||||
mailnewsfolder |
v | v | v | v | v | v | |
msgaccountmanager |
v | v | v | v | |||
msgfilters |
v | v | v | ||||
smtp |
v | v | v | ||||
|
v | v | v | v | |||
window- |
v | v | v | v | v | v | v |
|
v | v | v | v | |||
mailsounds |
v | ||||||
|
v | ||||||
relatedlinks |
v | v | v | ||||
in-memory- |
v | v | v | v | |||
|
v | возможно | v | v | |||
xml- |
v | v | только для URL с префиксом file: |
v | v | v |
Web-браузеры функционируют в среде, которая отличается от среды выполнения традиционных программ. В WWW нет таких сущностей как файл или имя файла. Вместо них имеются указатели ресурсов (URL), а также документы, представляющие эти ресурсы. Часто такие документы имеют сложную структуру и основаны на языке XML. Такая среда требует иного подхода к написанию скриптов, нежели описанный в разделах "Файлы и папки" и "Потоки".
Кроме того, браузеры работают с формирующимся стеком Web-протоколов,
который можно условно назвать стеком XML. В основе этого
стека лежит протокол HTTP, поверх которого реализуются различные
прикладные протоколы.
Этот еще не вполне устоявшийся
Вряд ли полномасштабная поддержка всех перечисленных стандартов будет когда-либо реализована в рамках платформы Mozilla, поскольку некоторые из них ориентированы на взаимодействие между корпоративными приложениями, а не между приложением и пользователем. Область применения стека Web-протоколов выходит далеко за рамки обычных задач клиентского ПО, например просмотра Web-страниц или получения электронной почты. Вместо каналов, которые являются стандартным средством работы с URL на платформе Mozilla, при работе с этими протоколами используются специализированные интерфейсы.
Приложение на платформе Mozilla может вообще не иметь собственного
графического интерфейса. Например, оно может быть основано на
инструменте xpcshell. Однако и в этом случае приложение может
использовать развитые средства для работы с XML, входящие в состав
платформы. Таким образом можно создавать серверы, реализующие
некоторые идеи протоколов для бизнес-процессов. Например, такой сервер
может перенаправлять получаемые документы XML в зависимости от их
содержания.
Формат
Платформа Mozilla позволяет представить
@mozilla.org/network/simple-uri;1 nsIURI
Для URL существует специализированный объект, который поддерживает
типичные http: and ftp:. Соответствующая пара
XPCOM широко применяется при разработке на платформе Mozilla:
@mozilla.org/network/standard-url;1 nsIURL
Этот компонент также поддерживает nsIURI. Вообще, большинство
компонентов, идентификатор контракта которых содержит подстроку
или url, поддерживают оба этих интерфейса или один из них.
Часто требуется проверять корректность URL, введенного пользователем. В рамках платформы предусмотрено несколько инструментов проверки и исправления синтаксиса. Следующая пара XPCOM может использоваться при попытке исправить некорректный URL, введенный пользователем:
@mozilla.org/docshell/urifixup;1 nsIURIFixup
Этот интерфейс содержит метод createFixupURI(), который может
применяться для работы с ключевыми словами, введенными в строку адреса
браузера, а также сокращенными формами URL, например www.test.com или
даже test.com вместо http://www.test.com. Соответствующий компонент docshell доступен как элемент объектной модели приложения в окне
браузера Mozilla (подробнее об этом рассказано в разделе
"<iframe>" лекции 10 "Окна и панели").
Второй способ исправления синтаксиса URL связан с использованием следующей пары XPCOM:
@mozilla.org/network/url-parser;1?auth=maybe nsIURLParser
Этот интерфейс позволяет выполнить разбор URL в соответствии с
Наконец, можно задействовать метод базового интерфейса nsIURI. Если передать этому методу сокращенный (
В любом случае, окончательной проверкой правильности URL является то, удается ли получить с его помощью нужный ресурс.
Специализированного объекта XPCOM, предназначенного для
Чтобы загрузить с удаленного сервера файл или документ, можно
создать канал для нужного
@mozilla.org/embedding/browser/nsWebBrowserPersist;1 nsIWebBrowserPersist
В качестве параметров этот интерфейс принимает nsILocalFile. С его помощью можно
загрузить документ и сохранить его в виде файла при помощи
единственного вызова метода.
Чтобы организовать асинхронную загрузку, во время которой могут
выполняться другие задачи, нужно создать объект – слушатель или
наблюдатель содержимого – на чистом JavaScript. В документации к
большинству интерфейсов, например nsIChannel, описано, какие слушатели
и наблюдатели поддерживаются данным интерфейсом. При загрузке
очередной порции документа, которая будет передана слушателю или
наблюдателю, можно обработать ее, сохранить или просто
проигнорировать.
Наиболее распространенный способ создания объекта для обработки
содержимого по частям – реализовать интерфейс nsIWebProgressListener.
Любой объект, поддерживающий интерфейс nsIWebProgress, может
зарегистрировать один или несколько таких слушателей, а многие другие
интерфейсы принимают такой объект-слушатель в качестве аргумента
инициализации. Существуют готовые объекты XPCOM, поддерживающие этот
интерфейс, поэтому во многих случаях реализовывать его самостоятельно
не понадобится.
Отслеживать состояние процесса асинхронной загрузки можно по-
разному. В принципе, обработка поступающего содержимого и получение
информации о
Наиболее простой способ отслеживать состояние загрузки – доработать обычный слушатель содержимого так, чтобы он при получении очередной порции данных определял, какая часть содержимого уже загружена. Такой слушатель не получает событий, связанных с завершением загрузки, и других управляющих событий; он лишь отслеживает состояние процесса загрузки.
Более удобный способ – с помощью чистого JavaScript создать объект,
поддерживающий интерфейс nsIProgressEventSink, и передать его объекту,
ответственному за загрузку. Такой объект-приемник (слушатель событий)
получает информацию обо всех изменениях в nsIRequestObserver, и передать его объекту канала или транспорта.
Такой наблюдатель получает только уведомления о начале и окончании
загрузки, но не о ее промежуточном состоянии, приостановке или
прерывании.
Более сложный способ отслеживания процесса загрузки предполагает использование объектов XPCOM, связанных с Менеджером загрузок Mozilla. Эти объекты могут применяться независимо от диалогового окна менеджера загрузок или вместе с ним. В последнем случае разработчик должен самостоятельно обеспечить взаимодействие объектов XPCOM с диалоговым окном. Независимо от использования диалогового окна этот способ может применяться лишь при загрузке файлов для последующего сохранения на диске. Отправной точкой для использования этого способа является следующая пара XPCOM:
@mozilla.org/download-manager;1 nsIDownloadManager
Единственный объект, созданный таким образом, одновременно
управляет всеми текущими загрузками. Метод addDownload() этого объекта
используется для создания нового объекта, поддерживающего интерфейс nsIDownload, для каждой загрузки.
Каждый такой объект, соответствующий отдельной загрузке, содержит
всю конфигурационную информацию о ней и уведомляет Менеджер загрузок о
ходе процесса. Конфигурационная информация передается объекту в момент
его создания в виде аргументов метода addDownload(). В частности,
последний аргумент, имеющий интерфейс nsIWebBrowserPersist, содержит
информацию о том, каким образом должен быть сохранен полученный файл.
Если этот аргумент существует, объект загрузки автоматически
уведомляет Менеджер загрузок о
Mozilla также поддерживает группы загрузки. Это вариант интерфейса nsIRequest, который позволяет одновременно работать с несколькими URL.
Группы загрузки могут использоваться, если необходимо получать
информацию о процессе загрузки нескольких файлов.
Тип
@mozilla.org/mime;1 nsIMIMEService
Прежде всего, этот объект обращается к информации о типах file(1).
Метод интерфейса nsILocalFile
позволяет запустить исполняемый файл или открыть файл данных при
помощи соответствующего приложения. Для вызова метода
нет необходимости знать что-либо о типе файла.
Для отправки форм можно использовать объект AOM (объектной модели
приложения) . Работа с ним описана в разделе
"Отправка форм" лекции 7 "Формы и меню". Он основан
на следующей паре XPCOM:
@mozilla.org/xmlextras/xmlhttprequest;1 nsIJSXMLHttpRequest
Загрузка документов на сервер столь же проста. Следуйте
рекомендациям раздела "Каналы" этой лекции и укажите адрес
для загрузки, используя интерфейс nsIIOService. В качестве адреса для
загрузки может быть указана программа, выполняемая на стороне сервера,
в случае применения запроса HTTP POST или каталог FTP в случае
использования протокола FTP. Создав канал, следует получить интерфейс nsIUploadChannel при помощи метода QueryInterface(), а затем передать
этому интерфейсу поток ввода с содержимым файла, который должен быть
загружен. Чтобы отправить содержимое, нужно снова получить интерфейс nsIChannel, и вызвать метод open() или asyncOpen(), как и при работе с
любым объектом канала.
Протокол HTTP, лежащий в основе стека Web-протоколов, широко
применяется в Mozilla. Скрипты могут работать с ним различными
способами, включая непосредственное использование объекта AOM . Работа с прочими протоколами, поддерживаемыми Mozilla,
требует применения специализированных объектов.
Документация по использованию Web-протоколов в Mozilla доступна по адресу http://www.mozilla.org/xmlextras/.
XML-RPC представляет собой простейшую надстройку над HTTP. Следующая пара XPCOM используется для формирования фрагмента XML, содержащего запрос RPC, его синхронную или асинхронную передачу по указанному URL с помощью HTTP, а также возвращение результатов или сообщений об ошибках:
@mozilla.org/xml-rpc/client;1 nsIXmlRpcClient
Сообщения об ошибках возвращаются в виде объектов с интерфейсом nsIXmlRpcFault.
При использовании традиционного RPC применяется утилита rpcgen(1)
или другие аналогичные инструменты, которые генерируют код на языке C
для удаленного вызова процедуры. Этот код:
Особенность использования XML-RPC на платформе Mozilla состоит в
том, что JavaScript является интерпретируемым языком, а код платформы,
работающий с удаленным вызовом процедур, как правило, уже
скомпилирован. Поэтому порядок работы с RPC отличается от
традиционного подхода. Объект nsIXmlRpcClient упаковывает
вызовы XML-RPC в переносимый XML, но передачу вызова и получение
результата он делегирует объекту канала. Поэтому для управления
задержками или получения информации о них необходимо обращаться именно
к объекту канала. Задача преобразования типов JavaScript в переносимые
типы XML-RPC оставлена разработчику приложения. Объект nsIXmlRpcClient предоставляет ряд методов-фабрик для создания
типов XML-RPC, однако разработчик должен воспользоваться этими
методами самостоятельно. Наконец, этот объект реализован на JavaScript
и постоянно обращается к объекту window.Components для
разрешения имен, что ограничивает его производительность.
Протокол
Вызов
Для того чтобы разработчик мог создать сообщение
Ниже перечислены пары XPCOM для создания объектов, соответствующих каждой из задач:
@mozilla.org/xmlextras/schemas/schemaloader;1 nsISchemaLoader@mozilla.org/xmlextras/soap /call;1 nsISOAPMessage@mozilla.org/xmlextras/soap /transport ;1?protocol=http;nsISOAPTransport@mozilla.org/xmlextras/soap /call;1 nsISOAPCall@mozilla.org/xmlextras/soap /fault ;1 nsISOAPFault@mozilla.org/xmlextras/soap /response;1 nsISOAPMessageПомимо этих объектов, обеспечивающих базовую функциональность для
работы с
Интерфейс nsISOAPMessage также доступен в форме объекта AOM SOAPCall, создать который очень просто:
var soap_call = new SOAPCall();
Аналогичным образом интерфейсу nsISOAPParameter соответствует
объект AOM SOAPParameter. Эти два объекта позволяют выполнять простые
вызовы
Широко известен сервис
http://lxr.mozilla.org/seamonkey/source/extensions/xmlextras/tests/
В этом каталоге находятся три небольших программы на языке Perl,
которые могут получать
Поддержка
Последний протокол стека Web-протоколов, поддерживаемый Mozilla, –
Поддержка
http://lxr.mozilla.org/seamonkey/source/extensions/xmlextras/wsdl/
Чтобы узнать идентификаторы контрактов (
@mozilla.org/xmlextras/wsdl/
Полная поддержка
Для обращения к системе обработки
@mozilla.org/document-transformer;1?type=text/xsl nsIXSLTProcessor
Объект с таким интерфейсом принимает в качестве аргументов два
дерева или поддерева
Некоторым скриптам необходимо получать информацию о состоянии самой платформы Mozilla или изменять это состояние. Для этого скрипты должны получить доступ к различным аспектам внутреннего состояния платформы при помощи интерфейсов XPCOM. В этом разделе обсуждаются управление кэшем, каталог файловой системы, настройки, защита, а также профили пользователя.
Предполагается, что кэш браузера Mozilla прозрачен для любых действий по получению Web-документов, однако при необходимости с ним можно взаимодействовать. Кэш используется для любых запросов к URL, выполняемых платформой, если в явном виде не было указано избегать использования кэша или отключить его. Доступ к кэшу на низком уровне осуществляется при помощи следующей пары XPCOM:
@mozilla.org/network/cache-service;1 nsICacheService
Объект, полученный таким образом, должен иметь доступ к константам,
предоставляемым интерфейсом nsICache. Техника работы с кэшем на низком
уровне является неожиданно сложной в силу используемой модели
блокировки, которая допускает несколько одновременных сеансов чтения,
но лишь один сеанс записи. Это означает, что попытка работы с кэшем на
низком уровне может оказаться неудачной по причине конфликта из-за
доступа к ресурсам. Разработчику приложений проще не иметь дела с
этими тонкостями и использовать сервисы более высокого уровня,
например транспорты и каналы, которые взаимодействуют с кэшем
автоматически. Однако указанный интерфейс предоставляет и ряд методов,
полезных для разработчика приложений, например evictEntries(), который
может использоваться для очистки кэша.
Очень простое применение кэша – упреждающая загрузка документов.
Такая загрузка подразумевает помещение документа в кэш без
обязательного отображения или какой-либо обработки. Упреждающая
загрузка может выполняться лишь для URL с префиксом http:, которые не
являются запросами GET (не имеют части вида ?param= ). Для упреждающей
загрузки используется следующая пара XPCOM:
@mozilla.org/prefetch-service;1 nsIPrefetchService
Аналогичная функциональность с возможностью более детального
управления доступна при помощи интерфейса nsIRequest, который является
основой для каналов и транспортов. Свойство loadFlags, которое может
устанавливаться для каждого
Платформа Mozilla поддерживает
Каталоги (
Разработчику приложений целесообразно использовать эту службу
каталогов, если приложение должно задействовать те же файлы и папки,
что и сама платформа Mozilla. Это позволяет обеспечить надлежащую
Эта
@mozilla.org/file/directory_service;1 nsIDirectoryService
Обратите внимание, что в названии directory_service использовано
подчеркивание, а не дефис. Этот каталог содержит пути ко всем важным
файлам и папкам, о которых должны знать приложения и их разработчики.
Поэтому каталог файловой системы является принципиально важным для
приложений на платформе Mozilla. После того, как объект,
представляющий файл или папку, получен с помощью каталога, с ним можно
обращаться так же, как с любым другим файлом или папкой.
Интерфейс nsIDirectoryService не слишком полезен сам по себе. Его
роль состоит в управлении объектами-источниками (providers). Источник
представляет собой объект, который предоставляет nsIDirectoryServiceProvider. Когда скрипт обращается с
запросом к
Помимо источников, nsIProperties – стандартный интерфейс, позволяющий
получить по имени объекта данные о нем, хранящиеся в каталоге. Нужное
имя (фактически, псевдоним искомого объекта) передается службе
каталогов при помощи метода get() интерфейса nsIProperties. В
результате будет возвращен любой объект (запись в каталоге), имя
которого соответствует переданному псевдониму. Как и другие службы
каталогов Mozilla, каталог файловой системы поддерживает этот
интерфейс. Пример его использования приведен в листинге 16.7:
var Cc = Components.classes;
var Ci = Components.interfaces;
var dir = Cc["@mozilla.org/file/directory_service;1"];
dir = dir.getService(Ci.nsIDirectoryService); // Инициализация
// Поместите сюда обращения к dir.registerProvider(provider_object)
var dir_props = dir.QueryInterface(Ci.nsIProperties);
var file = dir_props.get("myalias", Ci.nsIFile);
if (file == null )
alert("Нет такого ресурса");
Этот код создает объект nsIProperties и
запрашивает из каталога данные для псевдонима "myalias".
Поскольку каталог файловой системы XPCOM содержит информацию о файлах
и папках, ожидается, что будет возвращен объект, поддерживающий
интерфейс nsIFile.
В данном примере объект не будет найден в каталоге, и система
выдаст предупреждение (последняя строка кода). Это произойдет по двум
причинам. Во-первых, строка "myalias" в рамках платформы
Mozilla не определена в качестве псевдонима известных файла или папки.
Это легко исправить – достаточно использовать какой-либо известный
псевдоним. Однако есть и более серьезная проблема – к объекту службы
каталогов не было добавлено ни одного источника. Поэтому мы могли бы
ожидать, что в данном коде не будут распознаваться никакие псевдонимы.
Однако это не так. На практике, к объекту каталога файловой системы
всегда подключено, как минимум, два источника.
Дополнительную сложность работе со службой каталогов придает тот факт, что объект, который представляет каталог файловой системы, одновременно реализует интерфейс источника. Этот источник определяется следующей парой XPCOM:
@mozilla.org/file/directory_service;1 nsIDirectoryServiceProvider
Такой источник представляет собой дополнение к трем, перечисленным
выше. Его редко регистрируют в каких-либо
Пример непосредственной работы с этим источником приведен в листинге 16.8.
var Cc = Components.classes;
var Ci = Components.interfaces;
var prov = Cc["@mozilla.org/file/directory_service;1"];
prov = prov.getService(Ci.nsIDirectoryServiceProvider);
var result = {}; // пустой объект
var file = prov.getFile("alias", result);
if ( file == null ) alert("Нет такого ресурса");
// alert(result.value)
Поскольку обычно источники управляются объектом, представляющим
getFile() приспособлены для
использования с таким объектом. Второй аргумент этого метода, пустой
объект, позволяет источнику передать nsIDirectoryServiceProvider.
В оставшейся части раздела перечисляются псевдонимы, поддерживаемые названными источниками. Мы начнем с последнего, специализированного источника.
Эти псевдонимы поддерживаются специализированным источником,
встроенным в объект
| Псевдоним | Описание соответствующего nsIFile |
|---|---|
ComRegF |
Реестр компонентов XPCOM – не используется |
ComsD |
Папка, содержащая компоненты XPCOM |
CurProcD |
Папка с исполняемым файлом текущего процесса, в UNIX всегда определяется переменной окружения $MOZILLA_FIVE_HOME |
CurWorkD |
Папка, которая является текущим рабочим каталогом исполняемого файла |
DrvD |
Корень файловой системы операционной системы – в Windows обычно C:; в UNIX: /; в MacOS: корневой том |
GreComsD |
Папка, содержащая компоненты XPCOM, относящиеся к GRE (Gecko Runtime Engine) |
GreD |
Папка, в которой установлен GRE |
Home |
Домашняя папка (каталог) текущего пользователя – в Windows: %HOME%; в UNIX: $HOME; в MacOS: папка документов |
TmpD |
Папка операционной системы для хранения временных файлов – в Windows: %TMP%; в UNIX: $TMP; в MacOS: папка временных файлов |
В таблице 16.14 перечислены псевдонимы, которые поддерживаются
только в системе Microsoft Windows. Константы CSIDL, приведенные в
таблице, являются частью программного интерфейса (API) Microsoft
Windows и используются в таких функциях Windows, как, например, SHGetFolderPath(). Каждый псевдоним соответствует известной папке.
Полное описание этих констант приведено в документе по адресу:
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp
| Псевдоним | Эквивалентная константа CSIDL | Псевдоним | Эквивалентная константа CSIDL |
|---|---|---|---|
AppData |
CSIDL_APPDATA |
netH |
CSIDL_NETHOOD |
Buckt |
CSIDL_BITBUCKET |
NetW |
CSIDL_NETWORK |
CmDeskP |
CSIDL_COMMON_DESKTOPDIRECTORY |
Pers |
CSIDL_PERSONAL |
CmPrgs |
CSIDL_COMMON_PROGRAMS |
PrntHd |
CSIDL_PRINTHOOD |
CmStrt |
CSIDL_COMMON_STARTUP |
Prnts |
CSIDL_PRINTERS |
Cntls |
CSIDL_CONTROLS |
Progs |
CSIDL_PROGRAMS |
DeskP |
CSIDL_DESKTOPDIRECTORY |
Rcnt |
CSIDL_RECENT |
DeskV |
CSIDL_DESKTOP |
SndTo |
CSIDL_SENDTO |
Drivs |
CSIDL_DRIVES |
Tmpls |
CSIDL_TEMPLATES |
Favs |
CSIDL_FAVORATES |
WinD |
CSIDL_WINDOWS |
Нужно иметь в виду, что использование псевдонимов с префиксом Cm в
однопользовательских версиях Windows, например Microsoft Windows 98,
приводит к возникновению
В таблице 16.15 приведены псевдонимы, поддерживаемые только на
платформе Macintosh. Прочие псевдонимы перечислены в таблице 16.16.
Псевдонимы, поддерживаемые в таких системах, как OS/2,
| Псевдоним | Папка | Псевдоним | Папка |
|---|---|---|---|
ApplMenu |
Меню (Apple Menu) | |
Папка расширений (Extensions folder) |
ClassicPrfs |
Папка профиля Mac Classic | Isrch |
Папка поиска в Internet |
CntlPnl |
Панель управления | |
Папка настроек (Preferences folder) |
DfltDwnld |
Папка загрузок по умолчанию (Default |
Shdwn |
Папка отключения ( |
Docs |
Папка документов | Trsh |
Папка мусорной корзины |
Desk |
Папка рабочего стола |
| Псевдоним | Описание соответствующего nsIFile |
|---|---|
Fnts |
Macintosh и Microsoft Windows: папка, содержащая |
LibD |
UNIX: /usr/local/lib/ |
Locl |
UNIX: /usr/local/ |
Strt |
Macintosh и Microsoft Windows: папка запуска ("Автозагрузка") |
SysD |
Только Macintosh OSX: системная папка |
UlibDir |
Только Macintosh OSX: /usr/lib |
Эти псевдонимы предоставляются источником, который подключается к
Платформа Mozilla не сводится к системе XPCOM. Последняя образует
ядро платформы, поверх которого реализовано множество компонентов и
инфраструктура, также составляющие часть платформы. В состав платформы
входят папки, в которые установлены браузеры и другие продукты,
система
| Псевдоним | Соответствующий объект | Путь относительно папки установки |
|---|---|---|
AppRegF |
Файл глобального реестра приложений | Расположен в другом месте (см. лекцию 17 "Развертывание") |
AppRegD |
Папка, содержащая глобальный реестр приложений | Расположена в другом месте (см. лекцию 17 "Развертывание") |
DefRt |
Папка верхнего уровня для параметров по умолчанию | Defaults |
PrfDef |
Папка, содержащая настройки по умолчанию | Defaults/pref |
profDef |
Папка, содержащая параметры профиля по умолчанию для текущих параметров локализации | Defaults/profile/{locale} |
ProfDefNoLoc |
Папка, содержащая параметры профиля по умолчанию для параметров локализации по умолчанию | Defaults/profile |
DefProtRt |
Папка верхнего уровня для пользовательских профилей | Расположена в другом месте (см. ниже) |
Ares |
Папка ресурсов | |
Achrom |
Папка |
|
SrchPlugns |
Папка модулей поиска | Searchplugins |
ApluginsDL |
Список доступных nsIEnumerator ) |
Plugins/* |
XPIClnupD |
Папка, содержащая программы для удаления приложений | |
UserPlugins |
Папка в составе профиля текущего пользователя, содержащая установленные модули уровня пользователя | Расположена в другом месте |
OSXUserPlugins |
Папка, содержащая пользовательские модули (только MacOS X) | Расположена в другом месте |
OSXLocalPlugins |
Папка, содержащая локальные модули (только MacOS X) | Расположена в другом месте |
MacSysPlugins |
Папка, содержащая системные модули (только Mac Classic) | Расположена в другом месте |
Псевдоним DefProtRt возвращает следующие значения в
зависимости от платформы:
Эти псевдонимы предоставляются источником, который подключается к
| Псевдоним | Объект | Путь относительно папки |
|---|---|---|
PrefD |
Папка, содержащая файл пользовательских настроек; совпадает с ProfD |
- |
PrefF |
Файл пользовательских настроек | prefs.js |
ProfD |
Корневая папка текущего профиля | - |
Uchrm |
Папка, содержащая данные |
|
LclSt |
Файл конфигурации окон Mozilla для данного пользователя | localstore. |
Uhist |
Файл истории посещений классического браузера | |
Upanels |
Файл вкладок для боковой панели классического браузера, определяемых пользователем | panels. |
UmimTyp |
Информация о типах |
mimeTypes. |
Bmarks |
Файл закладок классического браузера | |
Dloads |
Файл истории загрузок классического браузера | |
SrchF |
Файл конфигурации |
search. |
MailD |
Папка, содержащая данные локальных почтовых ящиков | |
ImapMD |
Папка, содержащая данные учетных записей |
ImapMail |
NewsD |
Папка, содержащая информацию о конференциях | News |
MFCaD |
Файл, содержащий настройки отображения папок классического |
panacea. |
Псевдонимы, относящиеся к модулям (plugins), предоставляются
источником, который подключается к
Прочие источники
Известные псевдонимы представлены в таблице 16.19.
| Псевдоним | Описание соответствующего nsIFile |
|---|---|
plugin. |
Файл модуля Java (Mozilla OJI) |
plugin. |
Файл модуля Adobe Acrobat plugin file |
plugin. |
Файл модуля Apple |
plugin. |
Исполняемый файл |
plugin. |
Папка модулей |
Скрипты позволяют изменять текущие настройки
@mozilla.org/preferences-service;1 nsIPrefService
По умолчанию скрипты, не установленные в
В этом разделе рассказано, как реализована система защиты информации на платформе Mozilla. Поскольку защита вообще представляет собой обширную тему, мы расскажем только о тех ограничениях защиты, которые непосредственно влияют на работу скриптов.
В браузерах
Фрагмент кода Mozilla может находиться в одном из четырех режимов
защиты: Web
Поддержка протокола WDSL, недавно реализованная в составе
платформы, привела к добавлению дополнительных мер защиты. Они
подразумевают дополнительную проверку при первом обращении к
удаленному Web-сервису – платформа должна запросить разрешение на его
использование. Это делается с целью защиты сервера, предоставляющего
Web-сервис, а не локальной платформы, которая обращается к нему. В
момент подготовки книги к печати предполагалось, что на стороне
клиента для такой проверки будет использоваться интерфейс nsIWebScriptsAccessService.
Большинство механизмов защиты, хотя и не все, реализованы в составе кода XPConnect, который обеспечивает доступ скриптов JavaScript к функциональности платформы Mozilla.
В любом случае, сообщение о попытке нарушения ограничений защиты выдается на консоль JavaScript.
Режим Web
Первая группа ограничений призвана обеспечить контроль пользователя над интерфейсом и сделать заметными любые действия с ним. Пример такого ограничения – требование, чтобы любые окна имели размер не менее 100 пикселей в ширину и высоту и, следовательно, были заметны для пользователя.
Второй
Принцип "общего источника" не позволяет скриптам
воздействовать на содержимое других окон приложения или даже на
содержимое других фреймов в том же окне, если это содержимое было
получено из других источников. Все содержимое
Принцип "общего источника" не распространяется на
специальный URL about:
Режим
Некоторая проблема заключается в том, что при добавлении к
Режим
Цифровые подписи и сертификаты – самостоятельная обширная тема,
поэтому здесь мы затронем лишь те ее аспекты, которые значимы для
прикладных скриптов. Для цифровой подписи скриптов и других ресурсов
используется утилита SignTool. Эта утилита не входит в состав Mozilla,
но доступна вместе с документацией на ресурсе компании
С использованием
Разница между режимами защиты
Режим защиты
user_pref("signed.applets.codebase_principal_support",
true);
Эта настройка полезна, главным образом, при разработке и отладке
приложений, которые будут выполняться в режиме защиты
Если приложение использует модель защиты
window.netscape.security.PrivilegeManager.enablePrivilege("
P1 P2 P3");
Эта функция запрашивает у пользователя подтверждение, открывая
диалоговое окно, и, в случае согласия, предоставляет скрипту
необходимые привилегии. Если соответствующее разрешение было дано
раньше и сохранено в настройках пользователя, привилегии
предоставляются без обращения к пользователю. При этом скрипт получает
не все права, соответствующие режиму
В данном примере "P1 P2 P3" представляет собой список
ключевых слов привилегий, разделенных пробелами. В вызове метода enablePrivilege должно быть использовано, по крайней
мере, одно ключевое слово. Допустимые ключевые слова, а также
функциональность, на которую распространяются соответствующие
привилегии, приведены в таблице
16.20. Во внутренней реализации платформы наличие необходимых
привилегий проверяется в разных местах, что обеспечивает высокий
уровень защиты.
| Ключевое слово | Функциональность |
|---|---|
UniversalBrowserRead |
Чтение содержимого из любых окон браузера; позволяет успешно проходить проверку "общего источника" для чтения любого документа |
UniversalBrowserWrite |
Изменение содержимого любых окон браузера; позволяет успешно проходить проверку "общего источника" для модификации любого документа |
UniversalXPConnect |
Неограниченный доступ к компонентам XPCOM из JavaScript при помощи XPConnect |
UniversalPreferencesRead |
Чтение настроек при помощи метода navigator.preference() |
UniversalPreferenceWrite |
Изменение настроек при помощи метода navigator.preference() |
CapabilityReferencesAccess |
Чтение и изменение настроек, определяющих политику защиты, включая информацию о том, какие привилегии были предоставлены скриптам, и в каких им было отказано; для этого также необходимы привилегии UniversalPreferencesRead и/или UniversalPreferencesWrite |
UniversalFileRead |
Отображение или отправка любых файлов, имеющих URL типа file: |
Данные, приведенные в таблице, любезно предоставлены Джесси Рудерманом и mozilla.org.
В режиме защиты Domain
С другой стороны, она является наиболее гибкой – при ее
использовании на выполнение скриптов может быть наложено как меньше,
так и больше ограничений, чем в режиме Web
Большинство параметров, связанных с режимом Domain
Данная система защиты реализована для следующих целей:
Чтобы использовать эту модель защиты, необходимо добавить к пользовательским настройкам определенные правила. Это можно сделать в три этапа: определить имя политики защиты; задать источники, к которым применима эта политика; задать правила доступа к конкретным объектам для данной политики. Ниже мы последовательно рассмотрим все эти этапы.
Существует три типа имен политик защиты – явные имена, групповое имя и политика по умолчанию. Эти имена образуют иерархию. Каждое свойство или объект, для которых может быть определено правило доступа, могут быть связаны с одним из имен каждого типа.
На нижнем уровне иерархии находятся политики по умолчанию. Для каждого свойства JavaScript существует одна политика по умолчанию, которая применяется в том случае, если для этого свойства не определено других политик. Если никакие политики по умолчанию в явном виде не определены, ко всем свойствам применяется единственная политика по умолчанию, имеющая имя "default". Вы можете изменять эту политику, как и другие политики по умолчанию. Из дальнейшего изложения станет ясно, почему целесообразно использовать несколько политик по умолчанию.
На следующем уровне находится
На вершине иерархии находятся политики, заданные явными именами. Их имена всегда должны быть определены в явном виде. Эти политики имеют приоритет над всеми прочими.
Для задания имен политик используются следующие настройки:
user_pref("capability.policy.policynames","p1 test foo");
user_pref("capability.policy.default_policynames","normal,off");
Имена политик не могут содержать символ точки, списки имен
разделяются пробелами или запятыми. В первой строке заданы имена трех
политик; во второй строке заданы имена двух политик по умолчанию.
Существует единственная
Определив имена политик, необходимо задать для каждой политики список источников. Каждая политика применяется лишь к документам, полученным из указанных для нее источников. Ниже приведен пример задания источников для политики с именем mypol:
user_pref("capability.policy.mypol.sites", "http://test.com http://x.org");
Значением свойства в данном случае является список URL, разделенных
пробелами или запятыми. URL может относиться к серверу в целом, но не
к отдельным его каталогам. Каждый сайт не может входить более чем в
одну строку настроек такого вида. Если указанная политика является
политикой по умолчанию, именно она будет политикой по умолчанию для
перечисленных сайтов. Это позволяет использовать для различных сайтов
различные политики по умолчанию. Если вы хотите задать сайты, к
которым должна применяться
После того, как заданы имена политик и соответствующие источники,
остается определить правила доступа. Существует три типа правил.
Каждому правилу соответствует одна строка в
Первый и наиболее общий тип синтаксиса правил применим ко всем свойствам JavaScript независимо от того, являются ли они простыми значениями или же методами. В случае политики mypol правило имеет следующий вид:
user_pref("capabilities.policy.mypol.Iface.Prop","Keywords")
Iface, Prop и
необходимо заменить соответствующими строками.
Iface – ChromeWindow, HTMLDocument и XULImageElement. Некоторые объекты Image, однако в данном случае должно использоваться полное
имя – HTMLImageElement.Prop – имя свойства, к которому относится правило
доступа. Как правило, оно представляет собой атрибут или метод
интерфейса XPCOM, например свойство value для многих элементов Keywords – разделенный пробелами или запятыми список
имен привилегий, приведенных в таблице
16.20 или одно из следующих ключевых слов: AllAccess, NoAccess
или sameOrigin. AllAccess эквивалентно указанию всех ключевых слов из
таблицы 16.20. sameOrigin означает, что будут применяться ограничения
режима Web NoAccess полностью запрещает доступ к свойству как на
чтение, так и на запись.Ниже приведен пример правила:
user_pref("capabilities.policy.*.History.back","NoAccess");
Это правило означает, что back() объекта nsIDOMHistory. Этот объект
используется только в браузере Mozilla, и приведенное правило
отключает функцию возврата к ранее просмотренным страницам.
Второй тип синтаксиса применяется только к атрибутам JavaScript
(свойствам, не являющимся методами) и позволяет разрешать или
запрещать операции
user_pref("capabilities.policy.mypol.Iface.Prop.Access","Keyword");
Iface и Prop имеют то же значение, что и в предыдущем случае. должно иметь одно из следующих значений: AllAccess, NoAccess
или sameOrigin. Access должно быть одной из двух строк – set или get.
Таким образом, этот синтаксис допускает два правила для каждого
свойства – для чтения и для записи. Ниже приведен пример правила,
которое запрещает изменять текст заголовка окон XUL:
user_pref("capabilities.policy.default.ChromeWindow.title.set","NoAccess");
Третий тип синтаксиса относится к JavaScript в целом. Он позволяет при помощи единственного правила полностью запретить или разрешить выполнение скриптов JavaScript для группы источников:
user_pref("capabilities.policy.mypol.javascript.enabled","Keyword");
В этом случае можно задать лишь имя политики и значение строки . Вместо можно поставить одно из двух значений: NoAccess (отключить JavaScript) или AllAccess (активизировать
JavaScript). Если JavaScript отключен глобально, правила такого типа
игнорируются.
Типичная политика содержит ряд правил такого рода, разрешающих или
запрещающих определенные действия. Если вы хотите запретить какое-либо
действие, будьте внимательны и задайте правила для всех свойств,
которые могут обеспечивать доступ к этому действию. Чтобы получить
полный список свойств, следует изучить нужный объект при помощи
Инспектора
Особенно тщательно следует подходить к ограничению доступа к
связкам XBL. Такая связка часто содержит множество вспомогательных
методов и свойств, функциональность которых может пересекаться.
Поэтому при необходимости запретить изменение какого-либо свойства
одного из
Согласно "
Следует упомянуть еще несколько ограничений, которые не относятся к описанным режимам защиты. Эти ограничения действуют, по крайней мере, начиная с версии Mozilla 1.4.
На этом обсуждение системы защиты платформы Mozilla заканчивается.
@mozilla.org/profile/manager;1 nsIProfile
Однако интерфейс nsIProfile не позволяет осуществлять доступ к файлам внутри заданного профиля. Для этого используется другая пара XPCOM:
@mozilla.org/profile/manager;1 nsIProfileInternal
Это практическое занятие посвящено
В этом разделе мы завершим работу над приложением NoteTaker,
добавив к нему механизмы сохранения, удаления и загрузки заметок. Для
этого нам нужно создать необходимые источники данных
Мы также попытаемся усовершенствовать запросы
Как подобает при разработке программ, мы начнем с проектирования.
В лекции 14 "Шаблоны" мы сделали содержимое динамическим,
используя атрибуты элементов XUL. Для каждого шаблона был определен
собственный источник данных. Хотя это простой и удобный способ работы
с источниками, он предполагает, что разработчику известен точный URL
файла notetaker.
Чтобы учесть это изменение, мы перенесем интеграцию с данными
При описании шаблонов нам все равно понадобится указать источник
данных в коде XUL. Для этого мы используем "нулевой"
источник , предоставляемый платформой Mozilla. После загрузки
шаблонов мы создадим новый источник данных на основе объекта,
представляющего URL, и подключим его к каждому из шаблонов с помощью
JavaScript. В этом случае весь обмен данными
Это не единственный метод создания источника данных, совместно используемого несколькими шаблонами XUL. Если два шаблона имеют один и тот же атрибут, определяющий источник данных, оба они будут совместно использовать один набор фактов (общее хранилище фактов). В противном случае код, написанный нами в лекции 14, просто не смог бы корректно работать. Поэтому здесь мы всего лишь отделяем использование общего источника данных от XUL, чтобы иметь возможность работать с этим источником в виде отдельного объекта. В принципе, мы могли бы создать отдельный источник данных для каждого шаблона. Даже разные источники, основанные на одном и том же URL, фактически работают с одним общим набором фактов.
Получив объект источника данных, мы можем читать и записывать
данные при помощи функций JavaScript, а также многочисленных
интерфейсов для работы с
Чтобы усовершенствовать нашу объектную модель, мы дополним объект Note объектом NoteDataSource, представляющим
источник данных. В последний раз мы работали с объектом Note в
практическом разделе лекции 14. Всякий раз, когда нам нужно добавить
новую операцию с источником данных, мы можем реализовать ее как метод
нашего нового объекта.
Прежде всего, следует поместить копию файла notetaker.
Необходимое условие успешного создания источника данных – получение доступа к этому файлу. Мы начинаем, зная точное имя файла и имея представление о его местонахождении, а закончить должны, имея объект nsIRDFDataSource. Мы включим в код приложения имя файла, но не путь к нему. Чтобы создать источник данных, мы используем некоторые приемы, описанные в этой лекции.
Чтобы обнаружить файл независимо от платформы, мы используем службу
каталогов Mozilla. Изучая таблицы псевдонимов, приведенные выше в этой
лекции, мы обнаруживаем, что псевдоним ProfD, приведенный
в таблице 16.18, позволяет получить доступ к папке текущего профиля. На
основе этого псевдонима мы создадим объект nsIFile, представляющий
папку профиля, дополним путь к папке, чтобы получить нужный файл,
преобразуем файловый объект в URL и наконец создадим источник данных
на основе этого URL. Все эти операции представлены в листинге
16.9.
var Cc = Components.classes;
var Ci = Components.interfaces;
// Объект сеанса работы с NoteTaker
function NoteSession() {
this.init();
}
NoteSession.prototype = {
config_file : "notetaker.rdf",
datasource : null,
init : function (otherfile) {
var fdir, conv, rdf, file, url;
if (otherfile) this.config_file = otherfile;
with (window) {
fdir = Cc["@mozilla.org/file/directory_service;1"];
fdir = fdir.getService(Ci.nsIProperties);
conv = Cc["@mozilla.org/network/protocol;1?name=file"];
conv = conv.createInstance(Ci.nsIFileProtocolHandler);
rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
rdf = rdf.getService(Ci.nsIRDFService);
}
file = fdir.get("ProfD", Ci.nsIFile);
file.append(this.config_file);
if (!file.exists())
throw this.config_file + " is missing";
if (!file.isFile() || !file.isWritable() || !file.isReadable())
throw this.config_file + " has type or permission problems";
url = conv.newFileURI(file);
this.datasource = rdf.GetDataSource(url.spec);
}
};
var noteSession = new NoteSession();
Вся работа выполняется в методе init() объекта NoteSession. Сначала
мы создаем три объекта XCOM. Затем мы получаем папку текущего профиля
в виде объекта nsIFile. Метод , не возвращающий никакого
результата, изменяет этот объект так, что он в точности соответствует
нашему конфигурационному файлу. Затем мы убеждаемся, что нужный файл
существует, и что он доступен для чтения и записи. Предполагается, что
в нашем случае такой файл существует всегда, поскольку он будет
создаваться при установке приложения. Однако к реальному приложению
все же следовало бы добавить код, автоматически создающий файл в
случае его отсутствия – нельзя исключить возможность случайного
nsIFile в nsIURL с помощью метода newFileURI(), получаем URL в виде строки, используя свойство , и,
наконец, создаем источник данных на основе этой строки при помощи
метода GetDataSource().
Эта последовательность шагов – стандартные действия при подготовке
источников данных. При использовании внутреннего или удаленного
источника действия могут несколько отличаться от приведенного примера.
Так, если URL источника данных известен заранее, вся процедура может
свестись к вызову метода GetDataSource().
Теперь, когда мы получили источник данных, давайте используем его.
Мы хотим модифицировать существующие шаблоны так, чтобы они получали
данные от созданного нами объекта, а не из файла, указанного в коде
шаблона. Поэтому в коде шаблона мы укажем "нулевой" источник
данных , а реальный источник подключим
при помощи скрипта.
Мы полностью отказываемся от использования шаблона для текстового
поля <textbox> в панели инструментов NoteTaker. Это слишком
сложное решение для простого текстового поля. В предыдущих лекциях мы
использовали его с единственной целью: продемонстрировать один из
возможных способов работы с шаблонами. Однако шаблоны не являются
универсальным инструментом. Итак, код для текстового поля принимает
следующий вид:
<textbox id="notetaker-toolbar.summary"/>
Поле заполняется с помощью функции refresh_toolbar(), которая
копирует нужное значение из объекта заметки. Таким образом, задача
обновления текстового поля решена без обращения к шаблонам.
Раскрывающийся список ключевых слов в панели инструментов основан на стандартном использовании шаблона, и мы могли бы оставить его без изменений, если бы источник данных можно было указать в коде XUL. Однако, поскольку теперь местонахождение файла заметок нам заранее неизвестно, мы должны изменить код XUL и JavaScript.
Содержимое этого списка генерировалось на основе данных, начиная с
лекции 14 "Шаблоны", однако оно было "недостаточно
динамическим". Список заполнялся в момент создания страницы XUL и
с этого момента оставался неизменным. Теперь он должен обновляться
всякий раз при добавлении нового ключевого слова. Любое добавление или
удаление содержимого XUL может вызвать полную перерисовку документа,
включая список. Перерисовка выполняется автоматически, однако для
сложных тегов, подобных <menulist>, она может выполняться
неправильно. Необходимо использовать XUL аккуратно, иначе список после
перерисовки будет выглядеть некорректно.
Чтобы отслеживать ошибки при перерисовке, обратимся к коду для тега <menulist> и шаблона, который представлен в листинге 16.10. В
качестве источника данных в этом коде указан ".
<menulist id="notetaker-toolbar.keywords" editable="true">
<menupopup datasources="rdf:null" ref="urn:notetaker:keywords">
<template>
<menuitem uri="rdf:*"
label="rdf:http://www.mozilla.org/notetaker-rdf#label"/>
</template>
</menupopup>
</menulist>
В данном случае в состав шаблона входят только теги <menuitem>. Если источником данных является ", код XUL, полученный в результате обработки
шаблона, будет иметь следующий вид:
<menulist id="notetaker-toolbar.keywords" editable="true"> <menupopup datsources="rdf:null" ref="urn:notetaker:keywords"> </menupopup> </menulist>
Панель инструментов, построенная на основе такого кода, показана на рисунке 16.2.
(рис 16.2) Отображение списка с нулевым количеством элементовЭтот интерфейс имеет существенные недостатки, как в части
отображения, так и в части взаимодействия с пользователем. Мы могли бы
проигнорировать эти проблемы, понадеявшись на то, что при отображении
документа (событие onload ) к нему будет подключен созданный нами
источник данных, на основе которого будут созданы элементы <menuitem> для списка.
К сожалению, дела обстоят не так хорошо. Размер содержимого списка
определяется тегом <menupopup>, в состав которого входит фрейм.
С момента создания размер этого фрейма не изменяется динамически, хотя
в последующих версиях платформы ситуация может измениться. Это
означает, что после первоначального отображения раскрывающегося списка
его размер не будет изменяться, несмотря на изменение шаблона,
определяющего содержимое списка.
Усовершенствованный вариант кода, позволяющий обойти эту проблему, представлен в листинге 16.11:
<menulist id="notetaker-toolbar.keywords"
editable="true"
datasources="rdf:null"
ref="urn:notetaker:keywords"
>
<template>
<menupopup>
<menuitem uri="rdf:*"
label="rdf:http://www.mozilla.org/notetaker-rdf#label"/>
</menupopup>
</template>
</menulist>
В этом варианте тег <template> поднят на один уровень
иерархии, так что тег <menupopup> оказался вложенным в него. В
результате <menupopup> будет генерироваться заново при каждом
обновлении шаблона. При этом будет создаваться лишь одна пара тегов,
поскольку <menupopup> находится снаружи тега, в котором указан
источник данных (атрибут ). Вспомним, что при обработке шаблона
именно тег, имеющий атрибут , вместе со своим содержимым создается
многократно – по числу элементов, возвращаемых в результате запроса.
Поскольку теперь <menupopup> генерируется заново при каждом
обновлении содержимого списка, раскрывающийся список будет иметь
корректный размер. Это рекомендуемый подход для создания
раскрывающихся списков на основе шаблонов, если содержимое списка
может динамически изменяться после первоначального отображения.
Даже с учетом сделанных исправлений возможна еще одна проблема с отображением раскрывающегося списка, создаваемого на основе шаблона, хотя эта проблема не затрагивает нашего приложения.
На рисунке 16.3 показана тестовая панель до и после однократного щелчка по кнопке, вызывающей раскрытие списка, – сверху и снизу соответственно.
(рис 16.3) Проблема с перерисовкой раскрывающегося списка, основанного на шаблоне.В этом примере текстовое поле в верхней части раскрывающегося
списка первоначально имеет ширину по умолчанию для тега <textbox>. При щелчке по кнопке раскрывается список с
элементами, и текстовое поле перерисовывается заново с новой шириной,
которая определяется самым широким элементом списка. В результате
ширина элемента управления скачкообразно изменяется, что является
недостатком пользовательского интерфейса. Чтобы решить эту проблему,
достаточно в явном виде указать ширину (атрибут width ) для тега <menulist>. К счастью, эта проблема не затрагивает NoteTaker, по
крайней мере, при отображении реальных Web-страниц.
Необходимые изменения кода JavaScript очень просты. В функциях refresh_toolbar() и init_toolbar() следует подключить источник данных
к шаблону раскрывающегося списка. Эти изменения представлены в
листинге 16.12.
// слушатели onload работают в фазе перехвата
window.addEventListener("load", init_handler, true);
// загрузить содержимое RDF для панели инструментов. Используется объект заметки (note).
function init_toolbar(origin)
{
if ( origin != "timed" ) {
// избежать выполнения внутри любого обработчика onload
setTimeout("init_toolbar('timed')",1);
}
else
{
var menu = window.document.getElementById('notetakertoolbar.keywords');
menu.database.AddDataSource(noteSession.datasource);
menu.ref = 'urn:notetaker:keywords';
setInterval("content_poll()", 1000);
}
}
// обновить панель инструментов на основе текущей заметки.
function refresh_toolbar()
{
var box = document.getElementById('notetaker-toolbar.summary');
box.value = note.summary;
var menu = document.getElementById('notetaker-toolbar.keywords');
menu.ref = 'urn:notetaker:keywords';
}
Варианты этих функций, приведенные в практическом разделе лекции 14
"Шаблоны", вели себя беспокойно, вызывая при
малейшем изменении данных шаблона. В данном случае, однако, это
излишне, поскольку шаблоны основаны на источнике данных типа xml-,
который сам инициирует все необходимые действия при
изменении шаблона. Однако если при разработке собственного приложения
вы не знаете точно, стоит ли вызвать , всегда вызывайте эту
функцию.
Обновленная функция init_toolbar(), приведенная в листинге,
подключает источник данных к шаблону раскрывающегося списка, обновляет
свойство ref шаблона, а также обеспечивает периодическое выполнение
функции content_poll(), которая следит за изменениями URL содержимого
браузера. Даже если значение свойства ref в результате присваивания не
изменилось, выполняется запрос к источнику данных и список строится
заново на основе результатов запроса.
Вызов , как и раньше, представляет собой меру
предосторожности на случай непредвиденных проблем в обработчике
события onload. Функцию refresh_toolbar() можно сравнить с методом Refresh() интерфейса nsIRDFRemoteDataSource. Этот метод обновляет
хранилище фактов, лежащее в основе источника данных. Функция refresh_toolbar() обновляет только содержимое XUL, включая содержимое,
основанное на шаблоне.
На этом мы завершаем обсуждение изменений в коде панели инструментов, относящихся к отображению данных. Мы вернемся к панели инструментов, когда речь пойдет об обработке данных, вводимых пользователем.
Диалоговое окно Edit (окно редактирования заметки) – еще одна часть
приложения NoteTaker, использующая шаблоны. Источники данных для этих
шаблонов также должны динамически подключаться с использованием
скриптов JavaScript. Панель Edit (Правка) диалогового окна не содержит
никаких шаблонов. Панель (Ключевые слова) использует шаблоны
для заполнения двух элементов – <listbox> и <tree>.
Процедура подключения источников данных к этим шаблонам очень
похожа на использованную для панели инструментов. Мы заменяем на в двух местах документа editDialog.xul. Затем мы
создаем новую функцию init_dialog() в файле dialog_action.js и
модифицируем функцию refresh_dialog().
Результаты модификации скриптов приведены в листинге 16.13.
window.addEventListener("load", init_dialog, "true");
function init_dialog()
{
if ( origin != "timed" ) {
// избежать выполнения внутри любого обработчика onload
setTimeout("init_dialog('timed')",1);
}
else
{
var listbox = document.getElementById('notetaker.keywords');
listbox.database.AddDataSource(window.opener.noteSession.datasource);
var tree = document.getElementById('notetaker.related');
tree.database.AddDataSource(window.opener.noteSession.datasource);
refresh_dialog();
}
}
function refresh_dialog()
{
var listbox = document.getElementById('dialog.keywords');
listbox.ref = window.opener.note.url;
//listbox.ref = "http://saturn/test1.html"; // для тестирования
var tree = document.getElementById('dialog.related');
tree.ref = window.opener.note.url;
//tree.ref = "http://saturn/test1.html"; // для тестирования
}
Функция init_dialog(), добавляющая один источник данных к двум
шаблонам, практически идентична функции init_toolbar(). Функция refresh_dialog() также аналогична функции refresh_toolbar() и для
целей тестирования содержит некоторые URL, находящиеся в файле
notetaker.
В практическом разделе лекции 13 "Списки и деревья" мы
экспериментировали с динамическим заполнением списков при помощи
интерфейсов
В результате всех этих изменений все шаблоны NoteTaker могут
работать с файлом notetaker.
Механизм шаблонов XUL – лишь один из способов формировать запросы к
набору фактов
В приложении NoteTaker используется один запрос, который было бы разумно реализовать на основе скрипта. Это запрос, выполняющий поиск заметки, существующей для данного URL. Шаблоны не подходят для решения этой задачи по следующим причинам:
Поиск выполняется при помощи метода объекта заметки в
файле notes.js. Мы уже создали каркас этого метода в предыдущих
лекциях, а теперь добавим его полную реализацию. Метод загружает данные
resolve : function (url) {
var ds = window.noteSession.datasource;
var ns = "http://www.mozilla.org/notetaker-rdf#";
var rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
rdf = rdf.getService(Ci.nsIRDFService);
var container = Cc["@mozilla.org/rdf/container;1"];
container = container.getService(Ci.nsIRDFContainer);
var cu = Cc["@mozilla.org/rdf/container-utils;1"];
cu = cu.getService(Ci.nsIRDFContainerUtils);
var seq_node = rdf.GetResource("urn:notetaker:notes");
var url_node = rdf.GetResource(url);
var chopped_node = rdf.GetResource(url.replace(/\?.*/,""));
var matching_node, prop_node, value_node;
if (!cu.IsContainer(ds,seq_node)) {
throw "Missing <Seq> 'urn:notetaker:notes' in " + noteSession.config_file;
return;
}
container.Init(ds,seq_node);
// Сначала попробовать полный URL, потом усеченный URL, если не найдены – заметки нет
if ( container.IndexOf(url_node) != -1) {
matching_node = url_node;
this.url = url;
this.chop_query = false;
}
else if ( container.IndexOf(chopped_node) != -1 ) {
matching_node = chopped_node;
this.url = url.replace(/\?.*/,"");
}
else {
this.url = null;
return;
}
else
return;
// Если заметка найдена, получить все ее свойства.
var props = ["summary", "details", "width", "height", "top", "left"];
for (var i=0; i<props.length; i++)
{
pred_node = rdf.GetResource(ns + props[i]);
value_node = ds.GetTarget(matching_node, pred_node, true);
value_node = value_node.QueryInterface(Ci.nsIRDFLiteral);
this[props[i]] = value_node.Value;
}
}
Прежде всего, метод создает три служебных объекта XPCOM для работы
с nsIRDFService используется для преобразования простой
строки URL в объект nsIRDFResource, являющийся nsIRDFNode. Большинство методов для работы с nsIRDFNode. Мы
создаем такие объекты как для полных, так и для усеченных URL.
Единственное применение объекта nsIContainerUtils в нашем методе –
убедиться, что в файле notetaker., который является контейнером. Если это условие не
выполнено, работа метода прерывается и генерируется исключение. Затем
мы используем интерфейс nsIRDFContainer для того, чтобы связать <Seq> ) с источником данных и инициализировать эту
связь. Как правило, доступ к источнику данных осуществляется путем
обращения к отдельным фактам. Интерфейс nsIRDFContainer позволяет
работать с контейнером и его элементами как со структурой данных.
После инициализации служебных объектов мы переходим к выполнению собственно запроса, который в данном случае несложен. Его алгоритм таков: найти в источнике данных ресурс, соответствующий полному URL; если таковой отсутствует, найти ресурс, соответствующий URL без параметров; если и этот поиск завершился неудачей, отказаться от дальнейшего выполнения метода.
В оставшейся части метода мы извлекаем все факты, описывающие пары
свойство/значение для данной заметки. Метод всегда
возвращает объект nsIRDFNode, который необходимо преобразовать к тому
типу, который мы используем для свойств заметки. Во всех случаях это
простая строка (мы не храним размеры окна как целые). Наконец,
полученные значения свойств присваиваются свойствам объекта заметки.
Эта часть кода предполагает, что заметка описана в файле notetaker.
Запрос, выполняемый в этом методе, основан на двух фактах и в целом аналогичен тем простым запросам, которые мы применяли в шаблонах, за исключением некоторых проверок в начале метода и использования двух различных URL для поиска.
Если попытаться протестировать метод , например, добавив
код вида:
note.resolve("http://saturn/test1.html");
Система, скорее всего, выдаст неожиданные сообщения об ошибках. Как
правило, ошибки такого рода возникают при первом обращении к
интерфейсам для работы с noteSession. Источник данных
инициализируется в функции init() в составе этого объекта следующим
образом:
this.datasource = GetDataSource(url.spec);
Этот способ инициализации не подходит для наших задач. Он
осуществляет асинхронную загрузку данных, поэтому хранилище данных
заполняется постепенно. Тем временем скрипт переходит к выполнению
дальнейших инструкций и, возможно, пытается запросить данные до того,
как они загружены. Поэтому неудивительно, что методы
this.datasource = GetDataSourceBlocking(url.spec);
Это может привести к незначительной задержке при загрузке браузера, однако допустимо для нашего простого приложения.
Впрочем, мы могли бы избежать этой задержки, не отказываясь от
асинхронной загрузки, но используя дополнительные интерфейсы nsIRequestObserver или nsIStreamListener компонента xml-. С
помощью наблюдателя или слушателя мы могли бы определить момент
завершения загрузки. Некоторые из объектов XPCOM, созданных для этой
цели, могли бы найти применение и в других методах. Сделав эти объекты
доступными через объект noteSession, мы могли бы обеспечить
возможность их повторного использования. Дальнейшие подробности
реализации этой стратегии выходят за рамки данной книги.
В предыдущих лекциях мы написали код, который помещает данные
объекта заметки в поля формы или HTML-документ, отображаемый в окне
браузера. В этой лекции мы подключили объект заметки к хранилищу фактов
content_poll()
находящуюся в файле toolbar_action.js. Необходимо заменить эту
строку:
display_note()
на следующую:
if (note.url != null ) display_note()
Это позволит не отображать никаких дополнительных элементов для страниц, для которых не созданы заметки. Так гораздо лучше!
NoteTaker позволяет не только просматривать уже сохраненные данные
заметок, но и изменять или добавлять их. Последний раз мы работали с
вводом данных в лекции 7 "Формы и меню", где введенные данные
пересылались на Web-сервер. В этой лекции мы будем сохранять данные в
хранилище файлов
Для наших целей сохранить данные означает добавить их к источнику
данных. После этого они будут находиться в оперативной памяти до тех
пор, пока пользователь не выполнит какие-либо действия, приводящие к
синхронизации хранилища фактов с файлом, или до завершения работы
платформы. Какие именно действия пользователя должны приводить к
Пользователь может вводить и изменять данные как в панели инструментов NoteTaker, так и в диалоговом окне. Мы последовательно рассмотрим эти варианты, начав с панели инструментов.
Пользователь может легко создать или изменить заметку, используя
поля аннотации (summary) и ключевых слов (Edit, Save или Delete ), происходить
ничего не должно. Поэтому данные, измененные пользователем, могут
обрабатываться при помощи команд, доступных из панели инструментов.
Прибегать к обработчикам событий или другим подобным
механизмам не требуется.
Панель Edit диалогового окна в этом отношении аналогична панели
инструментов. Изменения, сделанные пользователем, должны сохраняться
лишь в том случае, если пользователь нажимает кнопку OK. Если
пользователь закрывает окно, нажав кнопку Cancel, изменения
сохраняться не должны. Однако ситуация с панелью
Панель позволяет пользователю добавить или удалить любое
количество ключевых слов, используя соответствующие кнопки. Проблема
состоит в следующем: где должны храниться эти изменения, пока
диалоговое окно не закрыто? Если пользователь, в конечном счете,
нажимает Cancel, добавленные слова не должны быть сохранены. Если
пользователь принимает сделанные изменения, они должны быть
сохранены.
Однако мы хотим, чтобы в процессе работы пользователя с окном
вносимые изменения отображались в соответствующих элементах <listbox> и <tree>. Это означает, что ключевые слова
должны добавляться к хранилищу
Таким образом, мы столкнулись с проблемой отмены сделанных действий, иными словами – отката транзакций. Мы хотим немедленно добавлять ключевые слова к источнику данных, чтобы они были доступны всем элементам, использующим этот источник, однако нам необходимо иметь возможность удалить их, если пользователь, в конце концов, не принимает сделанных изменений. Для решения этой проблемы мы реализуем новый контроллер команды. Этот контроллер будет записывать выполняемые операции с ключевыми словами в буфер отмены. Если будет получена команда на отмену транзакции, контроллер, используя сохраненную информацию, сможет вернуть источник данных к исходному состоянию. Платформа Mozilla не предоставляет готового объекта с такой функциональностью, но в большинстве случаев его реализация не должна вызывать затруднений.
Таким образом, вся обработка пользовательских данных основана на
инфраструктуре команд. Это простое и изящное архитектурное решение.
Данные пребывают в полях форм, не вызывая какой-либо активности
программы, до тех пор, пока пользователь не запускает одну из команд.
Эта команда может поместить данные в хранилище фактов, после чего они
станут доступны всему приложению, в частности, любым шаблонам. Если
команда также ответственна за
Теперь мы обратимся к конкретному коду, который обеспечивает
сохранение на диске введенных пользователем данных. Мы усовершенствуем
функции action(), вызываемые из панели инструментов и диалогового
окна, а также реализуем дополнительный контроллер команды для работы с
ключевыми словами в диалоговом окне.
Функция action() панели инструментов поддерживает команды notetaker-open-, notetaker-save, notetaker-display и notetaker-delete. Только команды -save и -delete связаны с изменением
содержимого notetaker-save.
function action(task)
{
var ns = "http://www.mozilla.org/notetaker-rdf#";
var rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
rdf = rdf.getService(Ci.nsIRDFService);
var container = Cc["@mozilla.org/rdf/container;1"];
container = container.getService(Ci.nsIRDFContainer);
var url_node;
// ... реализация других команд удалена ...
if ( task == "notetaker-save" )
{
var summary = document.getElementById("notetaker-toolbar.summary");
var keyword = document.getElementById("notetaker-toolbar.keywords");
var update_type = null;
if ( note.url != null )
{
if ( keyword.value != "" || summary.value != note.summary )
{
update_type = "partial"; // существующая заметка: обновить аннотацию, ключевые слова
url_node = rdf.GetResource(note.url);
}
}
else if ( window.content window.content.document window.content.document.visited )
{
update_type = "complete"; // a new note
url_node = window.content.document.location.href;
url_node = url_node.replace(/\?.*/,""); // toolbar chops any query
url_node = rdf.GetResource(url_node);
}
if ( update_type == "complete" )
{
// добавить url заметки к контейнеру
var note_cont = rdf.GetResource("urn:notetaker:notes");
container.Init(noteSession.datasource,note_cont);
container.AppendElement(url_node);
// добавить поля заметки, за исключением ключевых слов
var names = ["details", "top", "left", "width", "height"];
var prop_node, value_node;
for (var i=0; i < names.length; i++)
{
prop_node = rdf.GetResource(ns + names[i]);
value_node = rdf.GetLiteral(note[names[i]]);
noteSession.datasource.Assert(url_node, prop_node, value_node, true);
}
}
if ( update_type != null)
{
// обновить/добавить аннотацию
var summary_pred = rdf.GetResource(ns + "summary");
var summary_node = rdf.GetLiteral(summary.value);
noteSession.datasource.Assert(url_node, summary_pred, summary_node, true);
// начать работу с новым ключевым словом
var keyword_node = rdf.GetResource("urn:notetaker:keyword:" + keyword.value);
var keyword_value = rdf.GetLiteral(keyword.value);
// сделать ключевое слово связанным с одним из ключевых слов для данной заметки
var keyword_pred = rdf.GetResource(ns + "keyword");
var related_pred = rdf.GetResource(ns + "related");
var keyword2 = noteSession.datasource.GetTarget(url_node, keyword_pred, true);
if (keyword2)
noteSession.datasource.Assert(keyword_node, related_pred, keyword2, true);
// добавить ключевое слово к данной заметке
noteSession.datasource.Assert(url_node, keyword_pred, keyword_node, true);
// добавить текст ключевого слова
var label_pred = rdf.GetResource(ns + "label");
noteSession.datasource.Assert(keyword_node, label_pred, keyword_value, true);
// добавить ключевое слово к контейнеру, содержащему все ключевые слова
var keyword_cont = rdf.GetResource("urn:notetaker:keywords");
container.Init(noteSession.datasource,keyword_cont);
container.AppendElement(keyword_node);
}
// записать на диск
noteSession.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource)
.Flush();
note.resolve();
display_note();
}
Этот код содержит эквивалент одной транзакции применительно к
данным note и noteSession, а также
состояния текущего URL принимается решение о том, существует ли уже
заметка для данного URL. Чтобы облегчить себе задачу, мы используем
некоторые данные, полученные функцией content_poll(), например
значение свойства visited.
Если заметка уже существует, сохранение подразумевает частичное
обновление уже существующих фактов. Если же заметки для данного URL
еще нет, необходимо создать ее, что подразумевает создание всех
необходимых фактов. Если новая заметка создается при помощи панели
инструментов, мы удаляем все параметры запроса GET из URL. Значение,
указывающее на характер необходимого обновления, присваивается
переменной update_type.
Поскольку частичное обновление представляет собой подмножество полного обновления (создания новой заметки), существует часть кода, которая выполняется при любом обновлении. Ветвь кода, следующая за оператором:
if ( update_type == "complete" )
содержит действия, необходимые для полного обновления, за исключением общей части. Общая часть содержится в следующей ветви, которая выполняется при любом типе обновления:
if (update_type != null )
Рассмотрим каждую из ветвей поочередно.
При создании новой заметки сначала мы получаем доступ к контейнеру , а затем добавляем к нему URL, соответствующий
заметке. Затем создаются факты для каждого из свойств заметки, кроме
аннотации и ключевых слов. Любые строки перед передачей интерфейсам
nsIRDFNote или его
Затем вызывается код для частичного обновления. Он выполняется
всегда за исключением случаев, когда новую заметку создать нельзя.
Заметку невозможно создать, например, для URL about:, который не выполняет
никаких действий, если такой факт уже существует. Поскольку в
нормальных условиях в хранилище не должно существовать несколько копий
одного и того же факта, для добавлении новых фактов рекомендуется
использовать этот метод.
Затем код выполняет более сложную работу по добавлению ключевого
слова. Если заметке уже были присвоены ключевые слова, мы хотим, чтобы
новое слово было связано с остальными. Для этого мы должны добавить
факт, связывающий новое слово с одним из ранее присвоенных слов. Для
этого мы пытаемся найти ключевое слово, уже присвоенное данной
заметке, и в случае успеха добавляем факт, связывающий его с новым
словом. Мы делаем это перед тем, как связать новое ключевое слово с
заметкой, чтобы избежать ситуации, в которой новое слово окажется
связанным с самим собой. Логика оставшегося кода прямолинейна – мы
добавляем ключевое слово к заметке, к контейнеру ключевых слов и, наконец, сохраняем само слово (его
значение).
Таким образом, в этой ветви кода мы добавили пять фактов – один для
аннотации и четыре для ключевых слов. Поскольку источник данных
основан на полноценном xml-, эти изменения будут
автоматически переданы всем шаблонам, использующим данный источник.
Затем мы вызываем метод , чтобы записать состояние источника
данных на локальный диск. Имейте в виду, что этот метод полностью
переписывает файл notetaker.note и отображаем данные заметки, чтобы привести структуры
данных JavaScript и пользовательский интерфейс в соответствие с
данными notetaker-save.
Команда notetaker-delete реализуется аналогичным образом.
Наибольшую сложность при этом представляет определение того, какие из
ключевых слов, связанных с заметкой, могут быть удалены, а какие
нужны для других заметок. Это требует анализа многих возможных
вариантов, и мы не будем рассматривать их здесь. С кнопкой Delete
(Удалить) на панели связана аналогичная логика; мы обсудим ее
ниже.
Функция action() диалогового окна Edit поддерживает следующие
команды: notetaker-, notetaker-, notetaker-save, notetaker-load и notetaker-close-. Из них лишь команда notetaker-save требует работы с данными notetaker-save
панели инструментов.
if (task == "notetaker-save")
{
var field, widget, note = window.opener.note;
for (field in note)
{
widget = document.getElementById("dialog." + field.replace(/_/,"-"));
if (!widget) continue;
if (widget.tagName == "checkbox")
note[field] = widget.checked;
else
note[field] = widget.value;
}
window.opener.setTimeout('execute("notetaker-save")',1);
}
Последняя инструкция этого фрагмента представляет собой вызов
команды notetaker-save панели инструментов. Мы не можем вызвать метод window.opener.execute() непосредственно, поскольку в этом случае
функция будет выполняться в контексте диалогового окна, а не окна
браузера. Обращаясь к методу окна браузера, мы
обеспечиваем необходимый контекст выполнения функции.
Наконец, нам нужны дополнительные команды для работы с ключевыми
словами в панели диалогового окна Edit. Эти команды будут
объединены в контроллер, поддерживающий принятие или отмену изменений
(фиксацию и откат транзакций). Контроллер будет поддерживать следующие
команды: notetakerkeyword-add, notetaker-, notetaker- и notetaker-. Поскольку эти команды
тесно связаны друг с другом и используют общие данные, их
нецелесообразно реализовывать по отдельности в составе функции action(). Вместо этого мы реализуем их непосредственно в составе
контроллера, создав специально для этого новый файл
keywordController.js. Общая структура контроллера показана в листинге
16.17.
var keywordController = {
_cmds : { },
_undo_stack : [],
_rdf : null,
_ds : null,
_ns : "http://www.mozilla.org/notetaker-rdf#",
_related : null,
_label : null,
_keyword : null,
init : function (ds) { ... initialize ... },
_LoggedAssert : function (sub, pred, obj) { ... },
_LoggedUnassert : function (sub, pred, obj) { ... },
supportsCommand : function (cmd)
{ return (cmd in this._cmds); },
isCommandEnabled : function (cmd) { return true; },
onEvent : function (cmd) { return true; },
doCommand : function (cmd) {
... подготовительные операции ...
switch (cmd) {
case "notetaker-keyword-add":
case "notetaker-keyword-delete":
case "notetaker-keyword-commit":
case "notetaker-keyword-undo-all":
}
}
};
keywordController.init(window.opener.noteSession.datasource);
Как и любой контроллер команд, этот контроллер поддерживает четыре
стандартных метода, начиная с supportsCommand(). Метод doCommand()
выполняет различные действия в зависимости от переданного имени
команды; код в нем организован при помощи оператора case. Контроллер
также поддерживает ряд других действий. В массиве _undo_stack
сохраняются действия, которые могут быть отменены. Реализованные нами
методы _LoggedAssert() и _LoggedUnassert() аналогичны стандартным
методам для работы с и Unassert(), однако кроме этого они
выполняют _undo_stack. Давайте начнем анализ контроллера с метода init(), который показан в листинге 16.18:
init : function (ds) {
this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
this._rdf = this._rdf.getService(Ci.nsIRDFService);
this._ds = ds;
this._related = this._rdf.GetResource(this._ns + "related");
this._label = this._rdf.GetResource(this._ns + "label");
this._keyword = this._rdf.GetResource(this._ns + "keyword");
window.controllers.insertControllerAt(0,this);
},
Этот метод создает доступные для контроллера ссылки на ряд полезных
объектов – службу
Реализация двух следующих функций – _LoggedAssert() и _LoggedUnassert() – иллюстрирует, каким образом контроллер может
сохранять информацию о выполняемых командах. В данном случае речь идет
об истории добавления и отмены фактов
Реализация двух этих функций показана в листинге 16.19.
_LoggedAssert : function (sub, pred, obj)
{
if ( !this._ds.HasAssertion(sub, pred, obj, true))
{
this._undo_stack.push( { assert:true, sterm:sub,
pterm:pred, oterm:obj } );
this._ds.Assert(sub, pred, obj, true);
}
},
_LoggedUnassert : function (sub, pred, obj)
{
if ( this._ds.HasAssertion(sub, pred, obj, true))
{
this._undo_stack.push( { assert:false, sterm:sub,
pterm:pred, oterm:obj } );
this._ds.Unassert(sub, pred, obj, true);
}
},
Эти функции представляют собой замену стандартных методов nsIRDFDataSource. и nsIRDFDataSource.Unassert(). В обоих
случаях сначала выполняется проверка того, изменит ли предполагаемое
действие состояние хранилища фактов. Если это так, создается запись о
действии в форме объекта с четырьмя свойствами, которая добавляется в
журнал (стек). Свойство указывает на характер выполняемого
действия. После этого вызывается стандартный метод для изменения
фактов
notetaker- и notetaker-. Реализация этих команд в составе метода doCommand()
представлена в листинге 16.20.
case "notetaker-keyword-commit":
this._undo_stack = [];
break;
case "notetaker-keyword-undo-all":
while (this._undo_stack.length > 0 )
{
var cmd = this._undo_stack.pop();
if ( cmd.assert )
this._ds.Unassert(cmd.sterm, cmd.pterm, cmd.oterm, true);
else
this._ds.Assert(cmd.sterm, cmd.pterm, cmd.oterm, true);
}
break;
Реализация команды notetaker- тривиальна – она просто
удаляет из журнала все записи, чтобы выполненные действия нельзя было
отменить в дальнейшем. Команда notetaker- чуть более
сложна. Она перебирает элементы стека, выполняя Unassert() для каждого 'а, записанного в журнале, и наоборот. В конце концов
стек оказывается пустым, так что "отмена отмены" в данной
реализации невозможна.
Хотя в данном случае в журнал записываются добавления и удаления
отдельных фактов, столь же просто организовать
Оставшаяся часть метода doCommand() приведена
в листинге 16.21.
doCommand : function (cmd) {
var url = window.opener.content.document.location.href;
var keyword = window.document.getElementById("dialog.keyword").value;
if (keyword.match(/^[ \t]*$/))
return;
var keyword_node = this._rdf.GetResource("urn:notetaker:keyword:" + keyword);
var keyword_value = this._rdf.GetLiteral(keyword);
var url_node = this._rdf.GetResource(url);
var test_node, keyword2, enum1, enum2;
switch (cmd) {
case "notetaker-keyword-add":
// Связать ключевое слово с другим, если таковое существует
keyword2 = this._ds.GetTarget(url_node, this._keyword, true);
if (keyword2)
this._LoggedAssert(keyword_node, this._related, keyword2);
// добавить данное ключевое слово
this._LoggedAssert(keyword_node, this._label, keyword_value);
// добавить ключевое слово к текущей заметке
this._LoggedAssert(url_node, this._keyword, keyword_node);
break;
case "notetaker-keyword-delete":
// удалить связь ключевого слова с текущей заметкой.
this._LoggedUnassert(url_node, this._keyword, keyword_node);
// если ключевое слово не используется в других местах, удалить его и связанные с ним факты
enum1 = this._ds.GetSources( this._keyword, keyword_node, true);
if (!enum1.hasMoreElements())
{
// данное ключевое слово
this._LoggedUnassert(keyword_node, this._label, keyword_value);
// ключевое слово, с которым связано данное слово
enum2 = this._ds.GetTargets(keyword_node, this._related, true);
while (enum2.hasMoreElements())
this._LoggedUnassert(keyword_node, this._related,
enum2.getNext().QueryInterface(Ci.nsIRDFNode));
// ключевое слово, которое связано с данным словом
enum2 = this._ds.GetSources(this._related, keyword_node, true);
while (enum2.hasMoreElements())
this._LoggedUnassert(enum2.getNext().QueryInterface(Ci.nsIRDFNode), this._related, keyword_node);
}
else // ключевое слово используется в других местах
{
// удалить факты, если слова, с которыми связано данное ключевое слово, относятся только к данной заметке
enum1 = this._ds.GetTargets(keyword_node, this._related, true);
while (enum1.hasMoreElements())
{
keyword2 = enum1.getNext().QueryInterface(Ci.nsIRDFNode);
enum2 = this._ds.GetSources(this._keyword, keyword2, true);
test_node = enum2.getNext().QueryInterface(Ci.nsIRDFNode);
if (!enum2.hasMoreElements() test_node.EqualsNode(url_node))
this._LoggedUnassert(keyword_node, this._related, keyword2);
// удалить факты, если слова, которые связаны с данным ключевым словом, относятся только к данной заметке
enum1 = this._ds.GetSources(this._related, keyword_node, true);
while (enum1.hasMoreElements())
{
keyword2 = enum1.getNext().QueryInterface(Ci.nsIRDFNode);
enum2 = this._ds.GetSources(this._keyword, keyword2, true);
test_node = enum2.getNext().QueryInterface(Ci.nsIRDFNode);
if (!enum2.hasMoreElements() test_node.EqualsNode(url_node))
this._LoggedUnassert(keyword2, this._related, keyword_node);
}
}
}
break;
Около десятка строк, находящихся до оператора switch(), выполняют
инициализацию локальных переменных и прекращают выполнение команды в
том случае, если текущая заметка отсутствует. В листинге показаны
только ветви оператора switch(), соответствующие командам notetaker- и notetaker-.
Добавление и удаление ключевых слов было бы проще, если бы мы не
пытались поддерживать логику связей между ключевыми словами.
Значительная часть кода, особенно для удаления ключевых слов, связана
с решением этой задачи.
Обе команды предполагают, что заметка для данного URL уже существует или создается в данный момент. Поэтому ключевые слова добавляются и удаляются в контексте определенного URL. Основные действия отмечены в коде при помощи комментариев, однако ниже приводятся более подробные пояснения.
Весь код написан с учетом того ограничения, что в хранилище фактов не могут находиться две копии одного и того же факта. Каждый факт в хранилище уникален. Поэтому мы должны работать с хранилищем глобально, учитывая последствия добавления или удаления отдельных фактов для хранилища в целом.
Добавление факта – более простой случай, чем удаление. Необходимо добавить к хранилищу следующие факты:
<- keyword-urn, related, keyword2-urn -> (не обязательно) <- note-url, keyword, keyword-urn -> <- keyword-urn, label, keyword-literal ->
Мы должны сделать так, чтобы все ключевые слова, присвоенные одной
заметке, были связаны между собой. В нашей модели _LoggedAssert().
Процедура удаления ключевых слов значительно более сложна. Нетрудно удалить информацию, связанную с конкретной заметкой, но ключевое слово может быть присвоено нескольким заметкам. Это означает, что оно может быть включено в связи между ключевыми словами, также относящиеся к другим заметкам. Поэтому мы не можем просто удалить факты первого и третьего типа, не изучив вопрос, где еще используется данное ключевое слово. Наши дальнейшие действия будут зависеть от того, присвоено ли оно другим заметкам. Это требует довольно громоздкой логики, которая описана ниже.
Если ключевое слово используется для единственной заметки, то вся информация об этом слове ограничена данной заметкой. Поэтому мы можем просто удалить все три факта.
Если же слово используется для других заметок, следует действовать более аккуратно. Мы можем удалить факт о связи ключевого слова с данной заметкой, но мы не можем удалить само ключевое слово (факт о его значении). Самая сложная часть логики – удаление фактов, описывающих связь данного слова с другими ключевыми словами. Если факт о связи данного ключевого слова с другим сформирован на основе другой заметки, мы не должны удалять его. Поэтому, если другое слово присвоено какой-либо другой заметке наряду с данным, факт относится и к этой заметке и потому должен быть сохранен. В противном случае следует удалить этот факт. Мы выполняем проверку дважды, поскольку удаляемое ключевое слово может быть как субъектом, так и объектом факта о связи ключевых слов.
Внимательный читатель может заметить, что контейнер также должен быть обновлен командами -add и -delete. Мы не сделали этого,
поскольку реализация команд и без того достаточно сложна, а для того,
чтобы увязать дальнейшие обновления с нашей системой отмены, требуются
определенные ухищрения. Более общим решением было бы связать объекты-
наблюдатели источника данных со стеком-журналом, однако здесь мы не
будем рассматривать это решение. На этом мы завершаем обсуждение
команд NoteTaker для работы с данными
Для того чтобы эта система заработала, необходимо связать
контроллер и команды с диалоговым окном. Для этого требуется несколько
фрагментов кода. Мы должны добавить к файлу editDialog.xul
дополнительный тег <script>:
Кроме того, мы должны добавить несколько обработчиков событий к
тегу < в том же файле. Эти обработчики нужны для работы с
ключевыми словами из диалогового окна.
<dialog xmlns="http://www.mozilla.org/keymaster/
gatekeeper/there.is.only.xul"
id="notetaker.dialog"
title="Edit NoteTaker Note"
onload="execute('notetaker-load');"
ondialogaccept="execute('notetaker-keyword-commit');
execute('notetaker-save');
execute('notetaker-close-dialog');"
ondialogcancel="execute('notetaker-keyword-undo-all');
execute('notetaker-close-dialog');"
>
Обработчики разрастаются, и если по мере развития приложения придется добавлять новые, целесообразно объединить группы команд в функции. Некоторые другие обработчики для диалогового окна определены в файле dialog_handlers.js. Теперь, когда мы создали контроллер для работы с ключевыми словами, два из этих обработчиков превращаются в тривиальные вызовы команд:
function add_click(ev)
{
execute("notetaker-keyword-add");
}
function delete_click(ev)
{
execute("notetaker-keyword-delete");
}
Теперь мы можем считать, что приложение NoteTaker завершено – по крайней мере, в той степени, в какой это позволяет сделать объем книги.
В практическом разделе лекции 13
"Списки и деревья" мы экспериментировали с
"кадрами". При желании этот эксперимент может быть
распространен на данные <tree>,
содержащий кадр, может получать содержимое из источника данных без
помощи шаблона. Поскольку объем книги не позволяет подробно обсудить
этот вопрос, сделаем несколько кратких замечаний.
В лекции 13 мы реализовали метод calcRelatedMatrix(), который получал данные из массива treedata. Мы можем изменить этот метод так, чтобы он
получал связанные пары ключевых слов не из массива JavaScript, а от
источника данных. В этом случае наш код заработает немедленно, но уже
на основе данных
Однако такая стратегия – довольно примитивное использование
возможностей источников данных. Более разумное решение – использовать
интерфейс nsIRDFObserver. Если объект JavaScript, реализующий кадр,
поддерживает этот интерфейс, он может быть зарегистрирован в качестве
наблюдателя в источнике данных (источник, в свою очередь, должен
поддерживать интерфейс nsIRDFCompositeDataSource ). В результате кадр
будет получать уведомление всякий раз, когда факт в источнике
изменяется, и сможет отразить это изменение в элементе <tree>,
не перестраивая дерево полностью. Эта более сложная стратегия
совместима с системами
В заключение отметим, что объект с интерфейсом nsIRDFDataSource
может быть полностью реализован на JavaScript. Такой объект может
имитировать источник данных
Вот некоторые из типичных проблем, возникающих при работе с источниками данных:
Регистр символов. В отличие от остальных методов XPCOM, имена
методов интерфейсов источников данных начинаются с прописной буквы – InitCaps, а не initCaps. Поэтому, например, метод называется GetResource(), а не getResource().
Асинхронная загрузка. Если для создания источника данных
используется метод GetDataSource() интерфейса nsIRDFDataSource, а не GetDataSourceBlocking(), данные загружаются в источник параллельно с
выполнением других инструкций. В результате попытка обратиться к
данным немедленно после создания источника может привести к ошибке.
Данные, заведомо присутствующие в источнике с точки зрения
разработчика, могут оказаться еще не загружены.
Синтаксические ошибки в тестовых данных. Тестовые данные из файла
Попытка сохранить данные по сети. Если вы вносите изменения в
источники, получающие данные из сети (с Web-ресурса или FTP-сайта),
такие изменения не могут быть непосредственно "сохранены".
Этот механизм применим только к локальным файлам. Чтобы передать
сделанные изменения на удаленный сервер, необходимо создать на основе
источника данных документ
Использование false в качестве аргумента методов и Unassert(). Четвертым аргументом этих методов всегда должно быть true.
Использование false расширяет логику методов true.
Передача строк методам и Unassert(). Эти методы принимают
только объекты типа nsIRDFNode и его
Проблемы с множественными возвращаемыми значениями. Такие методы,
как GetTargets() возвращают объект-перечислитель nsISimpleEnumerator,
содержащий список nsISupports.
Используйте метод QueryInterface() для того, чтобы получить более
полезный интерфейс nsIRDFNode или один из его
Объекты, реализующие интерфейс nsIRDFContainerUtils, являются
служебными. Они используются для выполнения различных действий с
другими объектами, передаваемыми им в качестве аргументов.
Внутренние источники данных
function _dumpFactSubtree(ds, sub, level)
{
var iter, iter2, pred, obj, objstr, result="";
// выйти, если передан nsIRDFLiteral или другой не-URI
try { iter = ds.ArcLabelsOut(sub); }
catch (ex) { return; }
while (iter.hasMoreElements())
{
pred = iter.getNext().QueryInterface(Ci.nsIRDFResource);
iter2 = ds.GetTargets(sub, pred, true);
while (iter2.hasMoreElements())
{
obj = iter2.getNext();
try {
obj = obj.QueryInterface(Ci.nsIRDFResource);
objstr = obj.Value;
}
catch (ex)
{
obj = obj.QueryInterface(Ci.nsIRDFLiteral);
objstr = '"' + obj.Value + '"';
}
result += level + " " + sub.Value + " , " +
pred.Value + " , " + objstr + "\n";
result += dumpFactSubtree(ds, obj, level+1);
}
}
return result;
}
function dumpFromRoot(ds, rootURI)
{
return _dumpFactSubtree(ds, rootURI, 0);
}
Для вывода всех данных источника следует вызвать функцию dumpFromRoot(). Этот код использует ограниченное подмножество
функциональности интерфейса nsIRDFDataSource и должен работать с
большинством внутренних источников данных, а также с простыми файлами
В качестве аргументов функции должны быть переданы объекты nsIRDFDataSource и nsIRDFResource. Источник данных, представленный
первым объектом, должен быть полностью загружен – в противном случае
может быть выведена неполная информация о его содержимом. Аргумент rootURI должен быть контейнером или владельцем контейнера на графе
Инфраструктура платформы Mozilla содержит множество полезных
объектов, не все из которых были освещены в этой лекции. Большинство из
них являются объектами довольно высокого уровня в силу требований
переносимости и ориентации на разработку приложений. Можно представить
себе, что в дальнейшем в составе платформы будет реализован аналог
интерфейса
Mozilla предоставляет богатые возможности для работы с XML, что неудивительно. Первоначально интенсивная обработка XML была характерна для приложений класса business-to-business, однако инициатива .NET компании Microsoft подразумевает активное использование XML на стороне клиента.
К настоящему моменту мы рассмотрели как интерфейс приложений Mozilla, так и их инфраструктуру, и нам осталось лишь познакомиться с развертыванием этих приложений. Наряду с системой сборки Mozilla, которая используется для создания компилированных приложений, существует и система удаленной установки приложений Mozilla. Эта система, XPInstall, и является предметом заключительной лекции курса.
В состав платформы Mozilla входит объектная библиотека, содержащая более тысячи объектов, которые могут использоваться разработчиком при создании собственных приложений. Многие из этих объектов не имеют никакого отношения к графическому интерфейсу пользователя. Задача этой лекции – рассказать, для каких целей разработчик может использовать различные группы объектов платформы.
Тысяча примеров скриптов – слишком много для одной лекции. Здесь мы
можем лишь познакомиться с основными типами объектов, а также
предложить читателю некоторые рекомендации и ориентиры для дальнейшего
освоения библиотеки. Если вы займетесь разработкой на платформе
Mozilla, в дополнение к материалу этой лекции вам придется читать
определения
Объектная библиотека Mozilla состоит, главным образом, из
компонентов XPCOM. Без этих компонентов возможности разработчика
приложений были бы ограничены работой с документами XML, будь то HTML
или XUL. Механизмы взаимодействия приложений с внешним миром
сводились бы к таким технологиям, как URL, HTTP,
Набор компонентов Mozilla сравним с любой современной объектно-
ориентированной библиотекой или стандартной библиотекой языка третьего
поколения. Подобно языкам C++ и Java, платформа Mozilla использует
концепцию
Это "почти" связано с тем, что Mozilla пока достигла лишь версий 1.x. С относительной новизной платформы связана некоторая ограниченность круга объектов, доступных разработчику. Вместо того чтобы предложить широкий диапазон объектов низкого уровня, Mozilla содержит некоторое количество объектов низкого и среднего уровней, а также ряд объектов очень высокого уровня, рассчитанных на приложения определенного типа. Поскольку первоначально платформа была разработана как средство создания браузера и других приложений для работы с Internet, специализированные объекты, созданные для решения этой задачи, присутствуют в библиотеке на всех уровнях абстракции. В отличие, например, от библиотеки классов Java, набор компонентов Mozilla не был с самого начала спроектирован, как мощная универсальная библиотека. Тем не менее, тысяча компонентов – весьма мощный ресурс, который по объему приближается к обширной библиотеке модулей Perl.
Еще одна нетипичная особенность библиотеки Mozilla состоит в том,
что многие ее объекты рассчитаны на работу с сетью. Первоначальные
приложения Mozilla – навигатор,
Однако даже если разрабатываемое приложение не является браузером,
Как показано на схеме в начале лекции, компоненты XPCOM являются основой прикладной части Mozilla. Технологии XPCOM и XPConnect используют различные вспомогательные файлы, прежде всего реестр (простую базу данных, сходную с системным реестром Microsoft Windows) и библиотеки типов (описания компонентов). Настройки Mozilla и информация о сертификатах также хранятся отдельно от компонентов. С точки зрения разработчика, наиболее интересной частью архитектуры XPCOM являются отдельно хранимые файлы XPIDL, которые содержат описания всех интерфейсов XPCOM в форме, удобном для чтения.
Эта лекция начинается с изложения нескольких концепций, особенно важных для понимания программного окружения, в котором взаимодействуют компоненты XPCOM. Затем мы переходим к обсуждению типичных задач программирования и решений, предлагаемых платформой Mozilla. Сначала рассматриваются более общие задачи, а затем специфичные для отдельных групп приложений. Затем обсуждается платформа в целом и ее система защиты. Практический раздел лекции содержит множество примеров, демонстрирующих использование объектов для работы с JavaScript.
Обширная библиотека компонентов XPCOM, входящая в состав платформы Mozilla, – особый мир, где действуют свои законы и правила. Чтобы эффективно использовать эту библиотеку, полезно познакомиться со сложившейся терминологией и принятыми соглашениями в этой области.
Фактически платформа Mozilla образована множеством различных программ, написанных на разных языках, и разработчику часто приходится обращаться к исходным текстам, чтобы изучить те или иные возможности платформы. В большей части исходного кода Mozilla и документации используется определенный стиль кодирования, к которому надо привыкнуть. Помимо документации, разработчик может обратиться к следующим источникам:
Из перечисленных источников основным являются определения XPIDL – это необходимый минимум для "выживания" на платформе Mozilla. URL этих файлов можно найти во введении к книге.
Стиль кодирования для платформы Mozilla подразумевает использование
определенных соглашений об именовании. Это особенно важно для кода на
JavaScript, поскольку этот язык обладает лишь слабыми механизмами
Ниже приведены некоторые примеры соглашений об именовании, применяемых в исходном коде Mozilla.
Для определения характера интерфейсов XPCOM используются префиксы.
Наиболее распространенным является префикс nsI (от " n et s cape I nterface"), который применяется для обозначения интерфейсов,
предназначенных для использования разработчиками приложений.
Существует также ряд более специфичных префиксов, указывающих на связь
интерфейса с определенным приложением или технологией, например imgI, inI, jsdI и mozI (от image, inspector, JavaScript ns (без I) не предназначены для
использования разработчиками приложений. Как правило, эти объекты
применяются в системном коде платформы.
Для атрибутов и методов интерфейсов используются определенные соглашения об использовании прописных букв.
ALL_UPPERCASE_STYLE ).initCapStyle ).initCapStyle ).InitCapStyle ).В любом случае, прописные и строчные буквы используются одинаковым
образом в XPIDL и в JavaScript. В системном коде платформы (C/C++)
имена методов транслируются из initCap в InitCap. Имена интерфейсов
иногда используются в той же форме, что и в XPIDL, а иногда
записываются только прописными буквами ( ALL_CAPS ).
В коде Mozilla часто используются однобуквенные префиксы для имен переменных – атрибутов интерфейсов и аргументов методов. Эта нотация широко используется в связках XBL, системном коде на C/C++, интерфейсах XPIDL и прикладном коде на JavaScript. В последнем случае эти префиксы могут использоваться несколько бессистемно. Основные однобуквенные префиксы, используемые в коде Mozilla, представлены в таблице 16.1.
| Префикс | Частота использования | Значение |
|---|---|---|
a |
Регулярно | aVar – временная переменная либо аргумент функции или метода. Как правило, она используется для хранения значения или объекта, подлежащих обработке. Пример – aFile |
e |
В некоторых случаях | eVar – значение, чаще всего константа, которое используется в качестве одного из элементов eTuesday (элемент для вторника в |
g |
В некоторых случаях | gVar – глобальная переменная; глобальная в контексте текущего окна (JavaScript) или полностью глобальная (C/C++). Пример – gMenuControllers |
k |
В некоторых случаях | kVar – значение ключа (key), одно из фиксированного набора значений, которые может принимать некоторая переменная. Этот префикс сходен с префиксом e, однако в данном случае значения переменной часто являются побитовыми масками или строками, а не последовательными целыми, начиная с единицы. Пример – kMimeType |
m |
Регулярно | – член (свойство или mLength. |
n |
В некоторых случаях | nVar – как правило, содержит сумму или итог каких-либо вычислений. Это обычная переменная |
s,i,b,f,r,p |
Редко | Эти префиксы используются в системном коде платформы (C/C++), где они означают строку, целое, булево значение (Boolean), f может означать, например, файл или папку (folder) |
Еще один используемый префикс – PR, что означает
Платформа Mozilla использует разнообразные подходы к разделению программ на части или фрагменты. Практически любой термин для "части" или "фрагмента", используемый в разработке ПО, применяется в терминологии платформы Mozilla. Ниже приведены эти термины и их корректное использование в контексте Mozilla:
Связка (
Класс. Единственные классы в Mozilla – классы компонентов XPCOM. На
основе каждого класса может быть создано ноль или более объектов.
JavaScript 2.0 (
Компонент представляет собой сущность, имеющую уникальный
идентификатор в системе XPCOM. Компонентом может быть класс с CID
(идентификатором компонента) и соответствующим ContractID (вида @mozilla.org/test;1 ) или интерфейс с (идентификатором интерфейса).
Иногда настоящими компонентами считают только классы.
Интерфейс – набор точек доступа к объекту. Интерфейсы XPCOM являются единственным примером интерфейсов в Mozilla. О каждом объекте, который предоставляет точки доступа, соответствующие описанию интерфейса, говорят, что он реализует этот интерфейс. Каждый объект XPCOM и связка XBL реализует ноль или более интерфейсов XPCOM. Объекты JavaScript также могут реализовывать интерфейсы XPCOM.
Библиотека. В состав платформы Mozilla входит несколько динамически подключаемых библиотек, но вряд ли они представляют особенный интерес для разработчика приложений. Иногда библиотеками называют скрипты или группы скриптов на JavaScript, которые могут предоставлять полезную функциональность другим программам. Библиотеки типов представляют собой файлы данных, определяющие интерфейсы XPCOM. Они создаются в момент компиляции платформы и автоматически используются механизмом XPConnect при обращении к интерфейсам.
Модуль. Компоненты XPCOM, входящие в состав платформы, сгруппированы в модули, но этот факт значим только для разработчиков самой платформы. Модули не имеют практического смысла для разработчика приложения, если только он не создает новый модуль XPCOM.
Объект. Mozilla содержит объекты XPCOM и объекты JavaScript. Объект
XPCOM является экземпляром определенного класса XPCOM и реализует один
или несколько
Пакет – группа взаимосвязанных файлов, установленная в
Прототип – объект JavaScript, используемый в качестве основы для создания нового объекта JavaScript.
Система XPCOM обеспечивает доступ скриптов JavaScript к другим
программным окружениям, внешним по отношению к этим скриптам. Эти
внешние окружения имеют собственные
Из скриптов JavaScript доступны пять внешних систем типов:
Базовые типы платформы, реализованные в составе NSPR. Это – переносимые типы C/C++, лежащие в основе платформы Mozilla.
Типы данных RDF. Типы в документах
Типы данных схемы XML (XML schema). Mozilla способна выполнять
XML RPC XDR. Mozilla поддерживает сетевой протокол RPC-через-XML,
включая типы данных
Java.
Из пяти перечисленных систем
nsISupportsPrimitive, например nsISupportsPRInt32.nsIRDFLiteral, nsIRDFDate и nsIRDFInt, основанные на nsIRDFNode.nsISchemaSimpleType и nsISchemaBuiltinType.nsIXmlRpcClient содержит метод-фабрику, который порождает необходимые объекты типов NSPR.В дополнение к этим внешним
В этом разделе описано решение общих задач программирования на платформе Mozilla.
Платформа Mozilla, запущенная из командной строки, запоминает аргументы, переданные исполняемому файлу. В ОС Microsoft Windows платформа не запоминает аргументы командной строки, указанные при запуске последующих приложений, которые используют тот же экземпляр платформы.
Используйте эти компонент и интерфейс, чтобы получить доступ к
@mozilla.org/appShell/commandLineService;1 nsICmdLineService
Интерфейс nsICmdLineService поддерживает свойство argc, содержащее
количество аргументов, но не argv, которое могло бы содержать строки,
образующие аргументы. Свойство argc содержит количество пар
аргумент-значение, а не количество строк, разделенных пробелами (традиционная
практика и в UNIX, и в Windows). Поскольку свойство argv или
аналогичное ему не поддерживается, вам придется угадывать имена
параметров, используя метод getCmdLineValue() для получения их
значений. Типичный вызов этого метода выглядит следующим образом:
var url = cls.getCmdLineValue("-chrome");
Метод возвращает значение аргумента, а если аргумент с указанным
именем не был использован при запуске, возвращается значение null.
Этот интерфейс также содержит метод-фабрику getHandlerForParam(),
который возвращает объект, имеющий интерфейс nsICmdLineHandler. Такой
объект представляет собой структуру данных, доступную только для
чтения, и содержащую конфигурационную информацию для обработчика
командной строки, например значения по умолчанию. Каждый существующий
обработчик добавляет новые аргументы командной строки, доступные
платформе. При необходимости такие обработчики могут создаваться при
помощи JavaScript.
Средствами JavaScript невозможно получить исходную копию командной строки.
Язык JavaScript предоставляет простые массивы и объекты, которых
достаточно для большинства несложных задач. За пределами JavaScript
платформа Mozilla поддерживает обширную
Помимо
Объекты-коллекции XPCOM, которые могут применяться сами по себе, перечислены в таблице 16.2. Хотя во многих ситуациях их использование не рекомендуется, они заслуживают упоминания.
| Интерфейс | Реализован в | Описание |
|---|---|---|
nsIArray |
@mozilla.org/array;1 |
Доступный только для чтения массив JavaScript, реализованный средствами XPCOM |
nsIMutableArray |
@mozilla.org/array;1 |
Добавляет методы для модификации содержимого nsIArray |
nsICollection |
@mozilla.org/supports-array;1 |
Добавляет простой интерфейс коллекции к |
nsIDictionary |
@mozilla.org/ |
Простая коллекция, состоящая из пар ключ-значение (отображение, ассоциативный массив), реализованная на JavaScript; может быть полезна |
nsIProperties |
@mozilla.org/properties;1 |
Простая коллекция, состоящая из пар ключ-значение, реализованная на C/C++ |
Сами по себе эти коллекции не слишком полезны, однако для работы с
ними существуют специальные интерфейсы – курсоры или
nsIEnumerator nsIBidirectionalEnumerator
Перечислитель – курсор для перебора данной коллекции, допускающий
лишь nsISupports.
Более сложная разновидность курсора называется nsI{некая_строка} и
редко бывают нужны сами по себе. Однако их можно использовать в
качестве образцов при проектировании сложных структур данных и
способов доступа к ним. Иногда
Стандарт nsIDOMNodeIterator. Он может быть полезен при
работе со структурами данных
Платформа Mozilla содержит не слишком много реализаций алгоритмов
общего характера. В JavaScript доступны регулярные выражения и метод
для Array.sort(). Возможности сортировки,
применяемые в шаблонах XUL, недоступны за их пределами.
Платформа Mozilla в определенной степени поддерживает работу с
базами данных, но эта поддержка развивается медленно. При сборке
интегрированного пакета Mozilla с параметрами по умолчанию доступна
лишь минимальная поддержка работы с базами данных; получение
дополнительных возможностей требует дополнительных усилий. Базы
данных, поддерживаемые платформой, можно условно разделить на пять
групп: неструктурированные файлы,
В таблице 16.3. перечислены поддерживаемые Mozilla базы данных, основанные на неструктурированных файлах.
Две последние строки таблицы требуют пояснений.
| Формат файла | Поддержка в приложениях | Рассматривается в разделе |
|---|---|---|
| Произвольные файлы | Чтение/запись | "Файлы и папки", "Передача данных" |
| Документы |
Только чтение | Лекция 3 "Статическое содержимое" |
| Файлы свойств | Только чтение | Лекция 5 "Скрипты" – пример с интерфейсом nsIStringBundle |
| Настройки | Чтение/отложенная запись | "Настройки" |
| Документы XML | Чтение или запись | "Web-скрипты" |
| Документы |
Чтение/синхронизация кэша с файлом | "Источники данных" |
| Реестр Mozilla | Чтение/запись | Лекция 17 "Развертывание" |
| Недоступна | См. в тексте | |
| Недоступна | См. в тексте |
Что касается реляционных СУБД, то в версии Mozilla, собираемой по умолчанию, их поддержка отсутствует. Однако она может быть добавлена. В таблице 16.4 представлена ситуация на момент выхода Mozilla 1.4 и прогноз на ближайшее будущее.
Наконец, платформа поддерживает ряд форматов файлов, разработанных
для конкретных приложений. Работа со всеми этими файлами
осуществляется опосредованно, с помощью интерфейсов высокого уровня.
Все эти файлы находятся в каталоге
Файлы закладок и cookies полностью переписываются при каждом
изменении. Файл
Mork, упоминаемая в таблице, представляет собой простую технологию
хранения данных в неструктурированном файле, основанную на
| СУБД/технология | Способ установки | Платформы | Примечания |
|---|---|---|---|
| Загружаемый пакет XPInstall | Кросс-платформенный | См. mysqlxpcom.mozdev.org | |
| Перекомпилировать Mozilla 1.5+ | Linux/UNIX | См. http://www.mozilla.org/projects/sql | |
| PostgreSQL | Перекомпилировать Mozilla 1.3+ | Linux/UNIX | См. http://www.mozilla.org/projects/sql |
| Protozilla | Загружаемый пакет XPInstall | Кросс-платформенный | Обеспечивает возможность добавлять к Mozilla поддержку сетевых протоколов, включая взаимодействие с базами данных. См. protozilla.mozdev.org |
| Web-интерфейс | Сделать доступным Web-сервер с базой данных | Кросс-платформенный | Стандартный метод доступа к базам данных – использовать запросы GET и POST протокола HTTP для работы с Web-сервером, взаимодействующим с сервером СУБД |
| Формат файла | Примечания |
|---|---|
| Файл cookies | |
| Файл закладок | |
| Использует Mork | |
| Файл информации о сообщениях конференций ( |
Использует Mork |
| Файл информации о подписке на конференции | Стандартный формат файла информации о конференциях |
| Файл информации о сообщениях электронной почты | Использует Mork |
| Файл почтового ящика (папки) электронной почты | Стандартный формат почтового ящика утилиты mail(1) UNIX |
| Mork | См. обсуждение в тексте |
Mork/in-memory-.
Еще один вид технологий, применяемых в Mozilla и сходных с базами
данных, – кэши. В частности, это кэш Web-документов, в котором
хранятся локальные копии документов, расположенных на удаленных
серверах, а также кэш быстрой загрузки XUL, в котором хранятся файлы
Значения переменных окружения текущего процесса могут быть получены по одному значению за запрос при помощи следующей пары из компонента и интерфейса:
@mozilla.org/process/util;1 interface nsIProcess
Интерфейс nsIProcess предоставляет метод getEnvironment(), который
возвращает значение переменной, имя которой было передано в виде
параметра. Передаваемые имена переменных конвертируются из
Тип операционной системы или версия Mozilla могут быть получены без
обращения к переменным окружения. Для этого достаточно
проанализировать значение свойства window.navigator.userAgent
property.
Для версий Mozilla, собранных с поддержкой отладки, переменная MOZILLA_FIVE_HOME должна содержать путь к каталогу, в котором
установлены
Не существует переменной окружения, которая указывала бы путь к
текущему или любому другому каталогу
Целый ряд переменных, имеющих отношение к отладке, устанавливается
при использовании версии Mozilla, собранной с поддержкой отладки (с
ключом --debug-enabled ). Их интерпретация весьма сложна, наиболее
надежным источником информации по этому вопросу является исходный код
Mozilla.
В этом разделе рассказано, каким образом можно находить файлы и
папки на том компьютере, где установлена платформа Mozilla. Здесь мы
используем термин папка, а не каталог, поскольку последний в контексте
данной лекции относится, прежде всего, к
Работа с файлами в Mozilla довольно сложна в силу ограничений, связанных с требованиями переносимости и соответствия стандартам WWW. Код, работающий с объектами XPCOM, представляющими файлы, должен быть переносим между платформами (как минимум, между UNIX, Microsoft Windows и Macintosh), а понятие файла или папки должно быть совместимо с понятием URL.
Требования переносимости влияют на работу с именами файлов и папок.
Платформа Mozilla не поддерживает концепцию
Ключевой абстракцией, предназначенной для решения этой проблемы,
является интерфейс nsIFile. Объекты, поддерживающие этот интерфейс,
часто используются в скриптах, но разработчики редко создают их
вручную или извлекают информацию о пути и имени файла, хранящуюся
внутри такого объекта. Это означает, что такие объекты редко создаются
при помощи стандартной пары XPCOM:
@mozilla.org/file/local;1 nsIFile
Вместо этого объекты с интерфейсом nsIFile создаются непрямым
образом, при помощи методов других интерфейсов. Для локальных файлов
существует специализированный вариант интерфейса nsIFile, которому
соответствует следующая пара:
@mozilla.org/file/local;1 nsILocalFile
Оба интерфейса могут представлять как файлы, так и папки. В существующем коде можно встретить и устаревший интерфейс для работы с файлами, который в настоящее время не рекомендован к использованию:
@mozilla.org/filespec;1 nsIFileSpec
Таким образом, разработчик приложения полагается на то, что объект,
представляющий файл, будет создан, инициализирован и возвращен методом
другого интерфейса. Существует целый ряд способов получения объектов nsIFile, представляющих нужные файлы:
parent интерфейса nsIFile. Для получения содержимого папки может использоваться свойство directoryEntries того же интерфейса.nsIFile при помощи этой строки.nsIFile был использован при создании потока, канала или другого объекта XPCOM, как правило, его можно получить в дальнейшем, используя методы этого другого объекта.Примеры использования таких методов приведены ниже в этом разделе.
Интерфейс nsIFile позволяет решить проблему переносимости операций
с файлами, однако остается вопрос интеграции файлов и URL. Для работы
с URL используются объекты с интерфейсом nsIURL, использование которых
описано в разделе "Web-скрипты". Для взаимного
преобразования файлов и URL используется следующая пара XPCOM:
@mozilla.org/network/protocol;1?name=file nsIFileProtocolHandler
Этот интерфейс поддерживает методы newFileURI() и getFileFromURLSpec(), которые и выполняют необходимые преобразования.
Интерфейс nsIIOService также поддерживает метод newFileURI().
Интерфейсы nsIFile и nsIURL позволяют получать строки,
представляющие фрагменты пути к файлу или URL. Выполнив определенные
операции над этими строками, приложение может создать объект nsIURL,
соответствующий объекту nsIFile, и наоборот.
Каталог файловой системы подробно описан в разделе "Конфигурация платформы", там же приведен и ряд примеров. Здесь мы приведем короткий пример, демонстрирующий поиск папки, используемой для создания временных файлов (см. листинг 16.1).
var Cc = Components.classes;
var Ci = Components.interfaces;
var dp = Cc["@mozilla.org/file/directory_service;1"];
dp = dp.createInstance(Ci.nsIDirectoryServiceProvider);
var folder = dp.getFile("TmpD", {});
Центральным элементом данного кода является использование
специального псевдонима TmpD для папки временных файлов. Такой подход
пригоден для поиска файлов и каталогов, которые имеют определенное
значение для платформы Mozilla, и для которых определены псевдонимы.
Таблицы существующих псевдонимов приведены ниже, в разделе
"Каталог файловой системы".
Создание диалогового окна для выбора файла пользователем с последующим созданием объекта nsILocalFile на основе выбранного файла показано в листинге 16.2.
var file;
var CcFP = Components.classes["@mozilla.org/filepicker;1"];
var CiFP = Components.interfaces.nsIFilePicker;
var fp = CcFP.createInstance(CiFP);
// используйте любые допустимые параметры для
инициализации объекта nsIFilePicker
fp.init(window, "File to Read", Picker.modeOpen);
if ( fp.show() != fp.returnCancel )
file = fp.file;
Как видно из приведенного кода, объект nsIFilePicker создает объект nsILocalFile, который можно получить, используя свойство file.
Если полученный объект соответствует папке, его можно преобразовать
к другой папке или файлу при помощи строки, представляющей
appendRelativePath(), который принимает в качестве аргумента
Приняв определенные меры предосторожности, можно построить сроку
nsIFile поддерживает ряд атрибутов и методов,
которые могут быть полезны при формировании переносимого пути.
Наконец, если приложение не должно быть переносимым или допускает
использование отдельных фрагментов кода для каждой из поддерживаемых
платформ, можно инициализировать nsILocalFile непосредственно при
помощи строки, представляющей путь, использовав для этого метод initWinPath(). При этом следует экранировать символы обратного слеша в
путях Microsoft Windows (\\) или использовать вместо них прямой слеш.
Отдельный код для различных платформ может иметь вид ряда операторов if, проверяющих текущую платформу.
Как уже было сказано, если приложение не должно быть переносимым
или допускает использование отдельных фрагментов кода для каждой из
поддерживаемых платформ, объект nsILocalFile может быть
инициализирован непосредственно при помощи строки. Пример
соответствующего кода приведен в листинге 16.3.
var file;
var CcLF = Components.classes["@mozilla.org/local/file;1"];
var CiLF = Components.interfaces.nsILocalFile;
var file = CcLF.createInstance(CiLF);
file.initWithPath("C:\\WINDOWS\NOTEPAD.EXE");
Литерал "C:" можно заменить переносимым объектом,
представляющим корневую папку файловой системы, которую можно получить
при помощи каталога файловой системы и псевдонима DrvD. Использование
в качестве разделителя прямого слеша (который поддерживают все
платформы, включая Microsoft Windows) также сделает этот фрагмент
более переносимым.
Локальный файл может быть также задан с помощью URL. Преобразовать URL в файловый объект можно следующим образом:
var conv = Cc["@mozilla.org/network/protocol;1?name=file"]; conv = conv.createInstance(Ci.nsIFileProtocolHandler); var url = ... // Существующий объект nsIURL var file = conv.getFileFromURLSpec(url);
URL, используемый в этом примере, должен иметь префикс file:. Путь
к файлу можно также получить, используя свойство filePath объекта nsIURL, например:
file.initWithPath(myURL.filePath.replace(/\|/,":"));
В данном случае объект myURL поддерживает интерфейс nsIURL. Замена
регулярного выражения при помощи метода replace() приводит фрагмент
URL вида "C|/test" к "C:/test". Нужно иметь в
виду, что сетевые пути в системе Microsoft Windows (пути
После того как файл найден и представлен соответствующим объектом, его читают, в него записывают данные или выполняют с ним другие действия.
В программном окружении JavaScript платформы Mozilla не
используются дескрипторы или идентификаторы (nsIPipe создает канал уровня приложения, который не является
традиционным каналом UNIX. Из скриптов невозможно создавать
именованные каналы (или
Вместо идентификаторов файлов Mozilla использует объекты. При этом
скрипту приходится работать, как минимум, с двумя объектами. Один из
них представляет используемый файл или папку – это может быть объект nsIFile или nsILocalFile. Этот объект –
Следующая пара XPCOM, основанная на тех же принципах, что и
nsIFile, обеспечивает доступ к содержимому локальных файлов .zip и
.
@mozilla.org/libjar/zip-reader;1 nsIZipReader
С помощью этого интерфейса могут также создаваться новые архивы Zip.
Конверторы потоков, описанные в разделе "Преобразование содержимого потоков", могут использоваться для работы с потоком сжатого содержимого в необработанном виде.
Не существует способа отправлять или перехватывать сигналы операционной системы из скриптов JavaScript. Чтобы компонент XPCOM мог перехватывать сигналы, он должен быть написан на Java или C/C++.
Интерфейс nsIThread может использоваться для управления выполнением
фрагмента кода, которое может быть прервано. При этом код, выполнение
которого должно быть прервано, не может быть написан на JavaScript.
Интерпретатор JavaScript платформы Mozilla выполняется в одном потоке
вычислений (thread), и не может прервать собственное выполнение. Из
этого следует, что прерывания, основанные на потоках вычислений,
неприменимы в приложениях, написанных исключительно на JavaScript.
В качестве замены прерываний могут использоваться технологии,
ориентированные на события (см. лекцию 6 "События"), и
Mozilla поддерживает ряд хорошо известных прикладных сетевых протоколов, например FTP. При этом Mozilla предполагает, что протоколом транспортного уровня является TCP/IP. Другие транспортные протоколы, например RS232, X.25 или TP4, могут использоваться, только если они "упакованы" в TCP/IP. Mozilla поддерживает следующие протоколы низкого уровня:
--enable-ipv6 .Как правило, приложения на платформе Mozilla не работают
непосредственно с сетевыми протоколами. Сетевые ресурсы
идентифицируются при помощи URL, и префикс метода доступа (например, http:), входящий в состав URL, определяет необходимый протокол. Как
правило, объект сетевого канала принимает URL, после чего поддержка
нужного протокола задействуется платформой автоматически, и с точки
зрения приложения все "просто работает". Тем не менее,
конкретные протоколы доступны в виде объектов, которые могут быть
созданы при помощи следующей пары XPCOM:
@mozilla.org/network/protocol;1?name={x} nsIProtocolHandler
В приведенном имени компонента { x } должно быть заменено на
идентификатор конкретного протокола, например ftp или http. Все
протоколы (точнее, window.Components.classes. Каждый из них
представлен отдельным компонентом.
С помощью настроек можно сконфигурировать Mozilla на уровне портов
IP, активизируя (открывая) или отключая (закрывая) конкретные порты.
Открытие порта имеет практический смысл лишь в том случае, когда
соответствующий порт открыт на уровне операционной системы. Открытие
дополнительных портов снижает уровень защищенности системы на уровне
приложений, и может быть рекомендовано лишь при использовании сетевого
экрана (файрволла). Получить доступ к полному набору сетевых настроек
Mozilla можно, введя в строке адреса браузера about:config. Имена
параметров, имеющих отношение к сети, начинаются с префикса network.
Разработчики приложений также имеют доступ к сокетам. Операционные
системы представляют соединение TCP/IP при помощи сокета, имеющего
дескриптор, аналогичный
Наконец, существует проект Protozilla, информация о котором
доступна на сайте http://www.mozdev.org, и который позволяет расширять
поддержку сетевых протоколов в Mozilla. С помощью расширения,
разработанного в рамках этого проекта, можно добавлять к Mozilla
поддержку новых протоколов, причем для этого достаточно
программирования только на JavaScript. Требования к этим протоколам
следующие: они должны быть реализованы поверх сокетов TCP/IP, терпимы
к небольшим задержкам, соответствующий код должен реализовывать
интерфейс nsIProtocolHandler и быть зарегистрирован как полноценный
компонент XPCOM.
Теперь мы переходим к обсуждению конкретных задач, возникающих при работе с сетью на низком уровне. Работа с сетью на уровне приложений описана в разделах "Передача данных" и "Web-скрипты".
Чтобы определить IP-адрес по заданному доменному имени, используйте следующую пару XPCOM:
@mozilla.org/network/dns-service;1 interface nsIDNSService
Объект, созданный таким образом, возвращает IP-адрес для заданного
имени домена или текущего узла в форме строки вида
"192.168.1.10". Разрешение доменных имен – медленная
операция. Xтобы работа приложения не приостанавливалась до завершения,
следует использовать метод , которому должен быть передан
слушатель с интерфейсом nsIDNSListener. В этом случае запрос будет
выполняться асинхронно. Реализуйте объект-слушатель на чистом
JavaScript.
Создание соединения с использованием сокета включает несколько этапов.
Для работы с сокетом вам, в конечном счете, понадобится создать
объект nsITransport. Получив этот объект, можно до некоторой степени
забыть, что вы работаете с сокетом, и использовать методы более
высокого уровня, описанные в разделе "Передача данных". В
целом, техника работы с сокетом, доступная разработчику приложений на
платформе Mozilla, отличается довольно высоким уровнем абстракции.
Например, ему недоступен API ioctl(2) для настройки параметров
сокета.
Создавая объект nsITransport, необходимо предусмотреть возможность
того, что между платформой Mozilla и удаленным компьютером, с которым
устанавливается соединение, находится nsIProxyInfo для адреса удаленного
компьютера. Объект nsIProxyInfo может быть создан при помощи методов newProxyInfo() или examineForProxy() следующей пары XPCOM:
@mozilla.org/network/protocol-proxy-service; nsIProtocolProxyService
Затем, используя полученный объект nsIProxyInfo или null, если вы
уверены в том, что nsITransport. Объект-фабрика создается
при помощи следующей пары XPCOM:
@mozilla.org/network/socket-transport-service;1 nsISocketTransportService
Затем нужно создать объект nsITransport, передав объект nsIProxyInfo методу createTransport() объекта-фабрики. Полученный
объект будет поддерживать интерфейс nsISocketTransport, представляющий
простой сокет TCP/IP. Если необходимо создать сокет , следует
использовать метод createTransportOfType() и указать в качестве типа " для протокола "socks4" для
протокола
Сокеты
В состав платформы Mozilla входят и другие интерфейсы для работы с сокетами, однако все они недоступны из JavaScript. Просматривая определения интерфейсов в файлах XPIDL, обращайте внимание на пометку [noscript] перед именем интерфейса. Она означает, что интерфейс недоступен из JavaScript.
В листинге 16.4 приведена
простая программа на Perl, которая может использоваться в качестве
сервера для тестирования соединений, установленных через сокет. Эта
программа принимает данные от всех клиентов, подключившихся к ней, и
направляет их в stdout. Программа не
поддерживает протокол и не возвращает клиентам никаких
данных.
use IO::Socket;
my ($server, $client, $host);
$server = IO::Socket::INET->new(
Proto => 'tcp', LocalPort => 80, Listen => SOMAXCONN, Reuse=> 1);
while ($server ($client = $server->accept()))
{
while ( <$client> ) { print; }
close $client;
}
Для работы этой программы необходима корректная настройка порта на уровне операционной системы.
Платформа Mozilla не поддерживает работу с сеансами FTP на низком
уровне. Элементарную операцию, доступную для разработчика, составляет
обращение к URL при помощи объекта nsIChannel. Это означает, что
каждый сеанс FTP состоит не более чем из четырех команд. На
open {hostname and port} //открыть сеанс
cd {directory} // перейти в нужный каталог
dir OR get {file} //получить содержание каталога или нужный файл
close //закрыть сеанс
Этот сеанс FTP осуществляется внутри платформы. Разработчик
приложений не получает информации о выполнении отдельных команд и не
может отдавать собственные команды. На практике это означает, что
единственный способ создания сеанса FTP, доступный разработчику
приложений, - запросить документ, в URL которого указан метод доступа ftp:. Эта процедура подробно описана в разделах "Загрузка
файлов" и "Каналы".
Если приложению необходимо перемещаться по иерархии каталогов FTP, потребуется несколько последовательных запросов. Как известно, URL может представлять не только отдельный файл, но и каталог FTP. При обращении к такому URL платформа возвращает содержимое каталога, правда, оформленное в виде HTML-документа. Проанализировав этот документ, можно получить список файлов и подкаталогов, находящихся в исходном каталоге, к которым, в свою очередь, можно сформировать запрос.
Тот же самый подход – сессия FTP, выглядящая как обращение к URL, – используется и при загрузке файлов на FTP-сервер. Подробнее об этом рассказано в разделе "Загрузка файлов".
Если ни один из предложенных методов не подходит для ваших целей, можно создать два сокета средствами JavaScript и, используя их, самостоятельно реализовать протокол FTP. При этом важно позаботиться о производительности приложения. Можно ожидать, что этот подход окажется почти столь же трудоемким, как и написание полноценного компонента XPCOM, реализующего протокол FTP, на C/C++.
Возможно, фрагмент кода, который предполагается запустить из
Простейший способ запустить отдельную программу – активизировать ее
с помощью
@mozilla.org/file/local;1 nsILocalFile
Затем следует связать полученный объект с каким-либо существующим
файлом (см. раздел "Файлы и папки"), после чего вызвать
метод этого объекта. Имейте в виду, что в системе UNIX
поведение метода определяется настройками среды GNOME, а не
значением переменной окружения PATH. Приложение, запущенное таким
образом, не зависит от процесса, в котором выполняется платформа
Mozilla, и не может быть остановлено средствами последней.
Более общий способ запуска процессов связан с использованием следующей пары XPCOM:
@mozilla.org/process/util;1 nsIProcess
Имейте в виду, что этот интерфейс до сих пор не реализован
полностью на всех платформах, поддерживаемых Mozilla. Чтобы
воспользоваться им, как и в предыдущем случае, нужно создать объект nsILocalFile и связать его с соответствующим исполняемым файлом.
Поскольку код для работы с процессами, как правило, зависит от
платформы, можно использовать для этого непереносимый метод initWithPath(). Передайте полученный объект методу init() объекта nsIProcess, а затем вызовите метод run(), чтобы создать процесс.
Пример вызова этого метода приведен ниже:
var blocking = true;
var argv = ["arg1","arg2"];
var result = {};
nsIProcess_object.run(blocking, argv, argv.length, result);
В процессе выполнения метода run() к объекту result, который
является обязательным аргументом метода, добавляется поле value,
которому в свою очередь присваивается значение 0 в случае успешного
blocking имеет значение true,
выполнение Mozilla будет приостановлено до завершения запущенного
процесса; при этом никакие окна Mozilla обновляться не будут. Если же
аргументу присвоено значение false, выполнение Mozilla будет
продолжено. В любом случае, по завершении запущенного процесса будет
установлено значение свойства exitValue объекта nsIProcess. Придется
поэкспериментировать, чтобы установить, какие значения соответствуют
нормальному
Работа с потоками вычислений более сложна. С точки зрения
разработчика приложений, отдельный поток вычислений представляет собой
всего лишь фрагмент кода, выполнение которого запланировано при помощи
метода window.. Строго говоря, в данном случае существует
лишь иллюзия отдельного потока – запланированное выполнение скрипта ни
при каких условиях не начнется раньше, чем завершится текущий
скрипт.
Это связано со способом реализации интерпретатора JavaScript в
составе платформы Mozilla. На низком уровне платформа поддерживает
отдельные потоки вычислений. Система потоков
Хотя интерпретатор не поддерживает истинные потоки вычислений, для
работы с потоками предусмотрен ряд интерфейсов. Фактически, они
позволяют организовывать код более аккуратно, чем при использовании
методов и . Последовательность действий по
созданию потока приведена в листинге 16.5:
var Cct = Components.classes["@mozilla.org/thread;1"];
var Cit = Components.interfaces.nsIThread;
var thread = { Run : function ()
{ alert(this.foo+" – выполняемый поток"); }
foo : "bar"
};
var mgr = Cct.createInstance(Cit);
mgr.init(thread, 0, Cit.PRIORITY_NORMAL, Cit.SCOPE_GLOBAL,
Cit.STATE_JOINABLE);
mgr.join();
alert("поток создан");
Объект, представляющий фрагмент исполняемого кода (в данном случае
– объект thread ), поддерживает интерфейс nsIRunnable. Он содержит
собственно исполняемый код (метод Run() ), а также данные, которые
могут потребоваться для выполнения этого кода. Объект mgr (менеджер
потока) содержит данные о конфигурации и состоянии потока вычислений.
С помощью вызова метода join() поток помещается в очередь на
выполнение или возобновление приостановленного выполнения
(приостановка и последующее возобновление выполнения невозможны для
кода, написанного для JavaScript). join() не эквивалентен методу , поскольку его вызов не приводит к немедленному выполнению
кода. Вместо этого код, представленный объектом, помещается в очередь,
и интерпретатор JavaScript дойдет до него не раньше, чем закончится
выполнение текущего скрипта. Поскольку интерпретатор выполняется в
одном потоке и не может быть приостановлен другим потоком, сообщение в
последней строке листинга всегда выдается раньше, чем сообщение из
кода в объекте thread.
Это означает, что ситуация конкуренции потоков (nsIRunnable.
Кроме того, скрипт JavaScript может создавать истинные потоки
вычислений, взаимодействуя с
В этом разделе описывается универсальная инфраструктура,
используемая для чтения, записи и передачи содержимого в приложениях
платформы Mozilla. В этом разделе также рассматривается обработка
фактов
Одна из важнейших функций прикладной части Mozilla – обработка содержимого Web-документов и других данных. Для этого инфраструктура платформы должна обеспечивать получение данных из внешних источников, а также передачу данных между различными составляющими платформы. В основе этой инфраструктуры лежит целый ряд концепций, относящихся к обработке и передаче содержимого и других данных.
В лекции 6 "События" описаны слушатели, наблюдатели и
Основные концепции, лежащие в основе инфраструктуры Mozilla для работы с содержимым, – файлы, папки, потоки данных, сеансы, каналы, транспорты, а также источники и приемники данных.
Такие понятия, как файл и каталог (папка) широко используются практически во всех операционных системах. Работа с ними описана в разделе "Общие приемы и методы программирования".
Поток
Сеанс представляет собой набор конфигурационной информации о выполняемом процессе, задании или деятельности. Такая конфигурационная информация используется, прежде всего, самим процессом. Однако при этом сеанс не является самим процессом, хотя его можно рассматривать в качестве контроллера последнего.
Примером сеанса может служить передача файла по FTP.
Канал передачи
Транспорт представляет собой элемент платформы, отвечающий за сетевое взаимодействие с использованием одного или нескольких протоколов. Так, если канал обеспечивает передачу данных на высоком уровне (получение документа, имеющего указанный URL), то на уровне транспорта реализована поддержка конкретных протоколов, например SMTP или TCP/IP.
Источники и приемники рассматриваются в следующем разделе.
Концепция источников и приемников занимает важное место в
архитектуре Mozilla. Обычно они используются для обработки XML-
документов. Источники и приемники образуют один из самых высоких
уровней обработки информации в составе платформы и, как правило,
выполняют преобразования, зависящие от
Источники и приемники (
Действительно, если раковина существует и работает, вода вытекает
из крана (источник) и попадает в
Именно такая ситуация характерна для работы с Mozilla. Для того чтобы загрузить содержимое документа в память, используется объект-приемник. Затем это содержимое или его часть можно извлечь при помощи источника данных.
Независимо от того, в каком порядке используются приемник и источник в каждом конкретном случае, источник всегда является производителем, а приемник – потребителем. С точки зрения разработчика приложения, если содержимое документа еще не находится в памяти платформы Mozilla (например, документ хранится в файле на диске), сначала необходимо создать приемник, чтобы загрузить это содержимое. На следующем этапе создается источник, с помощью которого различные части приложения могут получить доступ к загруженному содержимому. В некоторых случаях документ загружается в память автоматически, и тогда разработчику приходится создавать только источник.
В этой схеме есть одна сложность – при работе с документами в них могут вноситься изменения. Как правило, приемники используются для первоначальной загрузки содержимого в память и, таким образом, обеспечивают лишь одностороннюю обработку данных. Это означает, что ответственность за управление изменениями в документе лежит на источнике. Таким образом, источники не только обеспечивают доступ к содержимому документа, но и часто способны изменять его.
Mozilla не предоставляет
Источники и приемники служат для обработки содержимого на высоком уровне. Источники и приемники, входящие в состав платформы Mozilla, предназначены для различных целей.
Источники данных используются для обработки содержимого
Синтаксический анализатор (
Сериализатор (
Различные инструменты обработки данных, описанные в этом разделе, можно неофициально рассматривать как систему уровней или слоев, хотя эта система организована не так строго, как, например, стек сетевых протоколов. Уровни обработки данных показаны на рис.16.1.
(рис 16.1) Уровни обработки данныхНа рисунке приведена
На схеме не показаны многочисленные интерфейсы, представляющие собой конкретные реализации изображенных элементов, а также конкретные способы взаимодействия с остальной платформой.
Если нужный файл или другой источник информации доступен,
необходимо создать поток для работы с его содержимым. Потоки являются
основой архитектуры обработки данных в Mozilla, и существует множество
интерфейсов, ориентированных на работу с потоками. Среди них –
интерфейсы для создания, инициализации, преобразования потоков и
управления ими. Существуют специализированные разновидности потоков, в
частности, потоки с произвольным доступом и потоки, основанные на
строках. Практически для любой распространенной задачи можно найти
подходящий интерфейс-поток – просмотрите интерфейсы, содержащие в
названии слово
Чтобы продемонстрировать эту гибкость, в листинге 16.6 показаны три способа создания потока. Этот поток используется для чтения локального файла (последовательности байтов).
var Cc = Components.classes; var Ci = Components.interfaces; var mode_bits = 0x01; // from nsIFileChannel var perm_bits = 0; // from Unix/Posix open(2) var file_bits = 0; // from nsIFileInputStream var stream; var file = ... // см. листинг 16.3 или 16.2 // [1] Непосредственное создание stream = Cc["@mozilla.org/network/file-input-stream;1"]; stream = stream.createInstance(Ci.nsIFileInputStream); stream.init(file, mode_bits, perm_bits, file_bits); // [2] Создание на основе транспорта var trans = Cc["@mozilla.org/network/stream-transport-service;1"]; trans = trans.getService(Ci.nsIStreamTransportService); trans = trans.createInputTransport(stream,0,-1,true); var stream2 = trans.openInputStream(0,-1,0); // [3] Создание на основе канала var channel = Cc["@mozilla.org/network/local-file-channel;1"] channel = channel.createInstance(Ci.nsIFileChannel); channel.init(file, mode_bits, perm_bits); stream = channel.open(); // В любом случае, работа с потоком средствами JavaScript var s2 = Cc["@mozilla.org/scriptableinputstream;1"]; s2 = s2.createInstance(Ci.nsIScriptableInputStream); s2.init(stream); var bytes = 100; var content = null; content = s2.read(bytes);
В каждом из трех случаев в какой-то момент инициализации в качестве
аргумента передается ранее созданный объект nsILocalFile.
Пример 1. Файл читается или записывается непосредственно с использованием потока. Если не принять специальных мер, операции с файлом выполняются синхронно.
Пример 2. Это несколько необычный пример, поскольку в качестве
отправной точки для создания потока используется
Пример 3. Канал позволяет получить файл, не делая никаких предположений о механизме получения.
Из соображений эффективности JavaScript не позволяет
непосредственно читать данные из потоков или записывать в них. Вместо
этого необходимо создать специальный объект для выполнения операций
чтения и записи, передав ему поток. Пример создания такого объекта, а
также
Достаточно небольших изменений в примерах 1 и 2, чтобы создать
поток для записи вместо потока для чтения. В примере 3 такая замена
невозможна, поскольку каналы применяются только для чтения. При записи
данных предполагается, что поток вывода состоит из однобайтовых
символов (расширенная кодировка ASCII). При выводе любого содержимого,
состоящего из символов
Внутри платформы все строки представлены с помощью nsIBinaryInputStream ), как восьмибитные символы (вариант по
умолчанию) или как
Для преобразования содержимого потока используется следующая пара XPCOM:
@mozilla.org/intl/scriptableunicodeconverter;1 nsIScriptableUnicodeConverter
Mozilla также поддерживает множество компонентов с идентификатором контракта следующего вида:
@mozilla.org/streamconv;1?from={mime1}to={mime2}
Здесь mime1 и mime2 – типы nsIStreamConverter.
Такой объект получает входной поток и преобразует его содержимое,
создавая при этом новый поток, из которого могут читаться
| Исходный тип |
Тип |
|---|---|
application/http-index-format |
text/html |
application/mac-binhex40 |
*/* |
application/x-unknown-content-type |
*/* |
|
несжатое содержимое |
|
несжатое содержимое |
gzip |
несжатое содержимое |
message/rfc822 |
application/vnd.mozilla.xul+xml |
message/rfc822 |
*/* |
message/rfc822 |
text/html |
multipart/byteranges |
*/* |
multipart/ |
*/* |
text/ftp-dir |
application/http-index-format |
text/ |
application/http-index-format |
text/plain |
text/plain |
x- |
несжатое содержимое |
x-gzip |
несжатое содержимое |
Изучив описание XPIDL объекта nsIStreamConverter, можно понять,
каким образом такое преобразование может быть реализовано с
использованием двух объектов nsIStreamListener вместо двух полноценных
потоков. Такой подход позволяет конвертерам работать не только с
потоками ввода, но и с потоками любого типа.
Объекты XPCOM транспортного уровня отвечают за передачу содержимого изнутри платформы Mozilla и наоборот. Таким образом, транспорты являются более общим средством, чем потоки, которые предназначены для передачи информации внутри платформы или операций чтения/записи с локальными файлами. Если потоки, как правило, выполняют синхронные операции и работают непосредственно с указанным источником, транспорты могут выполнять как синхронные, так и асинхронные операции. При этом они могут буферизовать данные в промежутках между запросами пользователя.
Транспортные уровни, доступные в настоящее время, приведены в таблице 16.7.
| Реализация | Интерфейс |
|---|---|
@mozilla.org/network/ |
nsIStreamTransportService |
@mozilla.org/network/socket- |
nsISocketTransportService |
@mozilla.org/network/storage- |
nsITransport |
@mozilla.org/xmlextras/ |
nsISOAPTransport |
@mozilla.org/xmlextras/ |
nsISOAPTransport |
Пять транспортов, приведенных в таблице, предназначены для: всех
потоков, включая локальные файлы; сокетов; кэша браузера; транспорта
HTTP для запросов
Реализация является относительно новой (с
версии 1.3) и заменяет недоступную более реализацию file-.
Вам могут встретиться примеры кода, использующие старую реализацию.
Каналы представляют собой односторонний (только для чтения)
механизм получения содержимого указанного URL. Хотя в принципе канал
может использоваться и для
В обычных ситуациях разработчик приложений редко создает каналы
самостоятельно. Подобно объектам nsIFile и потокам, каналы чаще всего
создаются платформой при выполнении операций более высокого уровня.
Файл и поток образуют пару объектов, тесно связанных между собой, и
аналогичную пару образуют канал и URL. Эта аналогия не идеальна,
поскольку каналы создаются разработчиком вручную значительно реже, чем
потоки. Как правило, работа с каналами скрыта в глубине того или иного
протокола. Второе различие состоит в том, что канал представляет собой
усовершенствованную реализацию запроса (объект nsIRequest ), который, в
свою очередь, является модернизированным вариантом объекта-URL. Так
что, строго говоря, канал и URL не являются независимыми
объектами.
Как правило, работа с каналами начинается с обращения к следующей паре XPCOM:
@mozilla.org/network/io-service;1 nsIIOService
Этот компонент позволяет получить интерфейс nsIIOService при помощи
метода getService(). Применительно к транспортам данный интерфейс
фактически представляет собой службу имен для nsIIOService представляет собой отправную точку для
получения содержимого определенного URL.
С помощью интерфейса nsIIOService можно создавать объекты с
интерфейсами nsIURI и nsIURL. Каждый такой объект представляет
определенный URL подобно тому, как объект с интерфейсом nsIFile
представляет определенный файл. Указанные интерфейсы также
предоставляют объекты для управления протоколом, соответствующим
данной схеме или URL (protocol newChannelFromURI() интерфейса nsIIOService.
Имея доступ к объекту-каналу, можно получить с его помощью объект-
поток и приступить к обработке данных. Поток необходим для работы с
получаемым содержимым. Интерфейс nsIIOService содержит ряд полезных
методов, с помощью которых во многих случаях можно обойтись без
непосредственного обращения к объекту, управляющему протоколом.
При обращении к URL каналы выполняют целый ряд рутинных операций –
устанавливают соединение и получают содержимое, преобразуют его,
получают и хранят информацию о типе
| Интерфейс канала | |
|---|---|
nsIChannel |
Все типы, перечисленные ниже |
nsICachingChannel |
http: |
nsIDataChannel |
data: |
nsIEncodedChannel |
http: |
nsIFileChannel |
file: |
nsIFTPChannel |
ftp: |
nsIHttpChannel |
http: |
nsIImapMockChannel |
: |
nsIInputStreamChannel |
@mozilla.org/network/input- |
nsIJarChannel |
: |
nsIMultiPartChannel |
для внутреннего использования |
nsIResumableChannel |
ftp: ( http: пока не поддерживается) |
nsIUploadChannel |
file:, ftp:, http: |
nsIViewSourceChannel |
view-source: |
nsIWyciwygChannel |
wyciwyg: (не путать с ' ) |
Как видно из таблицы 16.8, большинство каналов соответствуют схемам
URL. Все каналы поддерживают базовую функциональность интерфейса nsIChannel, в первую очередь, – методы open() и asyncOpen(). Эти
методы возвращают поток или объект – слушатель потока. Другие
интерфейсы каналов лишь дополняют эту базовую функциональность с
учетом специфики конкретных протоколов. Их не следует рассматривать
как принципиально отличные каналы – скорее это расширения базового
канала.
Несколько интерфейсов, приведенных в таблице, заслуживают особого
упоминания. Так, nsIUploadChannel ориентирован на загрузку содержимого
с локальной машины на удаленный сервер. Вместо того чтобы возвращать
поток, он принимает его при инициализации и направляет данные потока
на сервер. Канал nsIResumableChannel используется для загрузки по
протоколу FTP, которая может приостанавливаться и возобновляться.
Аналогичная функциональность для протокола HTTP в классическом
браузере пока не поддерживается.
Еще один особый случай – канал для тривиальных
"протоколов". Канал может использоваться не только для
сложных сетевых протоколов, например FTP и HTTP, но и для простых
случаев передачи данных между диском и памятью (input-, приведенный в таблице, связан как раз с таким
использованием каналов.
Пример использования канала для доступа к локальному файлу приведен в листинге 16.6.
Источники данных обеспечивают поддержку работы с фактами,
необходимую для шаблонов XUL и хранилищ данных
Концепция источника данных воплощена в интерфейсе nsIRDFDataSource.
Этот интерфейс поддерживает все операции, необходимые для работы с
фактами
Отдельные факты могут быть сконструированы из простых объектов
XPCOM, основанных на интерфейсах nsIRDFResource и nsIRDFLiteral. Как
правило, читать из источника данных можно всегда, а записывать в него
– при определенных условиях. Источник данных предоставляет простую
функциональность, обеспечивающую добавление, удаление и изменение
данных, а также запросы к ним. В отличие от других механизмов
обработки данных, источники данных работают с логическими объектами
(фактами), а не с потоками байтов или символов.
Полезные интерфейсы XPCOM для работы с источниками данных можно разделить на три группы:
Вспомогательные средства и утилиты. Они необходимы для выполнения базовых операций.
Дополнительные расширения. Некоторые интерфейсы расширяют базовую
функциональность интерфейса nsIRDFDataSource для различных целей.
Поддержка содержимого. Существуют специализированные источники
данных для содержимого определенного типа. Эти источники
подразделяются на обычные, в которых данные берутся из файлов
Выбрав неверный источник данных из последней категории, можно
напрасно потратить часы, дни или даже недели на отладку программы.
Поэтому важно представлять себе поддерживаемые
Обратите внимание, что в именах методов интерфейса nsIRDFDataSource
слово source (источник) не относится к источникам данных. Source и target (цель) в этих именах означают субъект и объект факта
соответственно – см., например, метод GetSource().
При использовании шаблонов XUL доступ к объектам, имеющим интерфейс nsIRDFDataSource, обеспечивают объекты
Компоненты, используемые для непосредственной работы с window.Components.classes. Компоненты, приведенные в таблице, можно
разделить на следующие четыре группы.
| Имя компонента | Интерфейсы | Назначение |
|---|---|---|
@mozilla.org/ |
nsIRDFService |
Отправная точка; позволяет создавать источники данных nsIRDFDataSource, субъекты, объекты и предикаты nsIRDFNode |
@mozilla.org/ |
nsIRDFContainer |
Создает объекты-контейнеры <, <Seq> или <Alt> |
@mozilla.org/ |
nsIRDFContainerUtils |
Вспомогательные методы для работы с объектами-контейнерами |
@mozilla.org/ |
различные | Создание объектов, представляющих части факта |
@mozilla.org/ |
nsIExpatSink |
Превращает объекты XML, основанные на |
@mozilla.org/ |
nsIRDFXMLParser |
Превращает документ |
@mozilla.org/ |
nsIRDFXMLSerializer nsIRDFXMLSource |
Превращает хранилище фактов в документ |
@mozilla.org/ |
nsIRDFDelegateFactory |
Позволяет связать ресурс (элемент факта) с объектом- |
Первая группа из одного интерфейса представляет собой отправную
точку для работы с nsIRDFService позволяет
создавать объекты nsIRDFDataSource на основе :. Соответствующие URL перечислены в таблице 16.11. Объект,
представляющий подлежащее, дополнение или предикат факта, также может
быть создан при помощи этого интерфейса на основе строки JavaScript.
Для получения доступа к объекту, поддерживающему интерфейс nsIRDFService, используется метод getService(), а не createInstance().
Вторая группа компонентов, приведенных в таблице, предоставляет
интерфейсы-фабрики для создания контейнеров
imap mailbox news moz-abdirectory moz-abldapdirectory moz-abmdbdirectory moz-aboutlookdirectory
Третья группа компонентов предназначена для непосредственного
Наконец, применение последнего интерфейса в таблице 16.9, nsIRDFDelegateFactory, требует глубокого понимания архитектуры
платформы. Оно позволяет привязать какое-либо действие к созданию или
уничтожению ресурса, используемого в факте. Такой объект-
Mozilla предоставляет ряд возможностей для расширения функциональности источников данных. Расширение выражается скорее в большей гибкости источников, чем в доступе к каким-либо дополнительным данным. Эти возможности доступны при помощи ряда интерфейсов XPCOM, которые описаны в таблице 16.10.
| Имя интерфейса | Компоненты, реализующие интерфейс | Назначение |
|---|---|---|
nsIRDFDataSource |
Все источники данных, но см. ограничения в таблице 16.12 | Основные операции с хранилищем фактов, доступным с помощью источника данных |
nsIRDFCompositeDataSource |
@mozilla.org/ |
Составной источник данных, представляющий объединение фактов из одного или нескольких источников. Добавляемые факты добавляются в первый из этих источников |
nsIRDFInMemoryDataSource |
@mozilla.org./ |
Источник данных, основанный на хранилище фактов, которое не зависит от всех остальных фактов |
nsIRDFPurgeableDataSource |
@mozilla.org/ |
Позволяет удалить все факты в источнике данных |
nsIRDFPropagatableDataSource |
@mozilla.org/ |
Активизирует и отключает передачу событий, представляющих изменение фактов, любым наблюдателям |
nsIRDFRemoteDataSource |
@mozilla.org/autocompleteSession;1?type= |
Обеспечивает возможность синхронизации хранилища фактов, доступного с помощью источника данных, с исходным источником фактов.
Идентификатор контракта xml- преназначен для работы с произвольными файлами |
Система шаблонов XUL не только позволяет использовать составные
источники данных, но и поддерживает nsIRDFDataSource. Каждый метод этого
интерфейса просматривает отдельные источники данных, содержащиеся в
контейнере, и при необходимости поочередно вызывает соответствующие
методы каждого из них.
Источники данных, находящиеся в памяти, лежат в основе нескольких более сложных источников. Создание источника данных в памяти представляет собой естественную отправную точку в ситуации, когда содержание хранилища фактов не берется откуда-либо в готовом виде, а должно конструироваться динамически. Очистка такого источника от данных представляет собой его возвращение к исходному пустому состоянию. Отключение передачи событий, представляющих изменение фактов, несколько улучшает производительность источника.
Интерфейс nsIRDFRemoteDataSource предоставляет возможность
сохранить текущее состояние хранилища фактов в том месте, откуда были
взяты исходные данные, или загрузить данные оттуда в хранилище фактов.
Как правило, это место представляет собой файл , а загрузка – с помощью метода Refresh(). , поддерживается только для URL типа file:,а Refresh() – для
URL типов file: и http:.
Не все источники данных одинаковы. Они могут отличаться характером содержимого, а также способами доступа к нему. Эти различия – основная причина сложностей при использовании источников данных. С точки зрения разработчика приложений не всегда очевидно, какие именно факты доступны при помощи тех или иных источников данных, и каким образом их можно получить. Все источники данных основаны на компонентах XPCOM, имена которых имеют следующий вид:
@mozilla.org/rdf/datasource;1?name={arg}
Допустимые значения для {arg} приведены в крайнем левом столбце
таблицы 16.12.
Характер содержимого, которое доступно приложению при помощи
источника данных, очевиден только в случае обычных файлов
Внутренние источники данных, содержащие информацию о закладках,
истории просмотра страниц, поиске, а также некоторых типах внутренних
данных, используют файлы в папке
Такая "анонимность" содержимого представляет собой серьезную проблему для шаблонов XUL и скриптов, которые пытаются работать с внутренними источниками данных. В обоих случаях необходимо знать структуру данных заранее. К счастью, работа с внутренними источниками данных необходима лишь для узкого круга задач.
В разделе "Отладка" этой лекции приведены примеры кода,
позволяющего просматривать содержимое источников данных. В таблице
16.11 приведены субъекты верхнего уровня, а также часто используемые
предикаты для большинства внутренних источников данных. Специальное
значение означает отсутствие источника данных, а не пустой
источник.
Помимо специфики содержимого, для многих источников данных Mozilla
характерна ограниченная функциональность. Это означает, что, даже если
вам известна структура содержимого источника данных, функциональность
источника может оказаться недостаточной для того, чтобы получить
доступ к этому содержимому. В таких случаях, хотя соответствующий
объект XPCOM формально поддерживает интерфейс nsIRDFDataSource, многие
методы этого интерфейса не выполняют никаких действий, возвращая
исключение или сообщение об ошибке. Работа над этими компонентами –
источниками данных - еще не завершена.
Степень поддержки интерфейса nsIRDFDataSource для каждого из
существующих источников данных представлена в таблице 16.12. Данные
таблицы соответствуют версии Mozilla 1.4 и отражают лишь принципиальное наличие той или иной функции. Перед работой с
некоторыми редко используемыми источниками данных могут понадобиться
сложные подготовительные действия, которые не рассматриваются в этом
курсе.
Источники данных, не имеющие :, не могут быть
подключены к шаблону XUL при помощи атрибутов XML. Однако они могут
быть подключены к шаблону при помощи скрипта. Источники данных,
которые не зарегистрированы в XPCOM при параметрах сборки по
умолчанию, не могут быть использованы ни из XUL, ни из XML. Составной
источник данных
: |
Предикаты, используемые в качестве контейнера для фактов | |
|---|---|---|
|
moz-abdirectory:// |
http://home.netscape.com/NC-rdf#child и http://home.netscape.com/NC-rdf#CardChild |
|
NC:BookmarksRoot
NC:PersonalToolbarFolder
|
Использует контейнеры |
|
Различные (например, NC:BrowserCharsetMenuRoot ) |
Использует контейнеры |
|
NC:FilesRoot |
http://home.netscape.com/NC-rdf#child |
|
NC:HistoryRoot
NC:HistoryByDate
|
http://home.netscape.com/NC-rdf#child |
|
URL индекса | |
|
NC:SearchEngineRoot
NC:LastSearchRoot
NC:SearchResultsSitesRoot
NC:FilterSearchUrlRoot
NC:FilterSearchSitesRoot
SearchCategoryRoot
LastSearchMode
|
http://home.netscape.com/NC-rdf#child |
|
||
|
Нет; используйте любой |
Контейнеры не используются; каждый |
|
Корневым является любой find: |
Контейнеры не используются; каждый |
|
Корневой |
http://home.netscape.com/NC-rdf#child |
|
msgaccounts:/ |
|
|
Корневой |
|
|
NC:smtpservers |
http://home.netscape.com/NC-rdf#child |
|
Корневой |
http://home.netscape.com/NC-rdf#child |
|
NC:WindowMediatorRoot |
Использует контейнеры |
| Имя, используемое в идентификаторе контракта | Есть ли URL с префиксом :? |
Зарегистрирован ли в XPCOM при параметрах сборки по умолчанию? | Интерфейс XPCOM для определенного |
Поддержка |
Поддержка ArcLabelsOut() |
Поддержка GetAllResources() |
Поддержка команд |
|---|---|---|---|---|---|---|---|
addressdirectory |
v | v | v | v | v | v | |
|
v | v | v | v | v | v | |
|
v | v | v | v | v | v | |
files |
v | v | v | ||||
|
v | v | v | v | v | ||
httpindex |
v | v | v | v | v | v | v |
internetsearch |
v | v | v | v | v | ||
ispdefaults |
v | v | |||||
local- |
v | v | v | v | v | ||
localsearch |
v | v | v | ||||
mailnewsfolder |
v | v | v | v | v | v | |
msgaccountmanager |
v | v | v | v | |||
msgfilters |
v | v | v | ||||
smtp |
v | v | v | ||||
|
v | v | v | v | |||
window- |
v | v | v | v | v | v | v |
|
v | v | v | v | |||
mailsounds |
v | ||||||
|
v | ||||||
relatedlinks |
v | v | v | ||||
in-memory- |
v | v | v | v | |||
|
v | возможно | v | v | |||
xml- |
v | v | только для URL с префиксом file: |
v | v | v |
Web-браузеры функционируют в среде, которая отличается от среды выполнения традиционных программ. В WWW нет таких сущностей как файл или имя файла. Вместо них имеются указатели ресурсов (URL), а также документы, представляющие эти ресурсы. Часто такие документы имеют сложную структуру и основаны на языке XML. Такая среда требует иного подхода к написанию скриптов, нежели описанный в разделах "Файлы и папки" и "Потоки".
Кроме того, браузеры работают с формирующимся стеком Web-протоколов,
который можно условно назвать стеком XML. В основе этого
стека лежит протокол HTTP, поверх которого реализуются различные
прикладные протоколы.
Этот еще не вполне устоявшийся
Вряд ли полномасштабная поддержка всех перечисленных стандартов будет когда-либо реализована в рамках платформы Mozilla, поскольку некоторые из них ориентированы на взаимодействие между корпоративными приложениями, а не между приложением и пользователем. Область применения стека Web-протоколов выходит далеко за рамки обычных задач клиентского ПО, например просмотра Web-страниц или получения электронной почты. Вместо каналов, которые являются стандартным средством работы с URL на платформе Mozilla, при работе с этими протоколами используются специализированные интерфейсы.
Приложение на платформе Mozilla может вообще не иметь собственного
графического интерфейса. Например, оно может быть основано на
инструменте xpcshell. Однако и в этом случае приложение может
использовать развитые средства для работы с XML, входящие в состав
платформы. Таким образом можно создавать серверы, реализующие
некоторые идеи протоколов для бизнес-процессов. Например, такой сервер
может перенаправлять получаемые документы XML в зависимости от их
содержания.
Формат
Платформа Mozilla позволяет представить
@mozilla.org/network/simple-uri;1 nsIURI
Для URL существует специализированный объект, который поддерживает
типичные http: and ftp:. Соответствующая пара
XPCOM широко применяется при разработке на платформе Mozilla:
@mozilla.org/network/standard-url;1 nsIURL
Этот компонент также поддерживает nsIURI. Вообще, большинство
компонентов, идентификатор контракта которых содержит подстроку
или url, поддерживают оба этих интерфейса или один из них.
Часто требуется проверять корректность URL, введенного пользователем. В рамках платформы предусмотрено несколько инструментов проверки и исправления синтаксиса. Следующая пара XPCOM может использоваться при попытке исправить некорректный URL, введенный пользователем:
@mozilla.org/docshell/urifixup;1 nsIURIFixup
Этот интерфейс содержит метод createFixupURI(), который может
применяться для работы с ключевыми словами, введенными в строку адреса
браузера, а также сокращенными формами URL, например www.test.com или
даже test.com вместо http://www.test.com. Соответствующий компонент docshell доступен как элемент объектной модели приложения в окне
браузера Mozilla (подробнее об этом рассказано в разделе
"<iframe>" лекции 10 "Окна и панели").
Второй способ исправления синтаксиса URL связан с использованием следующей пары XPCOM:
@mozilla.org/network/url-parser;1?auth=maybe nsIURLParser
Этот интерфейс позволяет выполнить разбор URL в соответствии с
Наконец, можно задействовать метод базового интерфейса nsIURI. Если передать этому методу сокращенный (
В любом случае, окончательной проверкой правильности URL является то, удается ли получить с его помощью нужный ресурс.
Специализированного объекта XPCOM, предназначенного для
Чтобы загрузить с удаленного сервера файл или документ, можно
создать канал для нужного
@mozilla.org/embedding/browser/nsWebBrowserPersist;1 nsIWebBrowserPersist
В качестве параметров этот интерфейс принимает nsILocalFile. С его помощью можно
загрузить документ и сохранить его в виде файла при помощи
единственного вызова метода.
Чтобы организовать асинхронную загрузку, во время которой могут
выполняться другие задачи, нужно создать объект – слушатель или
наблюдатель содержимого – на чистом JavaScript. В документации к
большинству интерфейсов, например nsIChannel, описано, какие слушатели
и наблюдатели поддерживаются данным интерфейсом. При загрузке
очередной порции документа, которая будет передана слушателю или
наблюдателю, можно обработать ее, сохранить или просто
проигнорировать.
Наиболее распространенный способ создания объекта для обработки
содержимого по частям – реализовать интерфейс nsIWebProgressListener.
Любой объект, поддерживающий интерфейс nsIWebProgress, может
зарегистрировать один или несколько таких слушателей, а многие другие
интерфейсы принимают такой объект-слушатель в качестве аргумента
инициализации. Существуют готовые объекты XPCOM, поддерживающие этот
интерфейс, поэтому во многих случаях реализовывать его самостоятельно
не понадобится.
Отслеживать состояние процесса асинхронной загрузки можно по-
разному. В принципе, обработка поступающего содержимого и получение
информации о
Наиболее простой способ отслеживать состояние загрузки – доработать обычный слушатель содержимого так, чтобы он при получении очередной порции данных определял, какая часть содержимого уже загружена. Такой слушатель не получает событий, связанных с завершением загрузки, и других управляющих событий; он лишь отслеживает состояние процесса загрузки.
Более удобный способ – с помощью чистого JavaScript создать объект,
поддерживающий интерфейс nsIProgressEventSink, и передать его объекту,
ответственному за загрузку. Такой объект-приемник (слушатель событий)
получает информацию обо всех изменениях в nsIRequestObserver, и передать его объекту канала или транспорта.
Такой наблюдатель получает только уведомления о начале и окончании
загрузки, но не о ее промежуточном состоянии, приостановке или
прерывании.
Более сложный способ отслеживания процесса загрузки предполагает использование объектов XPCOM, связанных с Менеджером загрузок Mozilla. Эти объекты могут применяться независимо от диалогового окна менеджера загрузок или вместе с ним. В последнем случае разработчик должен самостоятельно обеспечить взаимодействие объектов XPCOM с диалоговым окном. Независимо от использования диалогового окна этот способ может применяться лишь при загрузке файлов для последующего сохранения на диске. Отправной точкой для использования этого способа является следующая пара XPCOM:
@mozilla.org/download-manager;1 nsIDownloadManager
Единственный объект, созданный таким образом, одновременно
управляет всеми текущими загрузками. Метод addDownload() этого объекта
используется для создания нового объекта, поддерживающего интерфейс nsIDownload, для каждой загрузки.
Каждый такой объект, соответствующий отдельной загрузке, содержит
всю конфигурационную информацию о ней и уведомляет Менеджер загрузок о
ходе процесса. Конфигурационная информация передается объекту в момент
его создания в виде аргументов метода addDownload(). В частности,
последний аргумент, имеющий интерфейс nsIWebBrowserPersist, содержит
информацию о том, каким образом должен быть сохранен полученный файл.
Если этот аргумент существует, объект загрузки автоматически
уведомляет Менеджер загрузок о
Mozilla также поддерживает группы загрузки. Это вариант интерфейса nsIRequest, который позволяет одновременно работать с несколькими URL.
Группы загрузки могут использоваться, если необходимо получать
информацию о процессе загрузки нескольких файлов.
Тип
@mozilla.org/mime;1 nsIMIMEService
Прежде всего, этот объект обращается к информации о типах file(1).
Метод интерфейса nsILocalFile
позволяет запустить исполняемый файл или открыть файл данных при
помощи соответствующего приложения. Для вызова метода
нет необходимости знать что-либо о типе файла.
Для отправки форм можно использовать объект AOM (объектной модели
приложения) . Работа с ним описана в разделе
"Отправка форм" лекции 7 "Формы и меню". Он основан
на следующей паре XPCOM:
@mozilla.org/xmlextras/xmlhttprequest;1 nsIJSXMLHttpRequest
Загрузка документов на сервер столь же проста. Следуйте
рекомендациям раздела "Каналы" этой лекции и укажите адрес
для загрузки, используя интерфейс nsIIOService. В качестве адреса для
загрузки может быть указана программа, выполняемая на стороне сервера,
в случае применения запроса HTTP POST или каталог FTP в случае
использования протокола FTP. Создав канал, следует получить интерфейс nsIUploadChannel при помощи метода QueryInterface(), а затем передать
этому интерфейсу поток ввода с содержимым файла, который должен быть
загружен. Чтобы отправить содержимое, нужно снова получить интерфейс nsIChannel, и вызвать метод open() или asyncOpen(), как и при работе с
любым объектом канала.
Протокол HTTP, лежащий в основе стека Web-протоколов, широко
применяется в Mozilla. Скрипты могут работать с ним различными
способами, включая непосредственное использование объекта AOM . Работа с прочими протоколами, поддерживаемыми Mozilla,
требует применения специализированных объектов.
Документация по использованию Web-протоколов в Mozilla доступна по адресу http://www.mozilla.org/xmlextras/.
XML-RPC представляет собой простейшую надстройку над HTTP. Следующая пара XPCOM используется для формирования фрагмента XML, содержащего запрос RPC, его синхронную или асинхронную передачу по указанному URL с помощью HTTP, а также возвращение результатов или сообщений об ошибках:
@mozilla.org/xml-rpc/client;1 nsIXmlRpcClient
Сообщения об ошибках возвращаются в виде объектов с интерфейсом nsIXmlRpcFault.
При использовании традиционного RPC применяется утилита rpcgen(1)
или другие аналогичные инструменты, которые генерируют код на языке C
для удаленного вызова процедуры. Этот код:
Особенность использования XML-RPC на платформе Mozilla состоит в
том, что JavaScript является интерпретируемым языком, а код платформы,
работающий с удаленным вызовом процедур, как правило, уже
скомпилирован. Поэтому порядок работы с RPC отличается от
традиционного подхода. Объект nsIXmlRpcClient упаковывает
вызовы XML-RPC в переносимый XML, но передачу вызова и получение
результата он делегирует объекту канала. Поэтому для управления
задержками или получения информации о них необходимо обращаться именно
к объекту канала. Задача преобразования типов JavaScript в переносимые
типы XML-RPC оставлена разработчику приложения. Объект nsIXmlRpcClient предоставляет ряд методов-фабрик для создания
типов XML-RPC, однако разработчик должен воспользоваться этими
методами самостоятельно. Наконец, этот объект реализован на JavaScript
и постоянно обращается к объекту window.Components для
разрешения имен, что ограничивает его производительность.
Протокол
Вызов
Для того чтобы разработчик мог создать сообщение
Ниже перечислены пары XPCOM для создания объектов, соответствующих каждой из задач:
@mozilla.org/xmlextras/schemas/schemaloader;1 nsISchemaLoader@mozilla.org/xmlextras/soap /call;1 nsISOAPMessage@mozilla.org/xmlextras/soap /transport ;1?protocol=http;nsISOAPTransport@mozilla.org/xmlextras/soap /call;1 nsISOAPCall@mozilla.org/xmlextras/soap /fault ;1 nsISOAPFault@mozilla.org/xmlextras/soap /response;1 nsISOAPMessageПомимо этих объектов, обеспечивающих базовую функциональность для
работы с
Интерфейс nsISOAPMessage также доступен в форме объекта AOM SOAPCall, создать который очень просто:
var soap_call = new SOAPCall();
Аналогичным образом интерфейсу nsISOAPParameter соответствует
объект AOM SOAPParameter. Эти два объекта позволяют выполнять простые
вызовы
Широко известен сервис
http://lxr.mozilla.org/seamonkey/source/extensions/xmlextras/tests/
В этом каталоге находятся три небольших программы на языке Perl,
которые могут получать
Поддержка
Последний протокол стека Web-протоколов, поддерживаемый Mozilla, –
Поддержка
http://lxr.mozilla.org/seamonkey/source/extensions/xmlextras/wsdl/
Чтобы узнать идентификаторы контрактов (
@mozilla.org/xmlextras/wsdl/
Полная поддержка
Для обращения к системе обработки
@mozilla.org/document-transformer;1?type=text/xsl nsIXSLTProcessor
Объект с таким интерфейсом принимает в качестве аргументов два
дерева или поддерева
Некоторым скриптам необходимо получать информацию о состоянии самой платформы Mozilla или изменять это состояние. Для этого скрипты должны получить доступ к различным аспектам внутреннего состояния платформы при помощи интерфейсов XPCOM. В этом разделе обсуждаются управление кэшем, каталог файловой системы, настройки, защита, а также профили пользователя.
Предполагается, что кэш браузера Mozilla прозрачен для любых действий по получению Web-документов, однако при необходимости с ним можно взаимодействовать. Кэш используется для любых запросов к URL, выполняемых платформой, если в явном виде не было указано избегать использования кэша или отключить его. Доступ к кэшу на низком уровне осуществляется при помощи следующей пары XPCOM:
@mozilla.org/network/cache-service;1 nsICacheService
Объект, полученный таким образом, должен иметь доступ к константам,
предоставляемым интерфейсом nsICache. Техника работы с кэшем на низком
уровне является неожиданно сложной в силу используемой модели
блокировки, которая допускает несколько одновременных сеансов чтения,
но лишь один сеанс записи. Это означает, что попытка работы с кэшем на
низком уровне может оказаться неудачной по причине конфликта из-за
доступа к ресурсам. Разработчику приложений проще не иметь дела с
этими тонкостями и использовать сервисы более высокого уровня,
например транспорты и каналы, которые взаимодействуют с кэшем
автоматически. Однако указанный интерфейс предоставляет и ряд методов,
полезных для разработчика приложений, например evictEntries(), который
может использоваться для очистки кэша.
Очень простое применение кэша – упреждающая загрузка документов.
Такая загрузка подразумевает помещение документа в кэш без
обязательного отображения или какой-либо обработки. Упреждающая
загрузка может выполняться лишь для URL с префиксом http:, которые не
являются запросами GET (не имеют части вида ?param= ). Для упреждающей
загрузки используется следующая пара XPCOM:
@mozilla.org/prefetch-service;1 nsIPrefetchService
Аналогичная функциональность с возможностью более детального
управления доступна при помощи интерфейса nsIRequest, который является
основой для каналов и транспортов. Свойство loadFlags, которое может
устанавливаться для каждого
Платформа Mozilla поддерживает
Каталоги (
Разработчику приложений целесообразно использовать эту службу
каталогов, если приложение должно задействовать те же файлы и папки,
что и сама платформа Mozilla. Это позволяет обеспечить надлежащую
Эта
@mozilla.org/file/directory_service;1 nsIDirectoryService
Обратите внимание, что в названии directory_service использовано
подчеркивание, а не дефис. Этот каталог содержит пути ко всем важным
файлам и папкам, о которых должны знать приложения и их разработчики.
Поэтому каталог файловой системы является принципиально важным для
приложений на платформе Mozilla. После того, как объект,
представляющий файл или папку, получен с помощью каталога, с ним можно
обращаться так же, как с любым другим файлом или папкой.
Интерфейс nsIDirectoryService не слишком полезен сам по себе. Его
роль состоит в управлении объектами-источниками (providers). Источник
представляет собой объект, который предоставляет nsIDirectoryServiceProvider. Когда скрипт обращается с
запросом к
Помимо источников, nsIProperties – стандартный интерфейс, позволяющий
получить по имени объекта данные о нем, хранящиеся в каталоге. Нужное
имя (фактически, псевдоним искомого объекта) передается службе
каталогов при помощи метода get() интерфейса nsIProperties. В
результате будет возвращен любой объект (запись в каталоге), имя
которого соответствует переданному псевдониму. Как и другие службы
каталогов Mozilla, каталог файловой системы поддерживает этот
интерфейс. Пример его использования приведен в листинге 16.7:
var Cc = Components.classes;
var Ci = Components.interfaces;
var dir = Cc["@mozilla.org/file/directory_service;1"];
dir = dir.getService(Ci.nsIDirectoryService); // Инициализация
// Поместите сюда обращения к dir.registerProvider(provider_object)
var dir_props = dir.QueryInterface(Ci.nsIProperties);
var file = dir_props.get("myalias", Ci.nsIFile);
if (file == null )
alert("Нет такого ресурса");
Этот код создает объект nsIProperties и
запрашивает из каталога данные для псевдонима "myalias".
Поскольку каталог файловой системы XPCOM содержит информацию о файлах
и папках, ожидается, что будет возвращен объект, поддерживающий
интерфейс nsIFile.
В данном примере объект не будет найден в каталоге, и система
выдаст предупреждение (последняя строка кода). Это произойдет по двум
причинам. Во-первых, строка "myalias" в рамках платформы
Mozilla не определена в качестве псевдонима известных файла или папки.
Это легко исправить – достаточно использовать какой-либо известный
псевдоним. Однако есть и более серьезная проблема – к объекту службы
каталогов не было добавлено ни одного источника. Поэтому мы могли бы
ожидать, что в данном коде не будут распознаваться никакие псевдонимы.
Однако это не так. На практике, к объекту каталога файловой системы
всегда подключено, как минимум, два источника.
Дополнительную сложность работе со службой каталогов придает тот факт, что объект, который представляет каталог файловой системы, одновременно реализует интерфейс источника. Этот источник определяется следующей парой XPCOM:
@mozilla.org/file/directory_service;1 nsIDirectoryServiceProvider
Такой источник представляет собой дополнение к трем, перечисленным
выше. Его редко регистрируют в каких-либо
Пример непосредственной работы с этим источником приведен в листинге 16.8.
var Cc = Components.classes;
var Ci = Components.interfaces;
var prov = Cc["@mozilla.org/file/directory_service;1"];
prov = prov.getService(Ci.nsIDirectoryServiceProvider);
var result = {}; // пустой объект
var file = prov.getFile("alias", result);
if ( file == null ) alert("Нет такого ресурса");
// alert(result.value)
Поскольку обычно источники управляются объектом, представляющим
getFile() приспособлены для
использования с таким объектом. Второй аргумент этого метода, пустой
объект, позволяет источнику передать nsIDirectoryServiceProvider.
В оставшейся части раздела перечисляются псевдонимы, поддерживаемые названными источниками. Мы начнем с последнего, специализированного источника.
Эти псевдонимы поддерживаются специализированным источником,
встроенным в объект
| Псевдоним | Описание соответствующего nsIFile |
|---|---|
ComRegF |
Реестр компонентов XPCOM – не используется |
ComsD |
Папка, содержащая компоненты XPCOM |
CurProcD |
Папка с исполняемым файлом текущего процесса, в UNIX всегда определяется переменной окружения $MOZILLA_FIVE_HOME |
CurWorkD |
Папка, которая является текущим рабочим каталогом исполняемого файла |
DrvD |
Корень файловой системы операционной системы – в Windows обычно C:; в UNIX: /; в MacOS: корневой том |
GreComsD |
Папка, содержащая компоненты XPCOM, относящиеся к GRE (Gecko Runtime Engine) |
GreD |
Папка, в которой установлен GRE |
Home |
Домашняя папка (каталог) текущего пользователя – в Windows: %HOME%; в UNIX: $HOME; в MacOS: папка документов |
TmpD |
Папка операционной системы для хранения временных файлов – в Windows: %TMP%; в UNIX: $TMP; в MacOS: папка временных файлов |
В таблице 16.14 перечислены псевдонимы, которые поддерживаются
только в системе Microsoft Windows. Константы CSIDL, приведенные в
таблице, являются частью программного интерфейса (API) Microsoft
Windows и используются в таких функциях Windows, как, например, SHGetFolderPath(). Каждый псевдоним соответствует известной папке.
Полное описание этих констант приведено в документе по адресу:
http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp
| Псевдоним | Эквивалентная константа CSIDL | Псевдоним | Эквивалентная константа CSIDL |
|---|---|---|---|
AppData |
CSIDL_APPDATA |
netH |
CSIDL_NETHOOD |
Buckt |
CSIDL_BITBUCKET |
NetW |
CSIDL_NETWORK |
CmDeskP |
CSIDL_COMMON_DESKTOPDIRECTORY |
Pers |
CSIDL_PERSONAL |
CmPrgs |
CSIDL_COMMON_PROGRAMS |
PrntHd |
CSIDL_PRINTHOOD |
CmStrt |
CSIDL_COMMON_STARTUP |
Prnts |
CSIDL_PRINTERS |
Cntls |
CSIDL_CONTROLS |
Progs |
CSIDL_PROGRAMS |
DeskP |
CSIDL_DESKTOPDIRECTORY |
Rcnt |
CSIDL_RECENT |
DeskV |
CSIDL_DESKTOP |
SndTo |
CSIDL_SENDTO |
Drivs |
CSIDL_DRIVES |
Tmpls |
CSIDL_TEMPLATES |
Favs |
CSIDL_FAVORATES |
WinD |
CSIDL_WINDOWS |
Нужно иметь в виду, что использование псевдонимов с префиксом Cm в
однопользовательских версиях Windows, например Microsoft Windows 98,
приводит к возникновению
В таблице 16.15 приведены псевдонимы, поддерживаемые только на
платформе Macintosh. Прочие псевдонимы перечислены в таблице 16.16.
Псевдонимы, поддерживаемые в таких системах, как OS/2,
| Псевдоним | Папка | Псевдоним | Папка |
|---|---|---|---|
ApplMenu |
Меню (Apple Menu) | |
Папка расширений (Extensions folder) |
ClassicPrfs |
Папка профиля Mac Classic | Isrch |
Папка поиска в Internet |
CntlPnl |
Панель управления | |
Папка настроек (Preferences folder) |
DfltDwnld |
Папка загрузок по умолчанию (Default |
Shdwn |
Папка отключения ( |
Docs |
Папка документов | Trsh |
Папка мусорной корзины |
Desk |
Папка рабочего стола |
| Псевдоним | Описание соответствующего nsIFile |
|---|---|
Fnts |
Macintosh и Microsoft Windows: папка, содержащая |
LibD |
UNIX: /usr/local/lib/ |
Locl |
UNIX: /usr/local/ |
Strt |
Macintosh и Microsoft Windows: папка запуска ("Автозагрузка") |
SysD |
Только Macintosh OSX: системная папка |
UlibDir |
Только Macintosh OSX: /usr/lib |
Эти псевдонимы предоставляются источником, который подключается к
Платформа Mozilla не сводится к системе XPCOM. Последняя образует
ядро платформы, поверх которого реализовано множество компонентов и
инфраструктура, также составляющие часть платформы. В состав платформы
входят папки, в которые установлены браузеры и другие продукты,
система
| Псевдоним | Соответствующий объект | Путь относительно папки установки |
|---|---|---|
AppRegF |
Файл глобального реестра приложений | Расположен в другом месте (см. лекцию 17 "Развертывание") |
AppRegD |
Папка, содержащая глобальный реестр приложений | Расположена в другом месте (см. лекцию 17 "Развертывание") |
DefRt |
Папка верхнего уровня для параметров по умолчанию | Defaults |
PrfDef |
Папка, содержащая настройки по умолчанию | Defaults/pref |
profDef |
Папка, содержащая параметры профиля по умолчанию для текущих параметров локализации | Defaults/profile/{locale} |
ProfDefNoLoc |
Папка, содержащая параметры профиля по умолчанию для параметров локализации по умолчанию | Defaults/profile |
DefProtRt |
Папка верхнего уровня для пользовательских профилей | Расположена в другом месте (см. ниже) |
Ares |
Папка ресурсов | |
Achrom |
Папка |
|
SrchPlugns |
Папка модулей поиска | Searchplugins |
ApluginsDL |
Список доступных nsIEnumerator ) |
Plugins/* |
XPIClnupD |
Папка, содержащая программы для удаления приложений | |
UserPlugins |
Папка в составе профиля текущего пользователя, содержащая установленные модули уровня пользователя | Расположена в другом месте |
OSXUserPlugins |
Папка, содержащая пользовательские модули (только MacOS X) | Расположена в другом месте |
OSXLocalPlugins |
Папка, содержащая локальные модули (только MacOS X) | Расположена в другом месте |
MacSysPlugins |
Папка, содержащая системные модули (только Mac Classic) | Расположена в другом месте |
Псевдоним DefProtRt возвращает следующие значения в
зависимости от платформы:
Эти псевдонимы предоставляются источником, который подключается к
| Псевдоним | Объект | Путь относительно папки |
|---|---|---|
PrefD |
Папка, содержащая файл пользовательских настроек; совпадает с ProfD |
- |
PrefF |
Файл пользовательских настроек | prefs.js |
ProfD |
Корневая папка текущего профиля | - |
Uchrm |
Папка, содержащая данные |
|
LclSt |
Файл конфигурации окон Mozilla для данного пользователя | localstore. |
Uhist |
Файл истории посещений классического браузера | |
Upanels |
Файл вкладок для боковой панели классического браузера, определяемых пользователем | panels. |
UmimTyp |
Информация о типах |
mimeTypes. |
Bmarks |
Файл закладок классического браузера | |
Dloads |
Файл истории загрузок классического браузера | |
SrchF |
Файл конфигурации |
search. |
MailD |
Папка, содержащая данные локальных почтовых ящиков | |
ImapMD |
Папка, содержащая данные учетных записей |
ImapMail |
NewsD |
Папка, содержащая информацию о конференциях | News |
MFCaD |
Файл, содержащий настройки отображения папок классического |
panacea. |
Псевдонимы, относящиеся к модулям (plugins), предоставляются
источником, который подключается к
Прочие источники
Известные псевдонимы представлены в таблице 16.19.
| Псевдоним | Описание соответствующего nsIFile |
|---|---|
plugin. |
Файл модуля Java (Mozilla OJI) |
plugin. |
Файл модуля Adobe Acrobat plugin file |
plugin. |
Файл модуля Apple |
plugin. |
Исполняемый файл |
plugin. |
Папка модулей |
Скрипты позволяют изменять текущие настройки
@mozilla.org/preferences-service;1 nsIPrefService
По умолчанию скрипты, не установленные в
В этом разделе рассказано, как реализована система защиты информации на платформе Mozilla. Поскольку защита вообще представляет собой обширную тему, мы расскажем только о тех ограничениях защиты, которые непосредственно влияют на работу скриптов.
В браузерах
Фрагмент кода Mozilla может находиться в одном из четырех режимов
защиты: Web
Поддержка протокола WDSL, недавно реализованная в составе
платформы, привела к добавлению дополнительных мер защиты. Они
подразумевают дополнительную проверку при первом обращении к
удаленному Web-сервису – платформа должна запросить разрешение на его
использование. Это делается с целью защиты сервера, предоставляющего
Web-сервис, а не локальной платформы, которая обращается к нему. В
момент подготовки книги к печати предполагалось, что на стороне
клиента для такой проверки будет использоваться интерфейс nsIWebScriptsAccessService.
Большинство механизмов защиты, хотя и не все, реализованы в составе кода XPConnect, который обеспечивает доступ скриптов JavaScript к функциональности платформы Mozilla.
В любом случае, сообщение о попытке нарушения ограничений защиты выдается на консоль JavaScript.
Режим Web
Первая группа ограничений призвана обеспечить контроль пользователя над интерфейсом и сделать заметными любые действия с ним. Пример такого ограничения – требование, чтобы любые окна имели размер не менее 100 пикселей в ширину и высоту и, следовательно, были заметны для пользователя.
Второй
Принцип "общего источника" не позволяет скриптам
воздействовать на содержимое других окон приложения или даже на
содержимое других фреймов в том же окне, если это содержимое было
получено из других источников. Все содержимое
Принцип "общего источника" не распространяется на
специальный URL about:
Режим
Некоторая проблема заключается в том, что при добавлении к
Режим
Цифровые подписи и сертификаты – самостоятельная обширная тема,
поэтому здесь мы затронем лишь те ее аспекты, которые значимы для
прикладных скриптов. Для цифровой подписи скриптов и других ресурсов
используется утилита SignTool. Эта утилита не входит в состав Mozilla,
но доступна вместе с документацией на ресурсе компании
С использованием
Разница между режимами защиты
Режим защиты
user_pref("signed.applets.codebase_principal_support",
true);
Эта настройка полезна, главным образом, при разработке и отладке
приложений, которые будут выполняться в режиме защиты
Если приложение использует модель защиты
window.netscape.security.PrivilegeManager.enablePrivilege("
P1 P2 P3");
Эта функция запрашивает у пользователя подтверждение, открывая
диалоговое окно, и, в случае согласия, предоставляет скрипту
необходимые привилегии. Если соответствующее разрешение было дано
раньше и сохранено в настройках пользователя, привилегии
предоставляются без обращения к пользователю. При этом скрипт получает
не все права, соответствующие режиму
В данном примере "P1 P2 P3" представляет собой список
ключевых слов привилегий, разделенных пробелами. В вызове метода enablePrivilege должно быть использовано, по крайней
мере, одно ключевое слово. Допустимые ключевые слова, а также
функциональность, на которую распространяются соответствующие
привилегии, приведены в таблице
16.20. Во внутренней реализации платформы наличие необходимых
привилегий проверяется в разных местах, что обеспечивает высокий
уровень защиты.
| Ключевое слово | Функциональность |
|---|---|
UniversalBrowserRead |
Чтение содержимого из любых окон браузера; позволяет успешно проходить проверку "общего источника" для чтения любого документа |
UniversalBrowserWrite |
Изменение содержимого любых окон браузера; позволяет успешно проходить проверку "общего источника" для модификации любого документа |
UniversalXPConnect |
Неограниченный доступ к компонентам XPCOM из JavaScript при помощи XPConnect |
UniversalPreferencesRead |
Чтение настроек при помощи метода navigator.preference() |
UniversalPreferenceWrite |
Изменение настроек при помощи метода navigator.preference() |
CapabilityReferencesAccess |
Чтение и изменение настроек, определяющих политику защиты, включая информацию о том, какие привилегии были предоставлены скриптам, и в каких им было отказано; для этого также необходимы привилегии UniversalPreferencesRead и/или UniversalPreferencesWrite |
UniversalFileRead |
Отображение или отправка любых файлов, имеющих URL типа file: |
Данные, приведенные в таблице, любезно предоставлены Джесси Рудерманом и mozilla.org.
В режиме защиты Domain
С другой стороны, она является наиболее гибкой – при ее
использовании на выполнение скриптов может быть наложено как меньше,
так и больше ограничений, чем в режиме Web
Большинство параметров, связанных с режимом Domain
Данная система защиты реализована для следующих целей:
Чтобы использовать эту модель защиты, необходимо добавить к пользовательским настройкам определенные правила. Это можно сделать в три этапа: определить имя политики защиты; задать источники, к которым применима эта политика; задать правила доступа к конкретным объектам для данной политики. Ниже мы последовательно рассмотрим все эти этапы.
Существует три типа имен политик защиты – явные имена, групповое имя и политика по умолчанию. Эти имена образуют иерархию. Каждое свойство или объект, для которых может быть определено правило доступа, могут быть связаны с одним из имен каждого типа.
На нижнем уровне иерархии находятся политики по умолчанию. Для каждого свойства JavaScript существует одна политика по умолчанию, которая применяется в том случае, если для этого свойства не определено других политик. Если никакие политики по умолчанию в явном виде не определены, ко всем свойствам применяется единственная политика по умолчанию, имеющая имя "default". Вы можете изменять эту политику, как и другие политики по умолчанию. Из дальнейшего изложения станет ясно, почему целесообразно использовать несколько политик по умолчанию.
На следующем уровне находится
На вершине иерархии находятся политики, заданные явными именами. Их имена всегда должны быть определены в явном виде. Эти политики имеют приоритет над всеми прочими.
Для задания имен политик используются следующие настройки:
user_pref("capability.policy.policynames","p1 test foo");
user_pref("capability.policy.default_policynames","normal,off");
Имена политик не могут содержать символ точки, списки имен
разделяются пробелами или запятыми. В первой строке заданы имена трех
политик; во второй строке заданы имена двух политик по умолчанию.
Существует единственная
Определив имена политик, необходимо задать для каждой политики список источников. Каждая политика применяется лишь к документам, полученным из указанных для нее источников. Ниже приведен пример задания источников для политики с именем mypol:
user_pref("capability.policy.mypol.sites", "http://test.com http://x.org");
Значением свойства в данном случае является список URL, разделенных
пробелами или запятыми. URL может относиться к серверу в целом, но не
к отдельным его каталогам. Каждый сайт не может входить более чем в
одну строку настроек такого вида. Если указанная политика является
политикой по умолчанию, именно она будет политикой по умолчанию для
перечисленных сайтов. Это позволяет использовать для различных сайтов
различные политики по умолчанию. Если вы хотите задать сайты, к
которым должна применяться
После того, как заданы имена политик и соответствующие источники,
остается определить правила доступа. Существует три типа правил.
Каждому правилу соответствует одна строка в
Первый и наиболее общий тип синтаксиса правил применим ко всем свойствам JavaScript независимо от того, являются ли они простыми значениями или же методами. В случае политики mypol правило имеет следующий вид:
user_pref("capabilities.policy.mypol.Iface.Prop","Keywords")
Iface, Prop и
необходимо заменить соответствующими строками.
Iface – ChromeWindow, HTMLDocument и XULImageElement. Некоторые объекты Image, однако в данном случае должно использоваться полное
имя – HTMLImageElement.Prop – имя свойства, к которому относится правило
доступа. Как правило, оно представляет собой атрибут или метод
интерфейса XPCOM, например свойство value для многих элементов Keywords – разделенный пробелами или запятыми список
имен привилегий, приведенных в таблице
16.20 или одно из следующих ключевых слов: AllAccess, NoAccess
или sameOrigin. AllAccess эквивалентно указанию всех ключевых слов из
таблицы 16.20. sameOrigin означает, что будут применяться ограничения
режима Web NoAccess полностью запрещает доступ к свойству как на
чтение, так и на запись.Ниже приведен пример правила:
user_pref("capabilities.policy.*.History.back","NoAccess");
Это правило означает, что back() объекта nsIDOMHistory. Этот объект
используется только в браузере Mozilla, и приведенное правило
отключает функцию возврата к ранее просмотренным страницам.
Второй тип синтаксиса применяется только к атрибутам JavaScript
(свойствам, не являющимся методами) и позволяет разрешать или
запрещать операции
user_pref("capabilities.policy.mypol.Iface.Prop.Access","Keyword");
Iface и Prop имеют то же значение, что и в предыдущем случае. должно иметь одно из следующих значений: AllAccess, NoAccess
или sameOrigin. Access должно быть одной из двух строк – set или get.
Таким образом, этот синтаксис допускает два правила для каждого
свойства – для чтения и для записи. Ниже приведен пример правила,
которое запрещает изменять текст заголовка окон XUL:
user_pref("capabilities.policy.default.ChromeWindow.title.set","NoAccess");
Третий тип синтаксиса относится к JavaScript в целом. Он позволяет при помощи единственного правила полностью запретить или разрешить выполнение скриптов JavaScript для группы источников:
user_pref("capabilities.policy.mypol.javascript.enabled","Keyword");
В этом случае можно задать лишь имя политики и значение строки . Вместо можно поставить одно из двух значений: NoAccess (отключить JavaScript) или AllAccess (активизировать
JavaScript). Если JavaScript отключен глобально, правила такого типа
игнорируются.
Типичная политика содержит ряд правил такого рода, разрешающих или
запрещающих определенные действия. Если вы хотите запретить какое-либо
действие, будьте внимательны и задайте правила для всех свойств,
которые могут обеспечивать доступ к этому действию. Чтобы получить
полный список свойств, следует изучить нужный объект при помощи
Инспектора
Особенно тщательно следует подходить к ограничению доступа к
связкам XBL. Такая связка часто содержит множество вспомогательных
методов и свойств, функциональность которых может пересекаться.
Поэтому при необходимости запретить изменение какого-либо свойства
одного из
Согласно "
Следует упомянуть еще несколько ограничений, которые не относятся к описанным режимам защиты. Эти ограничения действуют, по крайней мере, начиная с версии Mozilla 1.4.
На этом обсуждение системы защиты платформы Mozilla заканчивается.
@mozilla.org/profile/manager;1 nsIProfile
Однако интерфейс nsIProfile не позволяет осуществлять доступ к файлам внутри заданного профиля. Для этого используется другая пара XPCOM:
@mozilla.org/profile/manager;1 nsIProfileInternal
Это практическое занятие посвящено
В этом разделе мы завершим работу над приложением NoteTaker,
добавив к нему механизмы сохранения, удаления и загрузки заметок. Для
этого нам нужно создать необходимые источники данных
Мы также попытаемся усовершенствовать запросы
Как подобает при разработке программ, мы начнем с проектирования.
В лекции 14 "Шаблоны" мы сделали содержимое динамическим,
используя атрибуты элементов XUL. Для каждого шаблона был определен
собственный источник данных. Хотя это простой и удобный способ работы
с источниками, он предполагает, что разработчику известен точный URL
файла notetaker.
Чтобы учесть это изменение, мы перенесем интеграцию с данными
При описании шаблонов нам все равно понадобится указать источник
данных в коде XUL. Для этого мы используем "нулевой"
источник , предоставляемый платформой Mozilla. После загрузки
шаблонов мы создадим новый источник данных на основе объекта,
представляющего URL, и подключим его к каждому из шаблонов с помощью
JavaScript. В этом случае весь обмен данными
Это не единственный метод создания источника данных, совместно используемого несколькими шаблонами XUL. Если два шаблона имеют один и тот же атрибут, определяющий источник данных, оба они будут совместно использовать один набор фактов (общее хранилище фактов). В противном случае код, написанный нами в лекции 14, просто не смог бы корректно работать. Поэтому здесь мы всего лишь отделяем использование общего источника данных от XUL, чтобы иметь возможность работать с этим источником в виде отдельного объекта. В принципе, мы могли бы создать отдельный источник данных для каждого шаблона. Даже разные источники, основанные на одном и том же URL, фактически работают с одним общим набором фактов.
Получив объект источника данных, мы можем читать и записывать
данные при помощи функций JavaScript, а также многочисленных
интерфейсов для работы с
Чтобы усовершенствовать нашу объектную модель, мы дополним объект Note объектом NoteDataSource, представляющим
источник данных. В последний раз мы работали с объектом Note в
практическом разделе лекции 14. Всякий раз, когда нам нужно добавить
новую операцию с источником данных, мы можем реализовать ее как метод
нашего нового объекта.
Прежде всего, следует поместить копию файла notetaker.
Необходимое условие успешного создания источника данных – получение доступа к этому файлу. Мы начинаем, зная точное имя файла и имея представление о его местонахождении, а закончить должны, имея объект nsIRDFDataSource. Мы включим в код приложения имя файла, но не путь к нему. Чтобы создать источник данных, мы используем некоторые приемы, описанные в этой лекции.
Чтобы обнаружить файл независимо от платформы, мы используем службу
каталогов Mozilla. Изучая таблицы псевдонимов, приведенные выше в этой
лекции, мы обнаруживаем, что псевдоним ProfD, приведенный
в таблице 16.18, позволяет получить доступ к папке текущего профиля. На
основе этого псевдонима мы создадим объект nsIFile, представляющий
папку профиля, дополним путь к папке, чтобы получить нужный файл,
преобразуем файловый объект в URL и наконец создадим источник данных
на основе этого URL. Все эти операции представлены в листинге
16.9.
var Cc = Components.classes;
var Ci = Components.interfaces;
// Объект сеанса работы с NoteTaker
function NoteSession() {
this.init();
}
NoteSession.prototype = {
config_file : "notetaker.rdf",
datasource : null,
init : function (otherfile) {
var fdir, conv, rdf, file, url;
if (otherfile) this.config_file = otherfile;
with (window) {
fdir = Cc["@mozilla.org/file/directory_service;1"];
fdir = fdir.getService(Ci.nsIProperties);
conv = Cc["@mozilla.org/network/protocol;1?name=file"];
conv = conv.createInstance(Ci.nsIFileProtocolHandler);
rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
rdf = rdf.getService(Ci.nsIRDFService);
}
file = fdir.get("ProfD", Ci.nsIFile);
file.append(this.config_file);
if (!file.exists())
throw this.config_file + " is missing";
if (!file.isFile() || !file.isWritable() || !file.isReadable())
throw this.config_file + " has type or permission problems";
url = conv.newFileURI(file);
this.datasource = rdf.GetDataSource(url.spec);
}
};
var noteSession = new NoteSession();
Вся работа выполняется в методе init() объекта NoteSession. Сначала
мы создаем три объекта XCOM. Затем мы получаем папку текущего профиля
в виде объекта nsIFile. Метод , не возвращающий никакого
результата, изменяет этот объект так, что он в точности соответствует
нашему конфигурационному файлу. Затем мы убеждаемся, что нужный файл
существует, и что он доступен для чтения и записи. Предполагается, что
в нашем случае такой файл существует всегда, поскольку он будет
создаваться при установке приложения. Однако к реальному приложению
все же следовало бы добавить код, автоматически создающий файл в
случае его отсутствия – нельзя исключить возможность случайного
nsIFile в nsIURL с помощью метода newFileURI(), получаем URL в виде строки, используя свойство , и,
наконец, создаем источник данных на основе этой строки при помощи
метода GetDataSource().
Эта последовательность шагов – стандартные действия при подготовке
источников данных. При использовании внутреннего или удаленного
источника действия могут несколько отличаться от приведенного примера.
Так, если URL источника данных известен заранее, вся процедура может
свестись к вызову метода GetDataSource().
Теперь, когда мы получили источник данных, давайте используем его.
Мы хотим модифицировать существующие шаблоны так, чтобы они получали
данные от созданного нами объекта, а не из файла, указанного в коде
шаблона. Поэтому в коде шаблона мы укажем "нулевой" источник
данных , а реальный источник подключим
при помощи скрипта.
Мы полностью отказываемся от использования шаблона для текстового
поля <textbox> в панели инструментов NoteTaker. Это слишком
сложное решение для простого текстового поля. В предыдущих лекциях мы
использовали его с единственной целью: продемонстрировать один из
возможных способов работы с шаблонами. Однако шаблоны не являются
универсальным инструментом. Итак, код для текстового поля принимает
следующий вид:
<textbox id="notetaker-toolbar.summary"/>
Поле заполняется с помощью функции refresh_toolbar(), которая
копирует нужное значение из объекта заметки. Таким образом, задача
обновления текстового поля решена без обращения к шаблонам.
Раскрывающийся список ключевых слов в панели инструментов основан на стандартном использовании шаблона, и мы могли бы оставить его без изменений, если бы источник данных можно было указать в коде XUL. Однако, поскольку теперь местонахождение файла заметок нам заранее неизвестно, мы должны изменить код XUL и JavaScript.
Содержимое этого списка генерировалось на основе данных, начиная с
лекции 14 "Шаблоны", однако оно было "недостаточно
динамическим". Список заполнялся в момент создания страницы XUL и
с этого момента оставался неизменным. Теперь он должен обновляться
всякий раз при добавлении нового ключевого слова. Любое добавление или
удаление содержимого XUL может вызвать полную перерисовку документа,
включая список. Перерисовка выполняется автоматически, однако для
сложных тегов, подобных <menulist>, она может выполняться
неправильно. Необходимо использовать XUL аккуратно, иначе список после
перерисовки будет выглядеть некорректно.
Чтобы отслеживать ошибки при перерисовке, обратимся к коду для тега <menulist> и шаблона, который представлен в листинге 16.10. В
качестве источника данных в этом коде указан ".
<menulist id="notetaker-toolbar.keywords" editable="true">
<menupopup datasources="rdf:null" ref="urn:notetaker:keywords">
<template>
<menuitem uri="rdf:*"
label="rdf:http://www.mozilla.org/notetaker-rdf#label"/>
</template>
</menupopup>
</menulist>
В данном случае в состав шаблона входят только теги <menuitem>. Если источником данных является ", код XUL, полученный в результате обработки
шаблона, будет иметь следующий вид:
<menulist id="notetaker-toolbar.keywords" editable="true"> <menupopup datsources="rdf:null" ref="urn:notetaker:keywords"> </menupopup> </menulist>
Панель инструментов, построенная на основе такого кода, показана на рисунке 16.2.
(рис 16.2) Отображение списка с нулевым количеством элементовЭтот интерфейс имеет существенные недостатки, как в части
отображения, так и в части взаимодействия с пользователем. Мы могли бы
проигнорировать эти проблемы, понадеявшись на то, что при отображении
документа (событие onload ) к нему будет подключен созданный нами
источник данных, на основе которого будут созданы элементы <menuitem> для списка.
К сожалению, дела обстоят не так хорошо. Размер содержимого списка
определяется тегом <menupopup>, в состав которого входит фрейм.
С момента создания размер этого фрейма не изменяется динамически, хотя
в последующих версиях платформы ситуация может измениться. Это
означает, что после первоначального отображения раскрывающегося списка
его размер не будет изменяться, несмотря на изменение шаблона,
определяющего содержимое списка.
Усовершенствованный вариант кода, позволяющий обойти эту проблему, представлен в листинге 16.11:
<menulist id="notetaker-toolbar.keywords"
editable="true"
datasources="rdf:null"
ref="urn:notetaker:keywords"
>
<template>
<menupopup>
<menuitem uri="rdf:*"
label="rdf:http://www.mozilla.org/notetaker-rdf#label"/>
</menupopup>
</template>
</menulist>
В этом варианте тег <template> поднят на один уровень
иерархии, так что тег <menupopup> оказался вложенным в него. В
результате <menupopup> будет генерироваться заново при каждом
обновлении шаблона. При этом будет создаваться лишь одна пара тегов,
поскольку <menupopup> находится снаружи тега, в котором указан
источник данных (атрибут ). Вспомним, что при обработке шаблона
именно тег, имеющий атрибут , вместе со своим содержимым создается
многократно – по числу элементов, возвращаемых в результате запроса.
Поскольку теперь <menupopup> генерируется заново при каждом
обновлении содержимого списка, раскрывающийся список будет иметь
корректный размер. Это рекомендуемый подход для создания
раскрывающихся списков на основе шаблонов, если содержимое списка
может динамически изменяться после первоначального отображения.
Даже с учетом сделанных исправлений возможна еще одна проблема с отображением раскрывающегося списка, создаваемого на основе шаблона, хотя эта проблема не затрагивает нашего приложения.
На рисунке 16.3 показана тестовая панель до и после однократного щелчка по кнопке, вызывающей раскрытие списка, – сверху и снизу соответственно.
(рис 16.3) Проблема с перерисовкой раскрывающегося списка, основанного на шаблоне.В этом примере текстовое поле в верхней части раскрывающегося
списка первоначально имеет ширину по умолчанию для тега <textbox>. При щелчке по кнопке раскрывается список с
элементами, и текстовое поле перерисовывается заново с новой шириной,
которая определяется самым широким элементом списка. В результате
ширина элемента управления скачкообразно изменяется, что является
недостатком пользовательского интерфейса. Чтобы решить эту проблему,
достаточно в явном виде указать ширину (атрибут width ) для тега <menulist>. К счастью, эта проблема не затрагивает NoteTaker, по
крайней мере, при отображении реальных Web-страниц.
Необходимые изменения кода JavaScript очень просты. В функциях refresh_toolbar() и init_toolbar() следует подключить источник данных
к шаблону раскрывающегося списка. Эти изменения представлены в
листинге 16.12.
// слушатели onload работают в фазе перехвата
window.addEventListener("load", init_handler, true);
// загрузить содержимое RDF для панели инструментов. Используется объект заметки (note).
function init_toolbar(origin)
{
if ( origin != "timed" ) {
// избежать выполнения внутри любого обработчика onload
setTimeout("init_toolbar('timed')",1);
}
else
{
var menu = window.document.getElementById('notetakertoolbar.keywords');
menu.database.AddDataSource(noteSession.datasource);
menu.ref = 'urn:notetaker:keywords';
setInterval("content_poll()", 1000);
}
}
// обновить панель инструментов на основе текущей заметки.
function refresh_toolbar()
{
var box = document.getElementById('notetaker-toolbar.summary');
box.value = note.summary;
var menu = document.getElementById('notetaker-toolbar.keywords');
menu.ref = 'urn:notetaker:keywords';
}
Варианты этих функций, приведенные в практическом разделе лекции 14
"Шаблоны", вели себя беспокойно, вызывая при
малейшем изменении данных шаблона. В данном случае, однако, это
излишне, поскольку шаблоны основаны на источнике данных типа xml-,
который сам инициирует все необходимые действия при
изменении шаблона. Однако если при разработке собственного приложения
вы не знаете точно, стоит ли вызвать , всегда вызывайте эту
функцию.
Обновленная функция init_toolbar(), приведенная в листинге,
подключает источник данных к шаблону раскрывающегося списка, обновляет
свойство ref шаблона, а также обеспечивает периодическое выполнение
функции content_poll(), которая следит за изменениями URL содержимого
браузера. Даже если значение свойства ref в результате присваивания не
изменилось, выполняется запрос к источнику данных и список строится
заново на основе результатов запроса.
Вызов , как и раньше, представляет собой меру
предосторожности на случай непредвиденных проблем в обработчике
события onload. Функцию refresh_toolbar() можно сравнить с методом Refresh() интерфейса nsIRDFRemoteDataSource. Этот метод обновляет
хранилище фактов, лежащее в основе источника данных. Функция refresh_toolbar() обновляет только содержимое XUL, включая содержимое,
основанное на шаблоне.
На этом мы завершаем обсуждение изменений в коде панели инструментов, относящихся к отображению данных. Мы вернемся к панели инструментов, когда речь пойдет об обработке данных, вводимых пользователем.
Диалоговое окно Edit (окно редактирования заметки) – еще одна часть
приложения NoteTaker, использующая шаблоны. Источники данных для этих
шаблонов также должны динамически подключаться с использованием
скриптов JavaScript. Панель Edit (Правка) диалогового окна не содержит
никаких шаблонов. Панель (Ключевые слова) использует шаблоны
для заполнения двух элементов – <listbox> и <tree>.
Процедура подключения источников данных к этим шаблонам очень
похожа на использованную для панели инструментов. Мы заменяем на в двух местах документа editDialog.xul. Затем мы
создаем новую функцию init_dialog() в файле dialog_action.js и
модифицируем функцию refresh_dialog().
Результаты модификации скриптов приведены в листинге 16.13.
window.addEventListener("load", init_dialog, "true");
function init_dialog()
{
if ( origin != "timed" ) {
// избежать выполнения внутри любого обработчика onload
setTimeout("init_dialog('timed')",1);
}
else
{
var listbox = document.getElementById('notetaker.keywords');
listbox.database.AddDataSource(window.opener.noteSession.datasource);
var tree = document.getElementById('notetaker.related');
tree.database.AddDataSource(window.opener.noteSession.datasource);
refresh_dialog();
}
}
function refresh_dialog()
{
var listbox = document.getElementById('dialog.keywords');
listbox.ref = window.opener.note.url;
//listbox.ref = "http://saturn/test1.html"; // для тестирования
var tree = document.getElementById('dialog.related');
tree.ref = window.opener.note.url;
//tree.ref = "http://saturn/test1.html"; // для тестирования
}
Функция init_dialog(), добавляющая один источник данных к двум
шаблонам, практически идентична функции init_toolbar(). Функция refresh_dialog() также аналогична функции refresh_toolbar() и для
целей тестирования содержит некоторые URL, находящиеся в файле
notetaker.
В практическом разделе лекции 13 "Списки и деревья" мы
экспериментировали с динамическим заполнением списков при помощи
интерфейсов
В результате всех этих изменений все шаблоны NoteTaker могут
работать с файлом notetaker.
Механизм шаблонов XUL – лишь один из способов формировать запросы к
набору фактов
В приложении NoteTaker используется один запрос, который было бы разумно реализовать на основе скрипта. Это запрос, выполняющий поиск заметки, существующей для данного URL. Шаблоны не подходят для решения этой задачи по следующим причинам:
Поиск выполняется при помощи метода объекта заметки в
файле notes.js. Мы уже создали каркас этого метода в предыдущих
лекциях, а теперь добавим его полную реализацию. Метод загружает данные
resolve : function (url) {
var ds = window.noteSession.datasource;
var ns = "http://www.mozilla.org/notetaker-rdf#";
var rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
rdf = rdf.getService(Ci.nsIRDFService);
var container = Cc["@mozilla.org/rdf/container;1"];
container = container.getService(Ci.nsIRDFContainer);
var cu = Cc["@mozilla.org/rdf/container-utils;1"];
cu = cu.getService(Ci.nsIRDFContainerUtils);
var seq_node = rdf.GetResource("urn:notetaker:notes");
var url_node = rdf.GetResource(url);
var chopped_node = rdf.GetResource(url.replace(/\?.*/,""));
var matching_node, prop_node, value_node;
if (!cu.IsContainer(ds,seq_node)) {
throw "Missing <Seq> 'urn:notetaker:notes' in " + noteSession.config_file;
return;
}
container.Init(ds,seq_node);
// Сначала попробовать полный URL, потом усеченный URL, если не найдены – заметки нет
if ( container.IndexOf(url_node) != -1) {
matching_node = url_node;
this.url = url;
this.chop_query = false;
}
else if ( container.IndexOf(chopped_node) != -1 ) {
matching_node = chopped_node;
this.url = url.replace(/\?.*/,"");
}
else {
this.url = null;
return;
}
else
return;
// Если заметка найдена, получить все ее свойства.
var props = ["summary", "details", "width", "height", "top", "left"];
for (var i=0; i<props.length; i++)
{
pred_node = rdf.GetResource(ns + props[i]);
value_node = ds.GetTarget(matching_node, pred_node, true);
value_node = value_node.QueryInterface(Ci.nsIRDFLiteral);
this[props[i]] = value_node.Value;
}
}
Прежде всего, метод создает три служебных объекта XPCOM для работы
с nsIRDFService используется для преобразования простой
строки URL в объект nsIRDFResource, являющийся nsIRDFNode. Большинство методов для работы с nsIRDFNode. Мы
создаем такие объекты как для полных, так и для усеченных URL.
Единственное применение объекта nsIContainerUtils в нашем методе –
убедиться, что в файле notetaker., который является контейнером. Если это условие не
выполнено, работа метода прерывается и генерируется исключение. Затем
мы используем интерфейс nsIRDFContainer для того, чтобы связать <Seq> ) с источником данных и инициализировать эту
связь. Как правило, доступ к источнику данных осуществляется путем
обращения к отдельным фактам. Интерфейс nsIRDFContainer позволяет
работать с контейнером и его элементами как со структурой данных.
После инициализации служебных объектов мы переходим к выполнению собственно запроса, который в данном случае несложен. Его алгоритм таков: найти в источнике данных ресурс, соответствующий полному URL; если таковой отсутствует, найти ресурс, соответствующий URL без параметров; если и этот поиск завершился неудачей, отказаться от дальнейшего выполнения метода.
В оставшейся части метода мы извлекаем все факты, описывающие пары
свойство/значение для данной заметки. Метод всегда
возвращает объект nsIRDFNode, который необходимо преобразовать к тому
типу, который мы используем для свойств заметки. Во всех случаях это
простая строка (мы не храним размеры окна как целые). Наконец,
полученные значения свойств присваиваются свойствам объекта заметки.
Эта часть кода предполагает, что заметка описана в файле notetaker.
Запрос, выполняемый в этом методе, основан на двух фактах и в целом аналогичен тем простым запросам, которые мы применяли в шаблонах, за исключением некоторых проверок в начале метода и использования двух различных URL для поиска.
Если попытаться протестировать метод , например, добавив
код вида:
note.resolve("http://saturn/test1.html");
Система, скорее всего, выдаст неожиданные сообщения об ошибках. Как
правило, ошибки такого рода возникают при первом обращении к
интерфейсам для работы с noteSession. Источник данных
инициализируется в функции init() в составе этого объекта следующим
образом:
this.datasource = GetDataSource(url.spec);
Этот способ инициализации не подходит для наших задач. Он
осуществляет асинхронную загрузку данных, поэтому хранилище данных
заполняется постепенно. Тем временем скрипт переходит к выполнению
дальнейших инструкций и, возможно, пытается запросить данные до того,
как они загружены. Поэтому неудивительно, что методы
this.datasource = GetDataSourceBlocking(url.spec);
Это может привести к незначительной задержке при загрузке браузера, однако допустимо для нашего простого приложения.
Впрочем, мы могли бы избежать этой задержки, не отказываясь от
асинхронной загрузки, но используя дополнительные интерфейсы nsIRequestObserver или nsIStreamListener компонента xml-. С
помощью наблюдателя или слушателя мы могли бы определить момент
завершения загрузки. Некоторые из объектов XPCOM, созданных для этой
цели, могли бы найти применение и в других методах. Сделав эти объекты
доступными через объект noteSession, мы могли бы обеспечить
возможность их повторного использования. Дальнейшие подробности
реализации этой стратегии выходят за рамки данной книги.
В предыдущих лекциях мы написали код, который помещает данные
объекта заметки в поля формы или HTML-документ, отображаемый в окне
браузера. В этой лекции мы подключили объект заметки к хранилищу фактов
content_poll()
находящуюся в файле toolbar_action.js. Необходимо заменить эту
строку:
display_note()
на следующую:
if (note.url != null ) display_note()
Это позволит не отображать никаких дополнительных элементов для страниц, для которых не созданы заметки. Так гораздо лучше!
NoteTaker позволяет не только просматривать уже сохраненные данные
заметок, но и изменять или добавлять их. Последний раз мы работали с
вводом данных в лекции 7 "Формы и меню", где введенные данные
пересылались на Web-сервер. В этой лекции мы будем сохранять данные в
хранилище файлов
Для наших целей сохранить данные означает добавить их к источнику
данных. После этого они будут находиться в оперативной памяти до тех
пор, пока пользователь не выполнит какие-либо действия, приводящие к
синхронизации хранилища фактов с файлом, или до завершения работы
платформы. Какие именно действия пользователя должны приводить к
Пользователь может вводить и изменять данные как в панели инструментов NoteTaker, так и в диалоговом окне. Мы последовательно рассмотрим эти варианты, начав с панели инструментов.
Пользователь может легко создать или изменить заметку, используя
поля аннотации (summary) и ключевых слов (Edit, Save или Delete ), происходить
ничего не должно. Поэтому данные, измененные пользователем, могут
обрабатываться при помощи команд, доступных из панели инструментов.
Прибегать к обработчикам событий или другим подобным
механизмам не требуется.
Панель Edit диалогового окна в этом отношении аналогична панели
инструментов. Изменения, сделанные пользователем, должны сохраняться
лишь в том случае, если пользователь нажимает кнопку OK. Если
пользователь закрывает окно, нажав кнопку Cancel, изменения
сохраняться не должны. Однако ситуация с панелью
Панель позволяет пользователю добавить или удалить любое
количество ключевых слов, используя соответствующие кнопки. Проблема
состоит в следующем: где должны храниться эти изменения, пока
диалоговое окно не закрыто? Если пользователь, в конечном счете,
нажимает Cancel, добавленные слова не должны быть сохранены. Если
пользователь принимает сделанные изменения, они должны быть
сохранены.
Однако мы хотим, чтобы в процессе работы пользователя с окном
вносимые изменения отображались в соответствующих элементах <listbox> и <tree>. Это означает, что ключевые слова
должны добавляться к хранилищу
Таким образом, мы столкнулись с проблемой отмены сделанных действий, иными словами – отката транзакций. Мы хотим немедленно добавлять ключевые слова к источнику данных, чтобы они были доступны всем элементам, использующим этот источник, однако нам необходимо иметь возможность удалить их, если пользователь, в конце концов, не принимает сделанных изменений. Для решения этой проблемы мы реализуем новый контроллер команды. Этот контроллер будет записывать выполняемые операции с ключевыми словами в буфер отмены. Если будет получена команда на отмену транзакции, контроллер, используя сохраненную информацию, сможет вернуть источник данных к исходному состоянию. Платформа Mozilla не предоставляет готового объекта с такой функциональностью, но в большинстве случаев его реализация не должна вызывать затруднений.
Таким образом, вся обработка пользовательских данных основана на
инфраструктуре команд. Это простое и изящное архитектурное решение.
Данные пребывают в полях форм, не вызывая какой-либо активности
программы, до тех пор, пока пользователь не запускает одну из команд.
Эта команда может поместить данные в хранилище фактов, после чего они
станут доступны всему приложению, в частности, любым шаблонам. Если
команда также ответственна за
Теперь мы обратимся к конкретному коду, который обеспечивает
сохранение на диске введенных пользователем данных. Мы усовершенствуем
функции action(), вызываемые из панели инструментов и диалогового
окна, а также реализуем дополнительный контроллер команды для работы с
ключевыми словами в диалоговом окне.
Функция action() панели инструментов поддерживает команды notetaker-open-, notetaker-save, notetaker-display и notetaker-delete. Только команды -save и -delete связаны с изменением
содержимого notetaker-save.
function action(task)
{
var ns = "http://www.mozilla.org/notetaker-rdf#";
var rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
rdf = rdf.getService(Ci.nsIRDFService);
var container = Cc["@mozilla.org/rdf/container;1"];
container = container.getService(Ci.nsIRDFContainer);
var url_node;
// ... реализация других команд удалена ...
if ( task == "notetaker-save" )
{
var summary = document.getElementById("notetaker-toolbar.summary");
var keyword = document.getElementById("notetaker-toolbar.keywords");
var update_type = null;
if ( note.url != null )
{
if ( keyword.value != "" || summary.value != note.summary )
{
update_type = "partial"; // существующая заметка: обновить аннотацию, ключевые слова
url_node = rdf.GetResource(note.url);
}
}
else if ( window.content window.content.document window.content.document.visited )
{
update_type = "complete"; // a new note
url_node = window.content.document.location.href;
url_node = url_node.replace(/\?.*/,""); // toolbar chops any query
url_node = rdf.GetResource(url_node);
}
if ( update_type == "complete" )
{
// добавить url заметки к контейнеру
var note_cont = rdf.GetResource("urn:notetaker:notes");
container.Init(noteSession.datasource,note_cont);
container.AppendElement(url_node);
// добавить поля заметки, за исключением ключевых слов
var names = ["details", "top", "left", "width", "height"];
var prop_node, value_node;
for (var i=0; i < names.length; i++)
{
prop_node = rdf.GetResource(ns + names[i]);
value_node = rdf.GetLiteral(note[names[i]]);
noteSession.datasource.Assert(url_node, prop_node, value_node, true);
}
}
if ( update_type != null)
{
// обновить/добавить аннотацию
var summary_pred = rdf.GetResource(ns + "summary");
var summary_node = rdf.GetLiteral(summary.value);
noteSession.datasource.Assert(url_node, summary_pred, summary_node, true);
// начать работу с новым ключевым словом
var keyword_node = rdf.GetResource("urn:notetaker:keyword:" + keyword.value);
var keyword_value = rdf.GetLiteral(keyword.value);
// сделать ключевое слово связанным с одним из ключевых слов для данной заметки
var keyword_pred = rdf.GetResource(ns + "keyword");
var related_pred = rdf.GetResource(ns + "related");
var keyword2 = noteSession.datasource.GetTarget(url_node, keyword_pred, true);
if (keyword2)
noteSession.datasource.Assert(keyword_node, related_pred, keyword2, true);
// добавить ключевое слово к данной заметке
noteSession.datasource.Assert(url_node, keyword_pred, keyword_node, true);
// добавить текст ключевого слова
var label_pred = rdf.GetResource(ns + "label");
noteSession.datasource.Assert(keyword_node, label_pred, keyword_value, true);
// добавить ключевое слово к контейнеру, содержащему все ключевые слова
var keyword_cont = rdf.GetResource("urn:notetaker:keywords");
container.Init(noteSession.datasource,keyword_cont);
container.AppendElement(keyword_node);
}
// записать на диск
noteSession.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource)
.Flush();
note.resolve();
display_note();
}
Этот код содержит эквивалент одной транзакции применительно к
данным note и noteSession, а также
состояния текущего URL принимается решение о том, существует ли уже
заметка для данного URL. Чтобы облегчить себе задачу, мы используем
некоторые данные, полученные функцией content_poll(), например
значение свойства visited.
Если заметка уже существует, сохранение подразумевает частичное
обновление уже существующих фактов. Если же заметки для данного URL
еще нет, необходимо создать ее, что подразумевает создание всех
необходимых фактов. Если новая заметка создается при помощи панели
инструментов, мы удаляем все параметры запроса GET из URL. Значение,
указывающее на характер необходимого обновления, присваивается
переменной update_type.
Поскольку частичное обновление представляет собой подмножество полного обновления (создания новой заметки), существует часть кода, которая выполняется при любом обновлении. Ветвь кода, следующая за оператором:
if ( update_type == "complete" )
содержит действия, необходимые для полного обновления, за исключением общей части. Общая часть содержится в следующей ветви, которая выполняется при любом типе обновления:
if (update_type != null )
Рассмотрим каждую из ветвей поочередно.
При создании новой заметки сначала мы получаем доступ к контейнеру , а затем добавляем к нему URL, соответствующий
заметке. Затем создаются факты для каждого из свойств заметки, кроме
аннотации и ключевых слов. Любые строки перед передачей интерфейсам
nsIRDFNote или его
Затем вызывается код для частичного обновления. Он выполняется
всегда за исключением случаев, когда новую заметку создать нельзя.
Заметку невозможно создать, например, для URL about:, который не выполняет
никаких действий, если такой факт уже существует. Поскольку в
нормальных условиях в хранилище не должно существовать несколько копий
одного и того же факта, для добавлении новых фактов рекомендуется
использовать этот метод.
Затем код выполняет более сложную работу по добавлению ключевого
слова. Если заметке уже были присвоены ключевые слова, мы хотим, чтобы
новое слово было связано с остальными. Для этого мы должны добавить
факт, связывающий новое слово с одним из ранее присвоенных слов. Для
этого мы пытаемся найти ключевое слово, уже присвоенное данной
заметке, и в случае успеха добавляем факт, связывающий его с новым
словом. Мы делаем это перед тем, как связать новое ключевое слово с
заметкой, чтобы избежать ситуации, в которой новое слово окажется
связанным с самим собой. Логика оставшегося кода прямолинейна – мы
добавляем ключевое слово к заметке, к контейнеру ключевых слов и, наконец, сохраняем само слово (его
значение).
Таким образом, в этой ветви кода мы добавили пять фактов – один для
аннотации и четыре для ключевых слов. Поскольку источник данных
основан на полноценном xml-, эти изменения будут
автоматически переданы всем шаблонам, использующим данный источник.
Затем мы вызываем метод , чтобы записать состояние источника
данных на локальный диск. Имейте в виду, что этот метод полностью
переписывает файл notetaker.note и отображаем данные заметки, чтобы привести структуры
данных JavaScript и пользовательский интерфейс в соответствие с
данными notetaker-save.
Команда notetaker-delete реализуется аналогичным образом.
Наибольшую сложность при этом представляет определение того, какие из
ключевых слов, связанных с заметкой, могут быть удалены, а какие
нужны для других заметок. Это требует анализа многих возможных
вариантов, и мы не будем рассматривать их здесь. С кнопкой Delete
(Удалить) на панели связана аналогичная логика; мы обсудим ее
ниже.
Функция action() диалогового окна Edit поддерживает следующие
команды: notetaker-, notetaker-, notetaker-save, notetaker-load и notetaker-close-. Из них лишь команда notetaker-save требует работы с данными notetaker-save
панели инструментов.
if (task == "notetaker-save")
{
var field, widget, note = window.opener.note;
for (field in note)
{
widget = document.getElementById("dialog." + field.replace(/_/,"-"));
if (!widget) continue;
if (widget.tagName == "checkbox")
note[field] = widget.checked;
else
note[field] = widget.value;
}
window.opener.setTimeout('execute("notetaker-save")',1);
}
Последняя инструкция этого фрагмента представляет собой вызов
команды notetaker-save панели инструментов. Мы не можем вызвать метод window.opener.execute() непосредственно, поскольку в этом случае
функция будет выполняться в контексте диалогового окна, а не окна
браузера. Обращаясь к методу окна браузера, мы
обеспечиваем необходимый контекст выполнения функции.
Наконец, нам нужны дополнительные команды для работы с ключевыми
словами в панели диалогового окна Edit. Эти команды будут
объединены в контроллер, поддерживающий принятие или отмену изменений
(фиксацию и откат транзакций). Контроллер будет поддерживать следующие
команды: notetakerkeyword-add, notetaker-, notetaker- и notetaker-. Поскольку эти команды
тесно связаны друг с другом и используют общие данные, их
нецелесообразно реализовывать по отдельности в составе функции action(). Вместо этого мы реализуем их непосредственно в составе
контроллера, создав специально для этого новый файл
keywordController.js. Общая структура контроллера показана в листинге
16.17.
var keywordController = {
_cmds : { },
_undo_stack : [],
_rdf : null,
_ds : null,
_ns : "http://www.mozilla.org/notetaker-rdf#",
_related : null,
_label : null,
_keyword : null,
init : function (ds) { ... initialize ... },
_LoggedAssert : function (sub, pred, obj) { ... },
_LoggedUnassert : function (sub, pred, obj) { ... },
supportsCommand : function (cmd)
{ return (cmd in this._cmds); },
isCommandEnabled : function (cmd) { return true; },
onEvent : function (cmd) { return true; },
doCommand : function (cmd) {
... подготовительные операции ...
switch (cmd) {
case "notetaker-keyword-add":
case "notetaker-keyword-delete":
case "notetaker-keyword-commit":
case "notetaker-keyword-undo-all":
}
}
};
keywordController.init(window.opener.noteSession.datasource);
Как и любой контроллер команд, этот контроллер поддерживает четыре
стандартных метода, начиная с supportsCommand(). Метод doCommand()
выполняет различные действия в зависимости от переданного имени
команды; код в нем организован при помощи оператора case. Контроллер
также поддерживает ряд других действий. В массиве _undo_stack
сохраняются действия, которые могут быть отменены. Реализованные нами
методы _LoggedAssert() и _LoggedUnassert() аналогичны стандартным
методам для работы с и Unassert(), однако кроме этого они
выполняют _undo_stack. Давайте начнем анализ контроллера с метода init(), который показан в листинге 16.18:
init : function (ds) {
this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"];
this._rdf = this._rdf.getService(Ci.nsIRDFService);
this._ds = ds;
this._related = this._rdf.GetResource(this._ns + "related");
this._label = this._rdf.GetResource(this._ns + "label");
this._keyword = this._rdf.GetResource(this._ns + "keyword");
window.controllers.insertControllerAt(0,this);
},
Этот метод создает доступные для контроллера ссылки на ряд полезных
объектов – службу
Реализация двух следующих функций – _LoggedAssert() и _LoggedUnassert() – иллюстрирует, каким образом контроллер может
сохранять информацию о выполняемых командах. В данном случае речь идет
об истории добавления и отмены фактов
Реализация двух этих функций показана в листинге 16.19.
_LoggedAssert : function (sub, pred, obj)
{
if ( !this._ds.HasAssertion(sub, pred, obj, true))
{
this._undo_stack.push( { assert:true, sterm:sub,
pterm:pred, oterm:obj } );
this._ds.Assert(sub, pred, obj, true);
}
},
_LoggedUnassert : function (sub, pred, obj)
{
if ( this._ds.HasAssertion(sub, pred, obj, true))
{
this._undo_stack.push( { assert:false, sterm:sub,
pterm:pred, oterm:obj } );
this._ds.Unassert(sub, pred, obj, true);
}
},
Эти функции представляют собой замену стандартных методов nsIRDFDataSource. и nsIRDFDataSource.Unassert(). В обоих
случаях сначала выполняется проверка того, изменит ли предполагаемое
действие состояние хранилища фактов. Если это так, создается запись о
действии в форме объекта с четырьмя свойствами, которая добавляется в
журнал (стек). Свойство указывает на характер выполняемого
действия. После этого вызывается стандартный метод для изменения
фактов
notetaker- и notetaker-. Реализация этих команд в составе метода doCommand()
представлена в листинге 16.20.
case "notetaker-keyword-commit":
this._undo_stack = [];
break;
case "notetaker-keyword-undo-all":
while (this._undo_stack.length > 0 )
{
var cmd = this._undo_stack.pop();
if ( cmd.assert )
this._ds.Unassert(cmd.sterm, cmd.pterm, cmd.oterm, true);
else
this._ds.Assert(cmd.sterm, cmd.pterm, cmd.oterm, true);
}
break;
Реализация команды notetaker- тривиальна – она просто
удаляет из журнала все записи, чтобы выполненные действия нельзя было
отменить в дальнейшем. Команда notetaker- чуть более
сложна. Она перебирает элементы стека, выполняя Unassert() для каждого 'а, записанного в журнале, и наоборот. В конце концов
стек оказывается пустым, так что "отмена отмены" в данной
реализации невозможна.
Хотя в данном случае в журнал записываются добавления и удаления
отдельных фактов, столь же просто организовать
Оставшаяся часть метода doCommand() приведена
в листинге 16.21.
doCommand : function (cmd) {
var url = window.opener.content.document.location.href;
var keyword = window.document.getElementById("dialog.keyword").value;
if (keyword.match(/^[ \t]*$/))
return;
var keyword_node = this._rdf.GetResource("urn:notetaker:keyword:" + keyword);
var keyword_value = this._rdf.GetLiteral(keyword);
var url_node = this._rdf.GetResource(url);
var test_node, keyword2, enum1, enum2;
switch (cmd) {
case "notetaker-keyword-add":
// Связать ключевое слово с другим, если таковое существует
keyword2 = this._ds.GetTarget(url_node, this._keyword, true);
if (keyword2)
this._LoggedAssert(keyword_node, this._related, keyword2);
// добавить данное ключевое слово
this._LoggedAssert(keyword_node, this._label, keyword_value);
// добавить ключевое слово к текущей заметке
this._LoggedAssert(url_node, this._keyword, keyword_node);
break;
case "notetaker-keyword-delete":
// удалить связь ключевого слова с текущей заметкой.
this._LoggedUnassert(url_node, this._keyword, keyword_node);
// если ключевое слово не используется в других местах, удалить его и связанные с ним факты
enum1 = this._ds.GetSources( this._keyword, keyword_node, true);
if (!enum1.hasMoreElements())
{
// данное ключевое слово
this._LoggedUnassert(keyword_node, this._label, keyword_value);
// ключевое слово, с которым связано данное слово
enum2 = this._ds.GetTargets(keyword_node, this._related, true);
while (enum2.hasMoreElements())
this._LoggedUnassert(keyword_node, this._related,
enum2.getNext().QueryInterface(Ci.nsIRDFNode));
// ключевое слово, которое связано с данным словом
enum2 = this._ds.GetSources(this._related, keyword_node, true);
while (enum2.hasMoreElements())
this._LoggedUnassert(enum2.getNext().QueryInterface(Ci.nsIRDFNode), this._related, keyword_node);
}
else // ключевое слово используется в других местах
{
// удалить факты, если слова, с которыми связано данное ключевое слово, относятся только к данной заметке
enum1 = this._ds.GetTargets(keyword_node, this._related, true);
while (enum1.hasMoreElements())
{
keyword2 = enum1.getNext().QueryInterface(Ci.nsIRDFNode);
enum2 = this._ds.GetSources(this._keyword, keyword2, true);
test_node = enum2.getNext().QueryInterface(Ci.nsIRDFNode);
if (!enum2.hasMoreElements() test_node.EqualsNode(url_node))
this._LoggedUnassert(keyword_node, this._related, keyword2);
// удалить факты, если слова, которые связаны с данным ключевым словом, относятся только к данной заметке
enum1 = this._ds.GetSources(this._related, keyword_node, true);
while (enum1.hasMoreElements())
{
keyword2 = enum1.getNext().QueryInterface(Ci.nsIRDFNode);
enum2 = this._ds.GetSources(this._keyword, keyword2, true);
test_node = enum2.getNext().QueryInterface(Ci.nsIRDFNode);
if (!enum2.hasMoreElements() test_node.EqualsNode(url_node))
this._LoggedUnassert(keyword2, this._related, keyword_node);
}
}
}
break;
Около десятка строк, находящихся до оператора switch(), выполняют
инициализацию локальных переменных и прекращают выполнение команды в
том случае, если текущая заметка отсутствует. В листинге показаны
только ветви оператора switch(), соответствующие командам notetaker- и notetaker-.
Добавление и удаление ключевых слов было бы проще, если бы мы не
пытались поддерживать логику связей между ключевыми словами.
Значительная часть кода, особенно для удаления ключевых слов, связана
с решением этой задачи.
Обе команды предполагают, что заметка для данного URL уже существует или создается в данный момент. Поэтому ключевые слова добавляются и удаляются в контексте определенного URL. Основные действия отмечены в коде при помощи комментариев, однако ниже приводятся более подробные пояснения.
Весь код написан с учетом того ограничения, что в хранилище фактов не могут находиться две копии одного и того же факта. Каждый факт в хранилище уникален. Поэтому мы должны работать с хранилищем глобально, учитывая последствия добавления или удаления отдельных фактов для хранилища в целом.
Добавление факта – более простой случай, чем удаление. Необходимо добавить к хранилищу следующие факты:
<- keyword-urn, related, keyword2-urn -> (не обязательно) <- note-url, keyword, keyword-urn -> <- keyword-urn, label, keyword-literal ->
Мы должны сделать так, чтобы все ключевые слова, присвоенные одной
заметке, были связаны между собой. В нашей модели _LoggedAssert().
Процедура удаления ключевых слов значительно более сложна. Нетрудно удалить информацию, связанную с конкретной заметкой, но ключевое слово может быть присвоено нескольким заметкам. Это означает, что оно может быть включено в связи между ключевыми словами, также относящиеся к другим заметкам. Поэтому мы не можем просто удалить факты первого и третьего типа, не изучив вопрос, где еще используется данное ключевое слово. Наши дальнейшие действия будут зависеть от того, присвоено ли оно другим заметкам. Это требует довольно громоздкой логики, которая описана ниже.
Если ключевое слово используется для единственной заметки, то вся информация об этом слове ограничена данной заметкой. Поэтому мы можем просто удалить все три факта.
Если же слово используется для других заметок, следует действовать более аккуратно. Мы можем удалить факт о связи ключевого слова с данной заметкой, но мы не можем удалить само ключевое слово (факт о его значении). Самая сложная часть логики – удаление фактов, описывающих связь данного слова с другими ключевыми словами. Если факт о связи данного ключевого слова с другим сформирован на основе другой заметки, мы не должны удалять его. Поэтому, если другое слово присвоено какой-либо другой заметке наряду с данным, факт относится и к этой заметке и потому должен быть сохранен. В противном случае следует удалить этот факт. Мы выполняем проверку дважды, поскольку удаляемое ключевое слово может быть как субъектом, так и объектом факта о связи ключевых слов.
Внимательный читатель может заметить, что контейнер также должен быть обновлен командами -add и -delete. Мы не сделали этого,
поскольку реализация команд и без того достаточно сложна, а для того,
чтобы увязать дальнейшие обновления с нашей системой отмены, требуются
определенные ухищрения. Более общим решением было бы связать объекты-
наблюдатели источника данных со стеком-журналом, однако здесь мы не
будем рассматривать это решение. На этом мы завершаем обсуждение
команд NoteTaker для работы с данными
Для того чтобы эта система заработала, необходимо связать
контроллер и команды с диалоговым окном. Для этого требуется несколько
фрагментов кода. Мы должны добавить к файлу editDialog.xul
дополнительный тег <script>:
Кроме того, мы должны добавить несколько обработчиков событий к
тегу < в том же файле. Эти обработчики нужны для работы с
ключевыми словами из диалогового окна.
<dialog xmlns="http://www.mozilla.org/keymaster/
gatekeeper/there.is.only.xul"
id="notetaker.dialog"
title="Edit NoteTaker Note"
onload="execute('notetaker-load');"
ondialogaccept="execute('notetaker-keyword-commit');
execute('notetaker-save');
execute('notetaker-close-dialog');"
ondialogcancel="execute('notetaker-keyword-undo-all');
execute('notetaker-close-dialog');"
>
Обработчики разрастаются, и если по мере развития приложения придется добавлять новые, целесообразно объединить группы команд в функции. Некоторые другие обработчики для диалогового окна определены в файле dialog_handlers.js. Теперь, когда мы создали контроллер для работы с ключевыми словами, два из этих обработчиков превращаются в тривиальные вызовы команд:
function add_click(ev)
{
execute("notetaker-keyword-add");
}
function delete_click(ev)
{
execute("notetaker-keyword-delete");
}
Теперь мы можем считать, что приложение NoteTaker завершено – по крайней мере, в той степени, в какой это позволяет сделать объем книги.
В практическом разделе лекции 13
"Списки и деревья" мы экспериментировали с
"кадрами". При желании этот эксперимент может быть
распространен на данные <tree>,
содержащий кадр, может получать содержимое из источника данных без
помощи шаблона. Поскольку объем книги не позволяет подробно обсудить
этот вопрос, сделаем несколько кратких замечаний.
В лекции 13 мы реализовали метод calcRelatedMatrix(), который получал данные из массива treedata. Мы можем изменить этот метод так, чтобы он
получал связанные пары ключевых слов не из массива JavaScript, а от
источника данных. В этом случае наш код заработает немедленно, но уже
на основе данных
Однако такая стратегия – довольно примитивное использование
возможностей источников данных. Более разумное решение – использовать
интерфейс nsIRDFObserver. Если объект JavaScript, реализующий кадр,
поддерживает этот интерфейс, он может быть зарегистрирован в качестве
наблюдателя в источнике данных (источник, в свою очередь, должен
поддерживать интерфейс nsIRDFCompositeDataSource ). В результате кадр
будет получать уведомление всякий раз, когда факт в источнике
изменяется, и сможет отразить это изменение в элементе <tree>,
не перестраивая дерево полностью. Эта более сложная стратегия
совместима с системами
В заключение отметим, что объект с интерфейсом nsIRDFDataSource
может быть полностью реализован на JavaScript. Такой объект может
имитировать источник данных
Вот некоторые из типичных проблем, возникающих при работе с источниками данных:
Регистр символов. В отличие от остальных методов XPCOM, имена
методов интерфейсов источников данных начинаются с прописной буквы – InitCaps, а не initCaps. Поэтому, например, метод называется GetResource(), а не getResource().
Асинхронная загрузка. Если для создания источника данных
используется метод GetDataSource() интерфейса nsIRDFDataSource, а не GetDataSourceBlocking(), данные загружаются в источник параллельно с
выполнением других инструкций. В результате попытка обратиться к
данным немедленно после создания источника может привести к ошибке.
Данные, заведомо присутствующие в источнике с точки зрения
разработчика, могут оказаться еще не загружены.
Синтаксические ошибки в тестовых данных. Тестовые данные из файла
Попытка сохранить данные по сети. Если вы вносите изменения в
источники, получающие данные из сети (с Web-ресурса или FTP-сайта),
такие изменения не могут быть непосредственно "сохранены".
Этот механизм применим только к локальным файлам. Чтобы передать
сделанные изменения на удаленный сервер, необходимо создать на основе
источника данных документ
Использование false в качестве аргумента методов и Unassert(). Четвертым аргументом этих методов всегда должно быть true.
Использование false расширяет логику методов true.
Передача строк методам и Unassert(). Эти методы принимают
только объекты типа nsIRDFNode и его
Проблемы с множественными возвращаемыми значениями. Такие методы,
как GetTargets() возвращают объект-перечислитель nsISimpleEnumerator,
содержащий список nsISupports.
Используйте метод QueryInterface() для того, чтобы получить более
полезный интерфейс nsIRDFNode или один из его
Объекты, реализующие интерфейс nsIRDFContainerUtils, являются
служебными. Они используются для выполнения различных действий с
другими объектами, передаваемыми им в качестве аргументов.
Внутренние источники данных
function _dumpFactSubtree(ds, sub, level)
{
var iter, iter2, pred, obj, objstr, result="";
// выйти, если передан nsIRDFLiteral или другой не-URI
try { iter = ds.ArcLabelsOut(sub); }
catch (ex) { return; }
while (iter.hasMoreElements())
{
pred = iter.getNext().QueryInterface(Ci.nsIRDFResource);
iter2 = ds.GetTargets(sub, pred, true);
while (iter2.hasMoreElements())
{
obj = iter2.getNext();
try {
obj = obj.QueryInterface(Ci.nsIRDFResource);
objstr = obj.Value;
}
catch (ex)
{
obj = obj.QueryInterface(Ci.nsIRDFLiteral);
objstr = '"' + obj.Value + '"';
}
result += level + " " + sub.Value + " , " +
pred.Value + " , " + objstr + "\n";
result += dumpFactSubtree(ds, obj, level+1);
}
}
return result;
}
function dumpFromRoot(ds, rootURI)
{
return _dumpFactSubtree(ds, rootURI, 0);
}
Для вывода всех данных источника следует вызвать функцию dumpFromRoot(). Этот код использует ограниченное подмножество
функциональности интерфейса nsIRDFDataSource и должен работать с
большинством внутренних источников данных, а также с простыми файлами
В качестве аргументов функции должны быть переданы объекты nsIRDFDataSource и nsIRDFResource. Источник данных, представленный
первым объектом, должен быть полностью загружен – в противном случае
может быть выведена неполная информация о его содержимом. Аргумент rootURI должен быть контейнером или владельцем контейнера на графе
Инфраструктура платформы Mozilla содержит множество полезных
объектов, не все из которых были освещены в этой лекции. Большинство из
них являются объектами довольно высокого уровня в силу требований
переносимости и ориентации на разработку приложений. Можно представить
себе, что в дальнейшем в составе платформы будет реализован аналог
интерфейса
Mozilla предоставляет богатые возможности для работы с XML, что неудивительно. Первоначально интенсивная обработка XML была характерна для приложений класса business-to-business, однако инициатива .NET компании Microsoft подразумевает активное использование XML на стороне клиента.
К настоящему моменту мы рассмотрели как интерфейс приложений Mozilla, так и их инфраструктуру, и нам осталось лишь познакомиться с развертыванием этих приложений. Наряду с системой сборки Mozilla, которая используется для создания компилированных приложений, существует и система удаленной установки приложений Mozilla. Эта система, XPInstall, и является предметом заключительной лекции курса.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.