Введение в Mozilla для программиста |
- Statistics
- Participants
- Translate into Russian
- Translation result
- 50% translated in draft.
Что такое Mozilla?
Mozilla — это организация и проект по разработке кросс-платформенного Интернет-клиента с открытыми исходными текстами. Это значит, что любой желающий может внести изменения в исходные тексты программы, соблюдая условия лицензий, под которыми выпущена та или иная часть ПО (смесь из MPL, NPL, GPL, LGPL).
Организация, которая предоставляет инфраструктуру разработчикам проекта, называется mozilla.org. Кроме того, mozilla.org — адрес главного сайта проекта.
Цель руководства
Mozilla — один из самых больших проектов с открытым исходным кодом. В базе исходных текстов Mozilla хранятся миллионы строк кода. Поэтому подступиться к работе над таким огромным проектом непросто. Цель руководства — дать разработчику Mozilla комплекс базовых знаний о разных технологиях, используемых в проекте.
Мне бы такое руководство, когда я только начинал изучать Mozilla. :)
Целевая аудитория
Руководство предназначено для разработчиков, которые хотят присоединиться к проекту Mozilla. Вы должны хорошо разбираться в объектно-ориентированном программировании, и особенно хорошо знать языки программирования C и C++, потому что в проекте использованы преимущественно именно они.
Даже если вы хотите работать только с подмножеством кода, скажем, только с кодом пользовательского интерфейса на JavaScript и XUL, всё равно в этом руководстве вы почерпнёте много полезной информации.
Аннотация
В руководстве даны ответы на следующие вопросы:
— Как структурирован исходный код?
— Какие используются технологии?
— С чего лучше начать?
— Как взаимодействуют компоненты?
— Какие есть инструменты, и как они могут помочь?
— Какие правила участия в проекте нужно знать?
Какое отношение к этому имеет Netscape?
По-моему, в основе проекта Mozilla лежит Netscape. Просто в определённый момент компания Netscape решила выпустить на условиях свободной лицензии части исходного текста, на которые у неё были исключительные авторские права.
Это нельзя было назвать полноценным продуктом, поэтому проекту Mozilla пришлось самостоятельно написать множество кода. Кроме того, многие куски кода были переписаны заново. Остальные, же были оставлены и дописаны. Это одна из причин, по которой термин «Netscape» можно встретить в этом документе а также в различных обсуждениях проекта Mozilla.
С++ и JavaScript
Так как они широко используются в Mozilla, не лишним будет описать как JavaScript и C++ уживаются в исходном коде Mozilla. C++ — это компилируемый язык, в то время как JavaScript является языком интерпретируемым. JavaScript известен как язык, используемый при разработке веб-сайтов. Несмотря на это, разработчики Mozilla решили, что код Mozilla должен из себя представлять смесь этих двух языков.
Когда вы запускаете приложение, компоненты написанные на C и C++ запускаются первыми. Но на раннем этапе инициализируется технология под названим XPConnect, которая позволяет использовать интерпретатор JavaScript во время выполнения. Более того, дистрибутив браузера Mozilla состоит и из компилированного кода на языке C++ и файлов с исходным текстом на языке JavaScript.
Заметим, что скрипты на JavaScript не могут быть откомпилированы и исполнены операционной системой напрямую, мы опять же используем C и C++ для выполнения программы, скрипты на JavaScript выполняются внутри Mozilla.
Также заметим, что во время навигации по веб-страницам, содержащим код на JavaScript, этот код выполняется внутри песочницы, и не имеет доступа к внутренним объектам Mozilla. Доступны только те объекты, которые представлены в DOM (англ. Document Object Model — объектная модель документа).
Всякий раз, когда JS упоминается в этом документе, имеется в виду технология для обеспечения внутренней функциональности Mozilla. JavaScript чаще всего использовался в тех местах исходного кода, которые обрабатывают события пользовательского интерфейса. Большая часть этого документа объясняет приложение с точки зрения языка C++.
NSPR — портируемая среда исполнения Netscape (англ. Netscape portable runtime).
Главное требование, предъявляемое к ПО, разрабатываемому проектом Mozilla — кросс-платформенность, т. е. оно не должно быть ограничено какой-нибудь одной операционной системой.
Хотя С++ задуман как переносимый (портируемый) язык программирования, эта переносимость относится только к основной программной логике и структурам данных. Если вы хотите написать ПО для определенной операционной системы, вам необходимо использовать ту функциональность, которая для нее специфична. Часто вам нужна одинаковая функциональность на всех платформах, но для этого вам придется писать свое ПО для каждой из платформ.
Целью NSPR является посредничество между операционной системой и исходным кодом Mozilla, для облегчения программирования в других областях кода Mozilla. Перед тем, как использовать библиотечную функцию на языке С, следует убедиться, возможно ли ее кросс-платформенное выполнение при помощи NSPR.
Потоки
Mozilla является многопоточным приложением. Вам следует понять значение этих слов, прежде чем пытаться писать код для нее.
NSPR обеспечивает независимость от операционной системы для многопоточности программы. К примеру, чтобы обеспечить быстродействие пользовательского интерфейса, все действия, связанные с передачей данных по сети, выполняются в рамках отдельного потока.
Одним из требованиями к вашему C++ коду являться его пригодность к работе в многопоточной среде.
Объектно-ориентированное программирование и модульность
Код на С++ для Mozilla предполагает следование принципам ООП, которые включают в себя создание модульных компонентов, в которых доступ ко внутренним данным (переменным) разрешен/возможен только используя общедоступные интерфейсы вашего класса.
В большинстве простых проектов на C++ это значит только то, что вы заботливо моделируете ваши классы, используя public/protected/private там где это уместно, но исходный код все еще доступен везде. К примеру, в любое время вы можете изменить компонент любого класса из private в public, чтобы он мог быть доступен из любого другого места в вашем проекте. Это не применяется в Mozilla. Было решено, что Mozilla должна быть еще более модульной.
Исходный код Mozilla составлен из отдельных компонентов. Внутри компонента имеется полная свобода действий как в простых проектах, описанных в предыдущем абзаце, но между компонентами такая гибкость отсутствует.
Взаимодействие друг с другом компоненты осуществляют только при помощи четко определенных интерфейсов, использующих объектную модель компонентов (COM — Сomponent Object Model).
Интерфейсы
Концепция интерфейсов подобна той, что используется, например, в технологии CORBA. И CORBA и Mozilla используют похожий язык для описания интерфейсов, который называется XPIDL (IDL расшифровывается как «язык описания интерфейсов» (англ. Interface Definition Language)).
В окружении CORBA жизнь более ограничена и трудна, потому что имеется межпроцессное и межсетевое взаимодействие, которое Mozilla не очень активно использует. В распределенной среде CORBA труднее изменить компоненты интерфейса, потому что вы обычно не можете заменить все работающие системы одновременно. Если вы хотите изменить что-либо, вы должны определить новую версию интерфейса, но вы должны будете поддерживать и старую.
Mozilla не настолько распределенное приложение, как это написано, и можно легко изменить большинство интерфейсов, если того требует процесс разработки. Но так как браузер Mozilla во время работы встроен в некоторое окружение, то этому окружению необходимо положится на исправленный интерфейс, по этой причине интерфейсы могут быть замороженными. Такое состояние обычно отображено в определении интерфейса. Поскольку Mozilla стабилизируется с течением времени, подходя к заветной версии 1.0, процент замороженных интерфейсов будет увеличиваться.
Один шаг сборки Mozilla — автоматическая трансляция файлов с описанием интерфейса в заголовочные файлы C/C++. Этим занимается разработанный Mozilla компилятор IDL, xpidl.
Кроме методов и компонентов данных, интерфейсы имеют дополнительные атрибуты. У них есть UUID, число, однозначно идентифицирующее интерфейс. Они могут иметь ***скриптовые*** атрибуты, то есть доступные из JavaScript-кода. ***Скриптовые*** интерфейсы могут использовать только такие типы данных для параметров, которые возможны внутри среды исполнения JavaScript.
XPCOM / nsISupports / nsCOMPtr
XPCOM — это собственная реализация COM (объектной модели компонентов) проектом Mozilla. Приставка XP означает кросс-платформенность (не путайте с XP — названием операционной системы известного производителя). Кросс-платформенность позволяет XPCOM быть более универсальной, чем какой либо другой разновидности COM.
Вам нужно обязательно прочитать вводные документы по XPCOM на сайте mozilla.org. Для начала скажем, что XPCOM — ***механизм***, отвечающий за работу концепции COM. Это включает в себя и роль брокера объектов.
Обычно интерфейс описывает объект, который может быть использован для выполнения ***работы***. Если вам нужно сделать ***работу***, вам необходимо запросить реализацию, которая предоставляет требуемый интерфейс. Эта реализация может размещаться внутри другого компонента. Для определения того, какая конкретная реализация вам нужна, вы используете ***ID контракта***, текстовый идентификатор. ***ID контракта — это контракт*** на поведение реализации, доступной через правильно определенный интерфейс. Среда исполнения XPCOM знает о том, какой ***ID контракта*** реализует каждый класс, и какой компонент его предоставляет.
Даже если ваш код полностью находится внутри единственного компонента, и следовательно использование COM не обязательно, тем не менее она все равно часто используется. Одна из причин — гибкость. Другая — возможность доступа к функциональности из реализованных на JavaScript частей программной логики Mozilla. Mozilla предоставляет технологию под названием XPConnect, которая организует сообщение между интерпретируемым кодом на JavaScript и компилируемым на C++.
Всякий раз, когда вы запрашиваете экземпляр COM-объекта во время выполнения, создается объект нужного класса и вам передается указатель на интерфейс. По нескольким причинам было решено использовать для данных экземпляров подсчет ссылок. Одна из причин — эффективность, избавление от ненужных копий объектов. Другая — объекты данных должны передаваться между потоками, но при этом указывать на одни и те же объекты в памяти. И наконец, на одни и те же объекты данных могут ссылаться несколько компонентов, либо они могут храниться в нескольких списках.
Поскольку жизненный цикл ссылок разный, то проще всего хранить счетчик ссылок для каждого объекта, для записи количества ссылающихся на объект в данный момент. Когда вы получаете ссылку на объект (напрямую через ***механизм*** XPCOM или в результате вызова функции), вы должны быть уверены, что вы позаботились о подсчете ссылок. Вы должны уведомить объект о том, что вы собираетесь получить на него ссылку, или что вы закончили с ним работу, и удаляете ссылку. Такой способ позволяет объекту знать что в нем еще нуждаются. Когда он больше не нужен, он сам удаляет себя из памяти.
Для реализации общей функциональности все классы в Mozilla, реализующие какой-либо интерфейс, имеют общий базовый класс nsISupports, который реализует подсчет ссылок и автоматическое уничтожение объектов. Похожий базовый класс существует в любой реализации COM.
Есть одно общее правило: вы должны освобождать ресурсы которые занимаете. Например, если вы добавляете ссылку, вы должны освободить ее как только она перестает быть нужной. Если вы этого не делаете, вы можете вызвать такие проблемы, как утечки памяти.
В C++ это может быть сделано явными вызовами методов базового класса nsISupports. Но кроме того, что можно легко забыть вызвать эти методы, они также делают ваш код менее читаемым, особенно в случае, когда много функций/методов имеют несколько точек выхода (т. е. выражения return).
Вы должны быть уверены, что удалили все ссылки на объекты в каждой из ваших точек выхода. Для облегчения этой задачи, чтобы не повторять множество вызовов функции удаления, предоставлен общий вспомогательный класс для управления указателями на COM-объекты, он называется nsCOMPtr. Он существует только для XPCOM и облегчает работу с COM. Он симулирует поведение указателя, перегружая некоторые операторы. За исключением крайних случаев, вы должны следовать почти во всем коде общему правилу: всякий раз, собираясь использовать указатель «interface*» для объекта, реализующего интерфейс, используйте вместо этого локальную переменную «nsCOMPtr<interface>». Как только указатель ***станет «неправильным»***, его деструктор автоматически уменьшит счетчик ссылок, и если это возможно, уничтожит его.
В интерпретируемом JavaScript это сделать проще, благодаря автоматической сборке мусора. Это особое волшебство, автоматически освобождающее ссылки там, где это возможно. Однако, эта магия работает только в том случае, если вы не используете циклические ссылки. Например, если у вас есть два ссылающихся друг на друга объекта, но на эту пару объектов больше не ссылается никто, то они не могут быть удалены, и будут находиться в памяти до конца работы программы.
Исключения / nsresult
Во время выполнения программа может аварийно закончить работу. Один из механизмов для обработки таких ситуаций — использование исключений. Пока Mozilla использует механизм исключений в частях кода на JavaScript, но не на C++. Одной из причин было то, что исключения есть не на всех платформах, поэтому для эмуляции исключений в С++ в Mozilla используются коды возврата. Это значит, что в JavaScript вы можете использовать конструкции try-catch, а в С++ вы должны проверять код возврата после каждого вызова метода интерфейса. Тип кода возврата — nsresult. По этой причине, для возврата результата работы, тип которого объявлен в файле IDL, в С++ используется дополнительный параметр, передаваемый в метод.
The nsresult type is intended to transport additional failure reasons. Instead of simply reporting failure or success, an integer type is used instead, to allow for defining a lot of different error codes.
There are some general result codes, like NS_OK, which is used to indicate that everything went well and program flow can continue, or NS_ERROR_FAILURE, which is used if something went wrong, but no more details need to be provided as of yet.
In addition to that, each component can request its own range of integers to define error codes that will not overlap with those failure codes used in other areas of an application. Look at mozilla/xpcom/base/nsError.h for more information.
Строки в С++
В то время, как во множестве фреймворков и библиотек классов используется единственный строковый класс, разработчики Mozilla решили, что им нужно что-то более мощное. Они реализовали иерархию из нескольких классов строк, которые позволяют динамически менять поведение класса, действуя оптимально в различных ситуациях. Иногда вам просто нужна строка с постоянной длинной, иногда нужна строка, длинна которой увеличивается со временем. Поэтому, например, доступны не только непрерывные но и сегментированные строки.
Дополнительное требование — Mozilla полностью мультиязычное приложение. Поэтому все строки, используемые для показа информации пользователю, состоят из символы Unicode.
Типы строк основаны на шаблоне, с типом символов как тип переменной, может быть использована похожая логика с регулярными и Unicode строками.
Хотя такой подход, наличия многих классов строк, и подразумевает больше гибкости, недостатком является то, что чтение строк классов Mozilla - непросто.
Графический интерфейс пользователя / XUL
Большинство операционных систем предоставляют разработчику собственные инструменты разработки графического интерфейса, которые являются очень специфичными и очень различаются в различных системах.
Поэтому для кросс-платформенных приложений, таких как Mozilla, очень важным моментом является наличие собственного набора инструментария, который бы позволил скрыть платформо-зависимый уровень от логики приложения.
Большинство разрабатываемых в прошлом C/C++ библиотек были кросс-платформенными. Насколько мне известно, ни одна из них не используется в Mozilla, взамен этого мы всё ещё создаём свою собственную графическую среду.
When defining the layout of a GUI (graphical user interface), you can choose to go with either of two possibilities. You could define the absolute positions of each UI (user interface) element that you want to appear. This approach actually has been chosen by a lot of GUI libraries. But it has some drawbacks - you are not very flexible when the layout changes when adding more elements, because you have to rearrange all elements to new positions. You also have to do that graphically, to get immediate feedback which elements overlap, etc. But still, the UI might not look as intended when a different font with different metrics has to be used - this can make a UI unusable.
Разработчики Mozilla хотели получить что-то очень гибкое. Так как Mozilla является кросс-платформенным, он должен быть очень гибким благодари шрифтам.
Разработчики Mozilla выбрали для себя такую позицию, согласно которой содержимое пользовательского интерфейса формируется в логическом виде. В настоящее время мы не используем редактор пользовательского интерфейса, вместо этого мы создаем файл с инструкциями, определяющими, как пользовательский интерфейс должен выглядеть. И во время выполнения оформительский движок анализирует какие шрифты доступны, и принимая во внимание все требования к внешнему виду пользовательского интерфейса, создает конкретный вид интерфейса динамически. Это похоже на то, как работают вэб-браузеры, когда отображают вэб-страницы.
Вэб прошёл путь от систем, базирующихся в основном на тексте, до очень богатого графикой окружения, имеющего пользовательский интерфейс, схожий с многими программами. Поэтому только для браузеров было естественно использовать вэб-языки для определения своего пользовательского интерфейса. Было решено использовать синтаксис описания интерфейса, основанный на XML, который был назван XUL (язык расширяемого пользовательского интерфейса). (Хорошая ссылка о XUL доступна на XULPlanet).
XUL-файл содержит описание того из каких элементов состоит пользовательский интерфейс, и где эти элементы должны отображаться. Язык XUL определяет атрибуты, позволяющие программисту указывать действия, на которые должны реагировать контролы. Для того чтобы создать интерактивное поведении приложения можно определить функции JavaScript, которые будут вызываться когда происходёт то или иное событие пользовательского интерфейса. Внутри этих функций вы можете как реализовать требуемое поведение приложения напрямую, так и вызывать любую другую логику, доступную с использование COM-объектов, включая логику, написанную на C++.
In addition to the logical representation of the UI, people also prefer to have a pretty looking UI. To define the detailed characteristics of the UI, one uses CSS files, that defines, for example, which images will be used to display certain UI elements. This makes it flexible to define additional "looks" for the application, which are referred to as "themes" or "skins". Mozilla defines currently two themes, classic and modern, which are actively maintained by the Mozilla developers. While there exist additional themes for Mozilla, they often exist only for certain versions of Mozilla. It is a lot of work for a theme designer to stay in sync with all the changes that happen to the UI each day.
Build System and Tree
Nowadays Mozilla is mostly used as a group of many shared libraries, loaded dynamically at runtime as needed. The COM system allows for a development environment, where you often need to re-compile only a subset of the application when you make changes to areas of the source code base. This is a very convenient development environment, but it means a slowdown at runtime. On the other hand, it is possible to create an executable where Mozilla's internal components are mostly linked statically. Due to the size of the application, this link step takes a lot of time, and makes most sense when preparing a package for distribution.
Each component contains its own directory in the root directory of Mozilla. It might also contain subcomponents within it called modules. There are make files throughout the tree which tell Mozilla how to build.
Most of the platform-specific code is contained in only a few places of the tree. The layer that sits between the operating system and the rest of the Mozilla code are the interfaces that are implemented by this code. Only the platform-specific code pertaining to the platform in which the build is occurring is built.
Operating system messages are gathered by the platform-specific code and then sent to the platform-independent code in the same way.
Widgets in respect to the Mozilla project are OS-independent widgets drawn using platform-specific rendering techniques.
Within the tree, directories named public usually contain interface headers, and directories named src usually contain interface implementations and non-interface headers.
Building this program can be daunting for someone not used to a project this large. It can take from 20 minutes on a powerful workstation to a couple hours on an older PC. First, you have to get the source, then build it using the rules contained in http://www.mozilla.org/build/. While it's building, it will move all the header files to the dist/include directory so that you don't have to specify the directory of each header. It will also copy all the XUL, graphics, and JavaScript files (collectively called the chrome) to the chrome directory (a child of the directory containing the Mozilla binary). They are mapped to chrome:// URLs as defined in a file called jar.mn. In release versions of Mozilla, the chrome directory will only contain .jar files.
Building Mozilla is only part of the process. If you want to develop, you will have to maintain the tree using a program called CVS. When the build fails, you have to solve conflicts that occur when a merge between a file in your tree and one in the repository fails. Also, when you hack the tree, you have to build parts of the trees you modified. Occasionally, you will have to rebuild the entire tree using a process called depending, so that dependencies between source files can be determined. Also, you will occasionally have to rebuild the tree. Most likely, while you are doing this, you will be maintaining your own changes to the tree and trying to keep it in sync with others' changes.
Запуск приложения
Mozilla's COM system relies on type libraries and an internal registry of available components and interfaces. During application startup, a check is being made whether the registry is still up to date. If changed libraries are detected, the registry is updated. Each component is allowed to do initialization during that registration phase. If changed libraries are detected, they are loaded and their initialization logic is executed. Besides changed libraries, only those application components are loaded as they become required.
Internal Notification System
User does not subscribed to service
To make this more generic, it has been decided to use a central observer service. When component B triggers an action, it just notifies the observer service, specifying a descriptive name of the event. Components like A subscribe to the observer service giving the names of events they would like to observe. Using that principle, only the observer service has to deal with lists of components watching for events. When the observer receives notification for an event, it passes that notification on to all listening components for that event. Look at nsIObserver for more information.
Локализация
Mozilla was designed to separate code from human language. Whenever you need to use a text string that needs to be shown to a user, you are not allowed to store that string directly in your JavaScript or C++ file. Instead you define an descriptive identifier for your text, which is used in your C++ or JavaScript file. You use that identifier as a key and call members of the string bundle interface to retrieve the actual text. The text itself is stored in separate files, that only contain text. As Mozilla is modular, there are a lot of those files, each owned by a separate module. With that separation, it is easy to have translators create different language versions of those text files.
When defining the UI, there are two kinds of strings. Some strings are known at the time the application is compiled and packaged, like labels for input fields, or the text that appears within the help system. Other text is assembled dynamically at runtime.
Whenever you define text that does not need to be accessed at runtime, you define it in DTD files. You can refer to that text directly in XUL files.
If you need to work with text at runtime, for example if your text contains a placeholder for a user name that needs to be filled at runtime, you define your text in properties files.
Coding and Review Rules
Вы можете свободно скачивать Mozilla, изменять код и работать с версиями, содержащими ваши изменения.
However, one idea behind open source as used in Mozilla is the following: You received the source for free, and if you make changes, you should consider giving something back to the community. By doing so, you become a contributor.
But the Mozilla community has decided that it can't accept just any change to be integrated into the public central Mozilla codebase. If you want your code to become a part of it, you need to follow rules. These rules are not like law, but basically you must convince people that your change is good.
Original (English): An Introduction To Hacking Mozilla.
Translation: © denton, Владимир, neter, sir, Passerby, ShellSmith, Олег, MaksBR, flycat.info, takas, SmallTalk, dionys.myopenid.com, olegafx, the_corrector, renny, Choose, dadam, Softy, gopstr .
License: Creative Commons
