用_Mozilla_构建系统(Mozilla_Build_System)创建定制的_Firefox_扩展

  • 版本网址缩略名: 用_Mozilla_构建系统(Mozilla_Build_System)创建定制的_Firefox_扩展
  • 版本标题: 用_Mozilla_构建系统(Mozilla_Build_System)创建定制的_Firefox_扩展
  • 版本 id: 277280
  • 创建于:
  • 创建者: Y001
  • 是否是当前版本?
  • 评论 12 words added

修订内容

--Efree 23:53 2008年2月17日 (PST)

备注: 这篇文章中的所有说明只适用于 Mozilla 1.8 分支(如 Firefox 1.5)。我尝试保持和 trunk 更改一样更新,但是你不应该认为它适用于 1.7 分支 (如 Firefox 1.0) 或更旧的版本.

在创建 Firefox 的扩展上有一个wealth of material。这些文档的全部假设你只使用XULJavaScript 开发扩展。对于复杂的扩展,可能需要在 C++ 中创建组件以提供额外的功能。你会在你的扩展中包含 C++ 组件的原因包括:

  • 需要超过 JavaScript 代码能够达到的高性能。
  • 需要使用由 C 或 c++ 编写的第三方库。
  • Mozilla 接口的使用不能通过 XPCOM 暴露(如 NSPR).

这篇文章描述如何为一个带有上述需求的部分或全部的大型的,复杂的 Firefox 扩展设置开发环境。 The process of garnering this information has been somewhat painful due to the lack of published information on this topic, but has been assisted by the contributions of various members of the Mozilla development community, who have shown extraordinary patience in fielding ignorant newbie questions. I can’t stress enough that I’m far from a Mozilla expert, though I’m getting better. There may be much in this document that is inaccurate, misleading or just plain wrong. In fact, one of my goals in writing this is to fine-tune these instructions until they constitute a definite guide for hardcore hackers who want to extend the Firefox platform. If you’re one of the many people who know more about this than I do, your help in improving this article would be greatly appreciated.

I should also stress that you do not have to build Mozilla or use the Mozilla build system if you want to create C++ components for Mozilla. If you are just looking to create an XPCOM component or two, this is probably overkill, and you might want to take a look at this guide instead. On the other hand, if you are an experienced developer or team, and you know that you are going to build a large, complex extension, you would do well to consider the approach described in this article.

One final note: I’ve only tried these techniques inside Firefox, but they’ll probably work more or less unchanged on other Gecko-based platforms like Thunderbird or Seamonkey. If someone can confirm this and/or provide guidelines for what’s different, I’ll update the article to incorporate this information.

Bambi Meets Mozilla

None of this is for the faint of heart. In particular, the initial step involves building Mozilla, which is a huge - nay, gargantuan! - project. Many intelligent developers have been driven to the brink of insanity trying to build it for the first time. If you're not an experienced C++ developer, I wouldn’t even bother. Stick to JavaScript.

在 Windows 平台

我第一次构建 Mozilla,我使用这个指南。我再也记不住为什么,但是我在很多的地方获得 stuck,并且整个事情结束花费了我原来所预料使用的时间。Much furniture was smashed, much hair torn out by the roots.这里是一个comprehensive looking 指南 可以获得很好的回顾。Follow every step methodically and you’ll probably be alright. Focus on the fact that once you get the build working, it’ll probably work effortlessly from then on. Maybe.

On other platforms(在其它系统平台)

On other platforms, namely Linux and MacOS, the process is much easier. All the tools for building are available built-in, and therefore all you have to do is run some commands in the terminal. You can find full instructions for almost any OS here.

在其它系统平台,也就是说,在Linux和MacOS系统,过程更为简单。所有的构建工具均是内置可用的,因此只需在端机上运行一些命令即可。你会在这里发现几乎任何操作系统的全部指令。 (以上译文如有错误请高手及时勘正)

Structuring Your Project(构建项目)

Mozilla includes a number of complex extensions that are integrated into its build process. It has thus been necessary to solve all of the issues involved in creating and registering XPCOM components, building JAR files and manifests, installing the lot into the Firefox extensions/ directory and so forth. So it behooves us to piggyback on this infrastructure to build our extension.

Mozilla 包含了很多复杂的扩展,它们集成到其构建过程中。从而它必需解决所有关于创建和寄存 XPCOM 元件的版本,构建 JAR 文件和 manifests(译为文件清单吗?),安装 lot 到 Firefox 扩展/目录和向外,因此它适宜我们这个下部结构上构建自己的扩展。

First of all, think of a catchy name for your extension and create a directory with that name under the /mozilla/extensions/ directory. Use only lowercase letters. You should see a bunch of other directories (inspector/, reporter/ and so forth) at the same level in the build tree.

首先,给你的扩展起一个简单易记的名称,然后在/mozilla/extensions/目录下新建文件夹(跟你的扩展同名),注意仅使用小写字母,你会在entensions下发现其它同级文件夹(inspector/, reporter/ and so forth)

Note that before actually building anything, the Mozilla build system invokes a configuration process that generates the actual makefiles used for the build from makefile templates called Makefile.in. The actual makefiles tend to be very similar or identical to the templates, but the extra flexibility gained from having the makefiles generated dynamically is one of the things that makes the build system so powerful.

Anatomy of a Simple C++ Extension(一个简单C++扩展的剖析)

We assume that you are using C++ to write XPCOM components that can be used either from other C++ components or from JavaScript. The process of creating a component is actually relatively straightforward when the Mozilla build system is used.

In the simplest case, a component will consist of a single main directory with two subdirectories, public/ and src/. The main directory and each subdirectory must contain a Makefile.in (from now on I’ll just refer to this file as a makefile although we know that it is actually used to generate the real makefile). This makefile says two things. First of all, it lists the subdirectories that make up the extension, so the build system knows where to look for additional makefiles. Secondly, it instructs the build system to create a new extension, rather than copying the components directly into Firefox’s binary directory. The main advantage of using an extension is that it is easy to package everything up and install it on another machine.

So here’s your basic, plain-vanilla top-level makefile (Makefile.in in the main extension directory):

DEPTH		= ../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
	
DIRS		= public src
	
XPI_NAME		= myextension
INSTALL_EXTENSION_ID	= myextension@mycompany.com
XPI_PKGNAME		= myextension
	
DIST_FILES = install.rdf
	
include $(topsrcdir)/config/rules.mk

A detailed description of the make process, describing the key features of this makefile, can be found here. MODULE and XPI_NAME are both set to the name of your extension; they should be repeated in all project makefiles so that all of the files land in the same place in the XPI staging area (see below). INSTALL_EXTENSION_ID is the unique ID of your extension. This can be a GUID, but the format shown above is prettier and, let’s face it, a lot easier to remember. You don’t have to provide an XPI_PKGNAME, but if you do an XPI file, suitable for distribution, is automatically created in the root of the XPI staging area (/mozilla/$(MOZ_OBJDIR)/dist/xpi-stage/).

每一个扩展都必须包含一个用于告知 Firefox 如何安装它的 install.rdf 文件。 这个文件应该被放置于扩展文件目录的主目录下,而且文件的内容形式如下: 

<?xml version="1.0"?>
	
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
  <Description about="urn:mozilla:install-manifest">
    <em:id>myextension@mycompany.com</em:id>
    <em:version>0.1</em:version>
	
    <em:targetApplication>
      <!-- Firefox -->
      <Description>
        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
        <em:minVersion>1.0+</em:minVersion>
        <em:maxVersion>1.0+</em:maxVersion>
      </Description>
    </em:targetApplication>
	
    <!-- front-end metadata -->
    <em:name>My First Extension</em:name>
    <em:description>Just an example.</em:description>
    <em:creator>allpeers.com</em:creator>
    <em:homepageURL>http://www.allpeers.com/blog/</em:homepageURL>
  </Description>
</RDF>

There's a detailed description of the format of the install.rdf file. Use the DIST_FILES variable in the makefile to tell make to copy the file into the extension directory and (optional) XPI file.

公共接口

The public/ directory contains any interfaces that need to be accessed by other modules. These can be IDL files describing XPCOM interfaces, which are used to generate normal C++ header files for inclusion in your source files. They can also be normal C++ header files that are to be used directly by other modules. The easiest way to accomplish the latter is to use inline implementations for all methods so you don’t have any additional linking dependencies. Otherwise you will have to link statically to your module if you use these public headers in other modules. Personally I would discourage this practice (among other things, static linking means the same code gets loaded more than once into memory, and the code won’t be available from JavaScript or other non-C++ languages) and encourage the use of XPCOM wherever possible.

The makefile in the public/ directory should follow this model:

DEPTH		= ../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE		= myextension
XPIDL_MODULE	= myextension
	
XPI_NAME = myextension
	
EXPORTS = \
		myHeader.h \
		$(NULL)
	
XPIDLSRCS	= \
		myIFirstComponent.idl \
		myISecondComponent.idl \
		$(NULL)
	
include $(topsrcdir)/config/rules.mk

XPIDL_MODULE is the name of the generated XPT file that contains type information about your IDL interfaces. If you have multiple modules, make absolutely sure that you use a different value for XPIDL_MODULE for each one. Otherwise the first module’s XPT file will be overwritten by the second and you’ll get NS_ERROR_XPC_BAD_IID errors when you try to access its IDL interfaces from your code. The files under EXPORTS are copied directly to the /mozilla/$(MOZ_OBJDIR)/dist/include/myextension/ directory and are thus accessible from other modules (the value of MOZ_OBJDIR is defined in /mozilla/.mozconfig). XPIDLSRCS are run through the IDL processor, and the generated C++ headers are copied into the same include directory. In addition, an XPT (type library) file is generated and placed in the components/ subdirectory of your extension.

Source Files(源文件)

Now it’s time to create the makefile and source files in the src/ subdirectory. If you're implementing interfaces that you've described using IDL, the easiest way to do this is to leave the src/ directory empty and run make on the public/ directory only; this will be explained shortly.

Then open the generated header file for your interface from /mozilla/$(MOZ_OBJDIR)/dist/include/myextension/. It contains stubs for the component .H and .CPP files that you can copy and paste into your implementation files. All you have to do is fill in the implementation stubs in the C++ file and you’re good to go.

Here’s an example of the makefile you need to place into your src directory:

DEPTH		= ../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@

include $(DEPTH)/config/autoconf.mk

IS_COMPONENT = 1
MODULE = myextension
LIBRARY_NAME =  myExtension
USE_STATIC_LIBS = 1

XPI_NAME = myextension

REQUIRES	= xpcom \
		  string \
		  $(NULL)

CPPSRCS		= \
		  myFirstComponent.cpp \
		  mySecondComponent.cpp \
		  myExtension.cpp \
		  $(NULL)

include $(topsrcdir)/config/rules.mk

EXTRA_DSO_LDOPTS += \
  $(XPCOM_GLUE_LDOPTS) \
  $(NSPR_LIBS) \
  $(NULL)

# NOTE: If you are coding against the 1.8.0 branch (not 1.8 branch or trunk), the
# above line won't work, due to linker flag issues. Use the following 
# variables instead:
#
# EXTRA_DSO_LDOPTS += \
#   $(MOZ_COMPONENT_LIBS) \
#   $(NULL)
#
# Unfortunately, using MOZ_COMPONENT_LIBS links against xpcom_core, which means
# your components will not work in future versions of Firefox.

The REQUIRES section tells make which modules your components uses. This causes the relevant subdirectories of /mozilla/$(MOZ_OBJDIR)/dist/include/ to be added to the C++ compiler's include path. If you’re including Mozilla headers and the compiler isn’t finding them, it could well mean that you haven’t listed all of the necessary modules here. CPPSRCS lists the source files that need to be built.

In this example, the first two files contain the implementation of the extension’s two components. The final file, myExtension.cpp, contains the code necessary to register these components, as described in the next section.

Registering Your Components(注册控件)

In order to use your components from other C++ modules and JavaScript, you first have to register them. To do this, your extension needs to implement a class that exposes the nsIModule interface, which has methods for accessing the components defined in a module. Luckily, this can be accomplished through the use of a few simple macros, so you don’t have to concern yourself with the messy details of what’s happening under the hood.

The first step is to define a CID, contract ID and class name for each of your components. Place the following code (adapting the #defines accordingly) into the header of each component that you want to be able to instantiate using the component manager:

// {00000000-0000-0000-0000-000000000000}
#define MYFIRSTCOMPONENT_CID \
	{ 0x00000000, 0x0000, 0x0000, \
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
	
#define MYFIRSTCOMPONENT_CONTRACTID	"@mycompany.com/myfirst;1"
#define MYFIRSTCOMPONENT_CLASSNAME	"My First Component"

Obviously you need to fill in the CID with a real GUID. Under Windows, this can be generated using guidgen.exe. Unix people can use uuidgen (comes standard with most unixes and Linux distros).

Now create the myExtension.cpp file like so:

现在,建立一个像下面那样的myExtension.cpp文件:

#include "nsXPCOM.h"
	
#include "nsIGenericFactory.h"
	
/**
 * Components to be registered
 */
#include "myFirstComponent.h"
#include "mySecondComponent.h"
	
NS_GENERIC_FACTORY_CONSTRUCTOR(myFirstComponent)
NS_GENERIC_FACTORY_CONSTRUCTOR(mySecondComponent)
	
//----------------------------------------------------------
	
static const nsModuleComponentInfo components[] =
{
	{
		MYFIRSTCOMPONENT_CLASSNAME,
		MYFIRSTCOMPONENT_CID,
		MYFIRSTCOMPONENT_CONTRACTID,
		myFirstComponentConstructor
	},
	{
		MYSECONDCOMPONENT_CLASSNAME,
		MYSECONDCOMPONENT_CID,
		MYSECONDCOMPONENT_CONTRACTID,
		mySecondComponentConstructor
	},
};
	
NS_IMPL_NSGETMODULE(MyExtension, components)

The NS_IMPL_NSGETMODULE macro creates the appropriate module object providing access to all of the components listed in the nsModuleComponentInfo array.

Building It(建立它)

As mentioned above, you’ll probably want to build your extension immediately after creating your IDL files in order to generate the C++ stubs for your component implementations. I’m assuming that you’ve already built Firefox successfully. If not, return immediately to the beginning of this article and don’t come back til you have a functioning firefox.exe. Do not pass go. Do not collect $200.

Still here? Okay, now we have to modify your .mozconfig (in the /mozilla/ root directory) so that your extension is built along with Mozilla. Add the following line at the end of the file:

ac_add_options --enable-extensions=default,myextension

Now launch make from the Mozilla root:

make -f client.mk build

Even if you have an up-to-date Firefox build, you’ll have to wait a while for make to recurse over the entire Mozilla source tree looking for new stuff (on my machine, which is pretty fast, this takes a good 10-15 minutes). Eventually it will reach your extension and generate a bunch of stuff under /mozilla/$(MOZ_OBJDIR)/:

  • Exported header files and generated header files (from IDL) in dist/include/myextension/
  • Static libraries for your modules in dist/lib/ (in case other modules want to link statically to your stuff instead of using XPCOM).
  • XPI file in dist/xpi-stage/myextension.xpi.
  • Generated makefiles for your projects in extensions/myextension/ (remember, we’re under /mozilla/$(MOZ_OBJDIR)/.
  • Everything else in dist/bin/extensions/myextension@mycompany.com/.

A lot of this stuff won’t get created on this first pass since make will gag when it doesn’t find the source files for your components. Don’t worry about this; all you need are the generated header files that contain the C++ implementation stubs. Go back and flesh out the C++ implementation of your components so that the build can complete next time. Remember that you should never, ever modify any of these generated files. Always modify the files used to generate them and rerun make. There might be exceptions to this rule, but if you’re changing the generated files directly, you’re probably doing something wrong.

The process of walking the entire Mozilla tree takes a long time. If you already have a Mozilla build, you can avoid this by creating a makefile for your extension directly. Go to the root of your $(MOZ_OBJDIR) and (from a bash-compatible shell) enter:

../build/autoconf/make-makefile extensions/myextension

If your $(MOZ_OBJDIR) is located outside your $(TOPSRCDIR), you'll need to do:

$(TOPSRCDIR)/build/autoconf/make-makefile -t $(TOPSRCDIR) extensions/myextension

in order for the script to know where your source is (it'll use the extension path you gave it relative to the current dir to figure out where you want your makefiles to go).

This will generate the proper makefile for your extension. Whether you build the whole Mozilla tree or take this shortcut, you can build from now on by going to /mozilla/$(MOZ_OBJDIR)/extensions/myextension/ and typing "make" on the command line. It should build your component without bothering with the rest of Mozilla. If everything works out, you’ll see your XPI file in the XPI staging area. You’ll also see the "exploded" version of the XPI (i.e. the unzipped directory structure) underneath /mozilla/$(MOZ_OBJDIR)/dist/bin/extensions. (If something goes wrong, figure out what, fix it and then come back here and add it to this article.)

To make sure that the build really finished, launch Firefox and check that your extension is listed when you select Tools/Extensions. If you are using Firefox as your regular browser (and if you’re not, why not!?), you might be annoyed by the fact that you have to close regular Firefox before running your custom-built version. If so, try setting the MOZ_NO_REMOTE environment variable to "1" before running the development version of Firefox. You’ll also need to use a different profile for your development version:

firefox -P development

Where development is replaced with the name of the extra profile you’ve created. This will let you run both versions of Firefox simultaneously, saving you oodles of time over the course of the build/test cycle.

No Place Like Chrome

Yippee-ki-yay! Now you have an extension that does, well, absolutely nothing. It’s time to do something with those groovy components that you’ve implemented and registered. The simplest way to do this is to write some JavaScript and XUL code. At this point, it would be very helpful to have a bit of experience writing "regular" extensions (i.e. without using custom C++ components). If you’ve never done this, I strongly recommend that you think of a cool idea for something simple that you’ve always wanted to tweak in Firefox and write it. Just displaying a new menu item that opens a "Hello, World!" dialog box would be already be a great exercise to get warmed up with.

Assuming you know how to write XUL/JavaScript extensions, you’re aware that the most important stuff goes in the chrome/ directory of your extension. Well, the fact that you’re also using C++ components doesn’t change that one whit. So now you need to create the normal content/, locale/ and skin/ directories in which to place your chrome files. Personally I like placing these directly under the root directory of my module, but I don’t suppose it makes any difference if you prefer putting them under a chrome/ subdirectory or whatever. Let freedom reign!

Once you’ve written the necessary chrome files (for instance, an overlay that adds a menu item to instantiate and use one of your components), you need to package them up as part of your extension. This is accomplished through the use of a JAR Manifest. For our simple extension example, this file might look something like this:

myextension.jar:
%  content myextension %content/
%  locale myextension en-US %locale/en-US/
%  skin myextension classic/1.0 %skin/classic/
%  overlay chrome://browser/content/browser.xul chrome://myextension/content/MyExtensionOverlay.xul
	content/MyExtensionOverlay.js		(content/MyExtensionOverlay.js)
	content/MyExtensionOverlay.xul		(content/MyExtensionOverlay.xul)
	locale/en-US/MyExtension.dtd		(locale/en-US/MyExtension.dtd)
	locale/en-US/MyExtension.properties	(locale/en-US/MyExtension.properties)
	skin/classic/MyExtension.css		(skin/classic/MyExtension.css)

Place this code in a file called jar.mn in the root directory of your extension, making sure that the paths in parentheses point to actual files (when interpreted relative to the root directory). You also have to make one small change to the makefile in the same directory, adding the following line:

USE_EXTENSION_MANIFEST = 1

This tells make to create a single manifest file called chrome.manifest instead of creating separate manifests with goofy names for each package.

Now launch make again, and you should see a chrome subdirectory appear in your extension (/mozilla/$(MOZ_OBJDIR)/dist/bin/extensions/myextension@mycompany.com/). Note that the chrome directory contains a JAR (i.e. ZIP) file with all the chrome files listed in jar.mn as well as a complete directory structure mirroring that of the JAR file. The directory structure, however, is empty. Why? I don’t know. Don’t worry about this, the files in the JAR are the ones that are actually used.

Keeping it Complex

If you’re developing a really complex extension with lots of XPCOM components, you’ll probably want to divide your code up into smaller modules.

Kinda, Sorta Complex Extensions

For a moderately complex extension, it’s probably enough just to subdivide the code into a single level of modules. Let’s assume that you have a base/ module that defines a bunch of basic XPCOM components and an advanced/ module that defines some chrome as well as other components that use the basic components. Your complete directory structure will look something like this:

  • myextension
    • base
      • public
      • src
    • advanced
      • content
      • locale
        • en-US
        • ...other locales...
      • public
      • skin
        • classic
        • ...other skins...
      • src

Other than that, nothing really changes. The makefiles in the base/ and advanced/ directories should look more or less like your original root makefile, remembering to change the DEPTH variable to account for the fact that they’ve moved a level further away from the Mozilla root. You also need to remove the DIST_FILES variable since that’s going to be in the top-level makefile. Every makefile that generates anything should define the XPI_NAME variable to make sure generated files go into your extension and not into the global components/ directory. In fact, just define this in every makefile to be safe. You can use the same MODULE in both base/ and advanced/ so that all the generated include files go into the same directory, but make sure that you don’t use the same XPIDL_MODULE in the two public/ directories or one of the component type libraries (i.e. XPT files) will overwrite the other one and all hell will break loose.

Each module must also have a different value for the LIBRARY_NAME variable. This is the name of the generated dynamic library, so if we call the libraries "myBase" and "myAdvanced", we’ll end up with myBase.dll and myAdvanced.dll (on Windows, at least). And each of these modules is going to have a separate C++ file for registering components. So there will be two files that look like myExtension.cpp in the original example, say Base.cpp and Advanced.cpp. Finally, each module will obviously have its own jar.mn, though they can reference the same JAR filename and package name if you want all the chrome files to be organized in a single JAR file and package. The only file that really stays put is install.rdf, which still exists once and only once in the extension root directory.

As for the top-level makefile, it will now look like this:

DEPTH		= ../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
	
DIRS		= base advanced
	
XPI_NAME               = myextension
INSTALL_EXTENSION_ID   = myextension@mycompany.com
XPI_PKGNAME		= myextension
	
DIST_FILES = install.rdf
	
include $(topsrcdir)/config/rules.mk
Seriously Complex Extensions

At some point, even a single module may grow to the point where you want to divide it further into submodules. The difference between having separate modules and having a single module with separate submodules is that the submodules all share the same file for registering components (the famous myExtension.cpp file), and when compiled they create a single dynamic library. The decision to split a module into submodules is all about code organization; it doesn’t really affect the final product at all.

To split a module into submodules, first create a subdirectory for each submodule. Then create an additional directory called build/. Each submodule will be configured to create a static library, and the build/ directory will pull these libraries together to create a single dynamic component library. Confused? Here’s an example, showing just the advanced/ subbranch of the myextension/ directory:

  • advanced
    • build
    • intricate
      • public
      • src
    • multifarious
      • public
      • src
    • content
    • locale
      • en-US
      • ...other locales...
    • skin
      • classic
      • ...other skins...

As you can see, we’ve split advanced/ into two submodules: intricate/ and multifarious/, and we’ve added an additional build/ subdirectory. We’ve left the chrome directories directly under advanced/, since they aren’t tied to any specific submodule. This means that jar.mn will stay in the same place.

The intricate/ and multifarious/ makefiles will look a lot like the original advanced/ makefile, but we’ll need to tweak them a bit. As always, we have to adjust the DEPTH variable since the makefiles are deeper in the directory structure. And we should change the LIBRARY_NAME to indicate that we’re generating a static library for each submodule. By convention the "_s" suffix is used for this purpose. So let’s call them "myIntricate_s" and "myMultifarious_s". Finally, we define the variable FORCE_STATIC_LIB, resulting in a makefile that starts something like this:

DEPTH		= ../../../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
LIBRARY_NAME = myIntricate_s
FORCE_STATIC_LIB = 1
USE_STATIC_LIBS = 1
	
XPI_NAME = myextension
	
...more stuff here...

The build makefile pulls together the static libraries generated by the submodules and creates a single (dynamic) component library:

DEPTH		= ../../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
IS_COMPONENT = 1
MODULE = myextension
LIBRARY_NAME = myAdvanced
USE_STATIC_LIBS = 1
	
XPI_NAME = myextension
	
DEFINES += XPCOM_GLUE
	
SHARED_LIBRARY_LIBS = \
		$(DIST)/lib/$(LIB_PREFIX)myIntricate_s.$(LIB_SUFFIX) \
		$(DIST)/lib/$(LIB_PREFIX)myMultifarious_s.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)xpcom.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)nspr4.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)plds4.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)plc4.$(LIB_SUFFIX) \
		$(NULL)
	
REQUIRES	= \
		xpcom \
		string \
		$(NULL)
	
CPPSRCS		= \
		Advanced.cpp \
		$(NULL)
	
include $(topsrcdir)/config/rules.mk
	
LOCAL_INCLUDES += \
        -I$(srcdir)/../intricate/src \
        -I$(srcdir)/../multifarious/src \
        $(NULL)

The makefile in the advanced/ directory should list the intricate/, multifarious/ and build/ directories in its DIRS variable. Make sure that build/ comes last since it can’t create the component library until the other makefiles have completed.

Other Topics(其它)

Adding Data Files to Your Extensions(添加数据文件到扩展)

In some cases, you may wish to include additional files in your extension that don’t belong in the chrome/ subdirectory. Examples might be database files or XML schemas. This can be achieved by adding a custom step to your makefile that copies the files from the source tree into the extension’s target directory.

Copying Data Files Into Target Directory(复制数据文件到目标文件夹)

Let’s say that you have some data files containing statistical information that you want to include in your extension and make available to your components. You’ve placed these files, which have the extension .TXT, into a stats/ subdirectory under your extension directory in the source tree. The following makefile rule can be used to copy these files into the final target directory of the extension:

libs::
	if test ! -d $(FINAL_TARGET)/stats; then \
		$(NSINSTALL) -D $(FINAL_TARGET)/stats; \
	fi
	$(INSTALL) $(srcdir)/*.txt $(FINAL_TARGET)/stats
Accessing Data Files From Components(控件访问数据文件)

The trick to accessing your data files is to figure out where the home directory of your extension is. Rumor has it that at some future date, this will possible through the nsIExtensionManager interface or something similar. In the meantime, there is a simple and reliable hack that can be used to achieve this. In the implementation of any JavaScript XPCOM component, there is a special __LOCATION__ (two leading and two trailing underscores) symbol that points to the component’s implementation file. So you can write a simple component which deduces the root directory of your extensions by extrapolating from its location.

This article explains how to create an XPCOM component in JavaScript. You’ll need an IDL file for an interface that looks something like this:

interface myILocation : nsISupports
{
    readonly attribute nsIFile locationFile;
};

Place the IDL file in the public/ directory of your project or subproject. In the src/ directory, place the JavaScript file that implements the component. The component implementation will include the methods for retrieving the path or file for the extension’s home directory:

myLocation.prototype =
{
  QueryInterface: function(iid)
  {
    if (iid.equals(nsISupports))
      return this;
    if (iid.equals(myILocation))
      return this;
	
    Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
    return null;
  },
	
  get locationFile()
  {
     return __LOCATION__.parent.parent;
  }
}

This assumes that the component resides in a subdirectory of the extension directory (by convention, this directory is called components/). The parent property of __LOCATION__ returns the components/, and the parent of this is the extension directory.

The last step is to modify the makefile of the source directory where you placed your JavaScript file so that it is copied into the appropriate location in the extension:

libs::
	$(INSTALL) $(srcdir)/*.js $(FINAL_TARGET)/components

Now you can instantiate an instance of this component and use the locationFile property to get an nsIFile interface that points to your extension’s home directory.

Using Third-Party Libraries

For more sophisticated extensions, you may want to integrate third-party libraries that provide specialized functionality for database connectivity, image processing, networking and the like. If you want your extension to run on all Firefox platforms, you will need to have the source code for the library in question, so I assume that this is available.

The most convenient approach from the perspective of the development cycle is to create a Mozilla-style makefile for the library. This works well for libraries that have a straightforward make process without extensive configuration. A good example of this is the SQLite library included in the Mozilla build tree at db/sqlite. By adapting the makefile in this way, the library is created as part of the standard Mozilla build process, which eliminates additional build steps. The downside is that you will need to update the modified makefile any time a new version of the library is released.

For libraries that have complex configuration processes, use a non-standard compiler or have other special characteristics, it may be unfeasible to create a Mozilla-compliant makefile. In this case, I would recommend placing the entire library distribution inside the project or subproject that uses it. So if library acmelib is used inside the multifarious/ subproject in the above example, it would be placed as a subdirectory underneath that subproject (at the same level as public/ and src/).

Of course, this means that you will have to build acmelib manually on all platforms before launching the Mozilla build. But at least you can then refer to include files and import libraries from your component using relative paths.

Building for Multiple Platforms(跨平台制作)

TODO

  

原始文档信息

{{ languages( { "es": "es/Crear_una_extensi\u00f3n_personalizada_de_Firefox_con_el_Mozilla_Build_System", "fr": "fr/Cr\u00e9ation_d\'extensions_pour_Firefox_avec_le_syst\u00e8me_de_compilation_de_Mozilla", "it": "it/Creare_Estensioni_personalizzate_per_Firefox_con_il_sistema_di_sviluppo_di_Mozilla", "ja": "ja/Creating_Custom_Firefox_Extensions_with_the_Mozilla_Build_System" } ) }}

修订版来源

<p>--<a href="/User:Efree" title="User:Efree">Efree</a> 23:53 2008年2月17日 (PST)</p>
<div class="note"><strong>备注:</strong> 这篇文章中的所有说明只适用于 Mozilla 1.8 分支(如 Firefox 1.5)。我尝试保持和 trunk 更改一样更新,但是你不应该认为它适用于 1.7 分支 (如 Firefox 1.0) 或更旧的版本.</div>
<p>在创建 Firefox 的扩展上有一个<a href="/cn/Extensions" title="cn/Extensions">wealth of material</a>。这些文档的全部假设你只使用<a href="/cn/XUL" title="cn/XUL">XUL</a> 和 <a href="/cn/JavaScript" title="cn/JavaScript">JavaScript</a> 开发扩展。对于复杂的扩展,可能需要在 C++ 中创建组件以提供额外的功能。你会在你的扩展中包含 C++ 组件的原因包括:</p>
<ul> <li>需要超过 JavaScript 代码能够达到的高性能。</li> <li>需要使用由 C 或 c++ 编写的第三方库。</li> <li>Mozilla 接口的使用不能通过 <a href="/cn/XPCOM" title="cn/XPCOM">XPCOM</a> 暴露(如 <a href="/cn/NSPR" title="cn/NSPR">NSPR</a>).</li>
</ul>
<p>这篇文章描述如何为一个带有上述需求的部分或全部的大型的,复杂的 Firefox 扩展设置开发环境。 The process of garnering this information has been somewhat painful due to the lack of published information on this topic, but has been assisted by the contributions of various members of the Mozilla development community, who have shown extraordinary patience in fielding ignorant newbie questions. I can’t stress enough that I’m far from a Mozilla expert, though I’m getting better. There may be much in this document that is inaccurate, misleading or just plain wrong. In fact, one of my goals in writing this is to fine-tune these instructions until they constitute a definite guide for hardcore hackers who want to extend the Firefox platform. If you’re one of the many people who know more about this than I do, your help in improving this article would be greatly appreciated.</p>
<p>I should also stress that you do <em>not</em> have to build Mozilla or use the Mozilla build system if you want to create C++ components for Mozilla. If you are just looking to create an <a href="/cn/XPCOM" title="cn/XPCOM">XPCOM</a> component or two, this is probably overkill, and you might want to take a look at <a class="external" href="http://www.iosart.com/firefox/xpcom/">this guide</a> instead. On the other hand, if you are an experienced developer or team, and you know that you are going to build a large, complex extension, you would do well to consider the approach described in this article.</p>
<p>One final note: I’ve only tried these techniques inside Firefox, but they’ll probably work more or less unchanged on other Gecko-based platforms like Thunderbird or Seamonkey. If someone can confirm this and/or provide guidelines for what’s different, I’ll update the article to incorporate this information.</p>
<h3 id="Bambi_Meets_Mozilla" name="Bambi_Meets_Mozilla">Bambi Meets Mozilla</h3>
<p>None of this is for the faint of heart. In particular, the initial step involves building Mozilla, which is a huge - nay, gargantuan! - project. Many intelligent developers have been driven to the brink of insanity trying to build it for the first time. If you're not an experienced C++ developer, I wouldn’t even bother. Stick to JavaScript.</p>
<h4 id=".E5.9C.A8_Windows_.E5.B9.B3.E5.8F.B0" name=".E5.9C.A8_Windows_.E5.B9.B3.E5.8F.B0">在 Windows 平台</h4>
<p>我第一次构建 Mozilla,我使用<a href="/cn/Windows_Build_Prerequisites" title="cn/Windows_Build_Prerequisites">这个指南</a>。我再也记不住为什么,但是我在很多的地方获得 stuck,并且整个事情结束花费了我原来所预料使用的时间。Much furniture was smashed, much hair torn out by the roots.这里是一个<a class="external" href="http://whereswalden.com/mozilla/msvcfree/">comprehensive looking 指南</a> 可以获得很好的回顾。Follow every step methodically and you’ll probably be alright. Focus on the fact that once you get the build working, it’ll probably work effortlessly from then on. Maybe.</p>
<h4 id="On_other_platforms.28.E5.9C.A8.E5.85.B6.E5.AE.83.E7.B3.BB.E7.BB.9F.E5.B9.B3.E5.8F.B0.29" name="On_other_platforms.28.E5.9C.A8.E5.85.B6.E5.AE.83.E7.B3.BB.E7.BB.9F.E5.B9.B3.E5.8F.B0.29">On other platforms(在其它系统平台)</h4>
<p>On other platforms, namely Linux and MacOS, the process is much easier. All the tools for building are available built-in, and therefore all you have to do is run some commands in the terminal. You can find full instructions for almost any OS <a href="/cn/%E7%BC%96%E8%AF%91%E6%89%8B%E5%86%8C" title="cn/编译手册">here</a>.</p>
<p>在其它系统平台,也就是说,在Linux和MacOS系统,过程更为简单。所有的构建工具均是内置可用的,因此只需在端机上运行一些命令即可。你会在<a href="/cn/%E7%BC%96%E8%AF%91%E6%89%8B%E5%86%8C" title="cn/编译手册">这里</a>发现几乎任何操作系统的全部指令。 (以上译文如有错误请高手及时勘正)</p>
<h3 id="Structuring_Your_Project.28.E6.9E.84.E5.BB.BA.E9.A1.B9.E7.9B.AE.29" name="Structuring_Your_Project.28.E6.9E.84.E5.BB.BA.E9.A1.B9.E7.9B.AE.29">Structuring Your Project(构建项目)</h3>
<p>Mozilla includes a number of complex extensions that are integrated into its build process. It has thus been necessary to solve all of the issues involved in creating and registering XPCOM components, building JAR files and manifests, installing the lot into the Firefox <code>extensions/</code> directory and so forth. So it behooves us to piggyback on this infrastructure to build our extension.</p>
<p>Mozilla 包含了很多复杂的扩展,它们集成到其构建过程中。从而它必需解决所有关于创建和寄存 XPCOM 元件的版本,构建 JAR 文件和 manifests(译为文件清单吗?),安装 lot 到 Firefox <code>扩展/</code>目录和向外,因此它适宜我们这个下部结构上构建自己的扩展。</p>
<p>First of all, think of a catchy name for your extension and create a directory with that name under the <code>/mozilla/extensions/</code> directory. Use only lowercase letters. You should see a bunch of other directories (<code>inspector/</code>, <code>reporter/</code> and so forth) at the same level in the build tree.</p>
<p>首先,给你的扩展起一个简单易记的名称,然后在<code>/mozilla/extensions/</code>目录下新建文件夹(跟你的扩展同名),注意仅使用小写字母,你会在entensions下发现其它同级文件夹(<code>inspector/</code>, <code>reporter/</code> and so forth)</p>
<p>Note that before actually building anything, the Mozilla build system invokes a configuration process that generates the actual makefiles used for the build from makefile templates called <code>Makefile.in</code>. The actual makefiles tend to be very similar or identical to the templates, but the extra flexibility gained from having the makefiles generated dynamically is one of the things that makes the build system so powerful.</p>
<h4 id="Anatomy_of_a_Simple_C.2B.2B_Extension.28.E4.B8.80.E4.B8.AA.E7.AE.80.E5.8D.95C.2B.2B.E6.89.A9.E5.B1.95.E7.9A.84.E5.89.96.E6.9E.90.29" name="Anatomy_of_a_Simple_C.2B.2B_Extension.28.E4.B8.80.E4.B8.AA.E7.AE.80.E5.8D.95C.2B.2B.E6.89.A9.E5.B1.95.E7.9A.84.E5.89.96.E6.9E.90.29">Anatomy of a Simple C++ Extension(一个简单C++扩展的剖析)</h4>
<p>We assume that you are using C++ to write XPCOM components that can be used either from other C++ components or from JavaScript. The process of creating a component is actually relatively straightforward when the Mozilla build system is used.</p>
<p>In the simplest case, a component will consist of a single main directory with two subdirectories, <code>public/</code> and <code>src/</code>. The main directory and each subdirectory must contain a <code>Makefile.in</code> (from now on I’ll just refer to this file as a makefile although we know that it is actually used to generate the real makefile). This makefile says two things. First of all, it lists the subdirectories that make up the extension, so the build system knows where to look for additional makefiles. Secondly, it instructs the build system to create a new extension, rather than copying the components directly into Firefox’s binary directory. The main advantage of using an extension is that it is easy to package everything up and install it on another machine.</p>
<p>So here’s your basic, plain-vanilla top-level makefile (<code>Makefile.in</code> in the main extension directory):</p>
<pre>DEPTH		= ../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
	
DIRS		= public src
	
XPI_NAME		= myextension
INSTALL_EXTENSION_ID	= myextension@mycompany.com
XPI_PKGNAME		= myextension
	
DIST_FILES = install.rdf
	
include $(topsrcdir)/config/rules.mk
</pre>
<p>A detailed description of the make process, describing the key features of this makefile, can be found <a href="/cn/How_Mozilla's_build_system_works" title="cn/How_Mozilla's_build_system_works">here</a>. <strong>MODULE</strong> and <strong>XPI_NAME</strong> are both set to the name of your extension; they should be repeated in all project makefiles so that all of the files land in the same place in the XPI staging area (see below). <strong>INSTALL_EXTENSION_ID</strong> is the unique ID of your extension. This can be a GUID, but the format shown above is prettier and, let’s face it, a lot easier to remember. You don’t have to provide an <strong>XPI_PKGNAME</strong>, but if you do an XPI file, suitable for distribution, is automatically created in the root of the XPI staging area (<code>/mozilla/$(MOZ_OBJDIR)/dist/xpi-stage/</code>).</p>
<p>每一个扩展都必须包含一个用于告知 Firefox 如何安装它的 <code>install.rdf</code> 文件。 这个文件应该被放置于扩展文件目录的主目录下,而且文件的内容形式如下: </p>
<pre>&lt;?xml version="1.0"?&gt;
	
&lt;RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#"&gt;
  &lt;Description about="urn:mozilla:install-manifest"&gt;
    &lt;em:id&gt;myextension@mycompany.com&lt;/em:id&gt;
    &lt;em:version&gt;0.1&lt;/em:version&gt;
	
    &lt;em:targetApplication&gt;
      &lt;!-- Firefox --&gt;
      &lt;Description&gt;
        &lt;em:id&gt;{ec8030f7-c20a-464f-9b0e-13a3a9e97384}&lt;/em:id&gt;
        &lt;em:minVersion&gt;1.0+&lt;/em:minVersion&gt;
        &lt;em:maxVersion&gt;1.0+&lt;/em:maxVersion&gt;
      &lt;/Description&gt;
    &lt;/em:targetApplication&gt;
	
    &lt;!-- front-end metadata --&gt;
    &lt;em:name&gt;My First Extension&lt;/em:name&gt;
    &lt;em:description&gt;Just an example.&lt;/em:description&gt;
    &lt;em:creator&gt;allpeers.com&lt;/em:creator&gt;
    &lt;em:homepageURL&gt;http://www.allpeers.com/blog/&lt;/em:homepageURL&gt;
  &lt;/Description&gt;
&lt;/RDF&gt;
</pre>
<p>There's a <a href="/cn/Install_Manifests" title="cn/Install_Manifests">detailed description</a> of the format of the <code>install.rdf</code> file. Use the <strong>DIST_FILES</strong> variable in the makefile to tell <code>make</code> to copy the file into the extension directory and (optional) XPI file.</p>
<h4 id=".E5.85.AC.E5.85.B1.E6.8E.A5.E5.8F.A3" name=".E5.85.AC.E5.85.B1.E6.8E.A5.E5.8F.A3">公共接口</h4>
<p>The <code>public/</code> directory contains any interfaces that need to be accessed by other modules. These can be <a class="external" href="http://www.mozilla.org/scriptable/xpidl/idl-authors-guide/index.html">IDL</a> files describing <a href="/cn/XPCOM" title="cn/XPCOM">XPCOM</a> interfaces, which are used to generate normal C++ header files for inclusion in your source files. They can also be normal C++ header files that are to be used directly by other modules. The easiest way to accomplish the latter is to use inline implementations for all methods so you don’t have any additional linking dependencies. Otherwise you will have to link statically to your module if you use these public headers in other modules. Personally I would discourage this practice (among other things, static linking means the same code gets loaded more than once into memory, and the code won’t be available from JavaScript or other non-C++ languages) and encourage the use of XPCOM wherever possible.</p>
<p>The makefile in the <code>public/</code> directory should follow this model:</p>
<pre>DEPTH		= ../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE		= myextension
XPIDL_MODULE	= myextension
	
XPI_NAME = myextension
	
EXPORTS = \
		myHeader.h \
		$(NULL)
	
XPIDLSRCS	= \
		myIFirstComponent.idl \
		myISecondComponent.idl \
		$(NULL)
	
include $(topsrcdir)/config/rules.mk
</pre>
<p><strong>XPIDL_MODULE</strong> is the name of the generated XPT file that contains type information about your <a class="external" href="http://www.mozilla.org/scriptable/xpidl/idl-authors-guide/index.html">IDL</a> interfaces. If you have multiple modules, make absolutely sure that you use a different value for <strong>XPIDL_MODULE</strong> for each one. Otherwise the first module’s XPT file will be overwritten by the second and you’ll get <strong>NS_ERROR_XPC_BAD_IID</strong> errors when you try to access its IDL interfaces from your code. The files under <strong>EXPORTS</strong> are copied directly to the <code>/mozilla/$(MOZ_OBJDIR)/dist/include/myextension/</code> directory and are thus accessible from other modules (the value of <strong>MOZ_OBJDIR</strong> is defined in <code>/mozilla/.mozconfig</code>). XPIDLSRCS are run through the IDL processor, and the generated C++ headers are copied into the same include directory. In addition, an XPT (type library) file is generated and placed in the <code>components/</code> subdirectory of your extension.</p>
<h4 id="Source_Files.28.E6.BA.90.E6.96.87.E4.BB.B6.29" name="Source_Files.28.E6.BA.90.E6.96.87.E4.BB.B6.29">Source Files(源文件)</h4>
<p>Now it’s time to create the makefile and source files in the <code>src/</code> subdirectory. If you're implementing interfaces that you've described using IDL, the easiest way to do this is to leave the <code>src/</code> directory empty and run <code>make</code> on the <code>public/</code> directory only; this will be explained shortly.</p>
<p>Then open the generated header file for your interface from <code>/mozilla/$(MOZ_OBJDIR)/dist/include/myextension/</code>. It contains stubs for the component .H and .CPP files that you can copy and paste into your implementation files. All you have to do is fill in the implementation stubs in the C++ file and you’re good to go.</p>
<p>Here’s an example of the makefile you need to place into your <code>src</code> directory:</p>
<pre class="eval">DEPTH		= ../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@

include $(DEPTH)/config/autoconf.mk

IS_COMPONENT = 1
<a href="/cn/MODULE" title="cn/MODULE">MODULE</a> = myextension
<a href="/cn/LIBRARY_NAME" title="cn/LIBRARY_NAME">LIBRARY_NAME</a> =  myExtension
<a href="/cn/USE_STATIC_LIBS" title="cn/USE_STATIC_LIBS">USE_STATIC_LIBS</a> = 1

XPI_NAME = myextension

REQUIRES	= xpcom \
		  string \
		  $(NULL)

<a href="/cn/CPPSRCS" title="cn/CPPSRCS">CPPSRCS</a>		= \
		  myFirstComponent.cpp \
		  mySecondComponent.cpp \
		  myExtension.cpp \
		  $(NULL)

include $(topsrcdir)/config/rules.mk

<a href="/cn/EXTRA_DSO_LDOPTS" title="cn/EXTRA_DSO_LDOPTS">EXTRA_DSO_LDOPTS</a> += \
  $(XPCOM_GLUE_LDOPTS) \
  $(NSPR_LIBS) \
  $(NULL)

# <span class="highlightred">NOTE: If you are coding against the 1.8.0 branch (not 1.8 branch or trunk), the</span>
# <span class="highlightred">above line won't work, due to linker flag issues.</span> Use the following 
# variables instead:
#
# EXTRA_DSO_LDOPTS += \
#   $(MOZ_COMPONENT_LIBS) \
#   $(NULL)
#
# Unfortunately, using MOZ_COMPONENT_LIBS links against xpcom_core, which means
# your components will not work in future versions of Firefox.
</pre>
<p>The <code>REQUIRES</code> section tells <code>make</code> which modules your components uses. This causes the relevant subdirectories of <code>/mozilla/$(MOZ_OBJDIR)/dist/include/</code> to be added to the C++ compiler's include path. If you’re including Mozilla headers and the compiler isn’t finding them, it could well mean that you haven’t listed all of the necessary modules here. <code>CPPSRCS</code> lists the source files that need to be built.</p>
<p>In this example, the first two files contain the implementation of the extension’s two components. The final file, <code>myExtension.cpp</code>, contains the code necessary to register these components, as described in the next section.</p>
<h4 id="Registering_Your_Components.28.E6.B3.A8.E5.86.8C.E6.8E.A7.E4.BB.B6.29" name="Registering_Your_Components.28.E6.B3.A8.E5.86.8C.E6.8E.A7.E4.BB.B6.29">Registering Your Components(注册控件)</h4>
<p>In order to use your components from other C++ modules and JavaScript, you first have to register them. To do this, your extension needs to implement a class that exposes the <code><a href="/cn/NsIModule" title="cn/NsIModule">nsIModule</a></code> interface, which has methods for accessing the components defined in a module. Luckily, this can be accomplished through the use of a few simple macros, so you don’t have to concern yourself with the messy details of what’s happening under the hood.</p>
<p>The first step is to define a CID, contract ID and class name for each of your components. Place the following code (adapting the <strong>#defines</strong> accordingly) into the header of each component that you want to be able to instantiate using the component manager:</p>
<pre>// {00000000-0000-0000-0000-000000000000}
#define MYFIRSTCOMPONENT_CID \
	{ 0x00000000, 0x0000, 0x0000, \
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
	
#define MYFIRSTCOMPONENT_CONTRACTID	"@mycompany.com/myfirst;1"
#define MYFIRSTCOMPONENT_CLASSNAME	"My First Component"
</pre>
<p>Obviously you need to fill in the CID with a real GUID. Under Windows, this can be generated using <a class="external" href="http://www.microsoft.com/downloads/details.aspx?familyid=94551F58-484F-4A8C-BB39-ADB270833AFC">guidgen.exe</a>. Unix people can use uuidgen (comes standard with most unixes and Linux distros).</p>
<p>Now create the <code>myExtension.cpp</code> file like so:</p>
<p>现在,建立一个像下面那样的myExtension.cpp文件:</p>
<pre>#include "nsXPCOM.h"
	
#include "nsIGenericFactory.h"
	
/**
 * Components to be registered
 */
#include "myFirstComponent.h"
#include "mySecondComponent.h"
	
NS_GENERIC_FACTORY_CONSTRUCTOR(myFirstComponent)
NS_GENERIC_FACTORY_CONSTRUCTOR(mySecondComponent)
	
//----------------------------------------------------------
	
static const nsModuleComponentInfo components[] =
{
	{
		MYFIRSTCOMPONENT_CLASSNAME,
		MYFIRSTCOMPONENT_CID,
		MYFIRSTCOMPONENT_CONTRACTID,
		myFirstComponentConstructor
	},
	{
		MYSECONDCOMPONENT_CLASSNAME,
		MYSECONDCOMPONENT_CID,
		MYSECONDCOMPONENT_CONTRACTID,
		mySecondComponentConstructor
	},
};
	
NS_IMPL_NSGETMODULE(MyExtension, components)
</pre>
<p>The <strong>NS_IMPL_NSGETMODULE</strong> macro creates the appropriate module object providing access to all of the components listed in the <code><a href="/cn/NsModuleComponentInfo" title="cn/NsModuleComponentInfo">nsModuleComponentInfo</a></code> array.</p>
<h4 id="Building_It" name="Building_It">Building It(建立它)</h4>
<p>As mentioned above, you’ll probably want to build your extension immediately after creating your IDL files in order to generate the C++ stubs for your component implementations. I’m assuming that you’ve already built Firefox successfully. If not, return immediately to the beginning of this article and don’t come back til you have a functioning <code>firefox.exe</code>. Do not pass go. Do not collect $200.</p>
<p>Still here? Okay, now we have to modify your <code>.mozconfig</code> (in the <code>/mozilla/</code> root directory) so that your extension is built along with Mozilla. Add the following line at the end of the file:</p>
<pre>ac_add_options --enable-extensions=default,myextension
</pre>
<p>Now launch <code>make</code> from the Mozilla root:</p>
<pre>make -f client.mk build
</pre>
<p>Even if you have an up-to-date Firefox build, you’ll have to wait a while for <code>make</code> to recurse over the entire Mozilla source tree looking for new stuff (on my machine, which is pretty fast, this takes a good 10-15 minutes). Eventually it will reach your extension and generate a bunch of stuff under <code>/mozilla/$(MOZ_OBJDIR)/</code>:</p>
<ul> <li>Exported header files and generated header files (from IDL) in <code>dist/include/myextension/</code></li> <li>Static libraries for your modules in <code>dist/lib/</code> (in case other modules want to link statically to your stuff instead of using XPCOM).</li> <li>XPI file in <code>dist/xpi-stage/myextension.xpi</code>.</li> <li>Generated makefiles for your projects in <code>extensions/myextension/</code> (remember, we’re under <code>/mozilla/$(MOZ_OBJDIR)/</code>.</li> <li>Everything else in <code>dist/bin/extensions/<a class=" link-mailto" href="mailto:myextension@mycompany.com" rel="freelink">myextension@mycompany.com</a>/</code>.</li>
</ul>
<p>A lot of this stuff won’t get created on this first pass since <code>make</code> will gag when it doesn’t find the source files for your components. Don’t worry about this; all you need are the generated header files that contain the C++ implementation stubs. Go back and flesh out the C++ implementation of your components so that the build can complete next time. Remember that you should never, ever modify any of these generated files. Always modify the files used to generate them and rerun <code>make</code>. There might be exceptions to this rule, but if you’re changing the generated files directly, you’re probably doing something wrong.</p>
<p>The process of walking the entire Mozilla tree takes a long time. If you already have a Mozilla build, you can avoid this by creating a makefile for your extension directly. Go to the root of your $(MOZ_OBJDIR) and (from a bash-compatible shell) enter:</p>
<pre class="eval">../build/autoconf/make-makefile extensions/myextension
</pre>
<p>If your $(MOZ_OBJDIR) is located outside your $(TOPSRCDIR), you'll need to do:</p>
<pre class="eval">$(TOPSRCDIR)/build/autoconf/make-makefile -t $(TOPSRCDIR) extensions/myextension
</pre>
<p>in order for the script to know where your source is (it'll use the extension path you gave it relative to the current dir to figure out where you want your makefiles to go).</p>
<p>This will generate the proper makefile for your extension. Whether you build the whole Mozilla tree or take this shortcut, you can build from now on by going to <code>/mozilla/$(MOZ_OBJDIR)/extensions/myextension/</code> and typing "make" on the command line. It should build your component without bothering with the rest of Mozilla. If everything works out, you’ll see your XPI file in the XPI staging area. You’ll also see the "exploded" version of the XPI (i.e. the unzipped directory structure) underneath <code>/mozilla/$(MOZ_OBJDIR)/dist/bin/extensions</code>. (If something goes wrong, figure out what, fix it and then come back here and add it to this article.)</p>
<p>To make sure that the build really finished, launch Firefox and check that your extension is listed when you select Tools/Extensions. If you are using Firefox as your regular browser (and if you’re not, why not!?), you might be annoyed by the fact that you have to close regular Firefox before running your custom-built version. If so, try setting the <strong>MOZ_NO_REMOTE</strong> environment variable to "1" before running the development version of Firefox. You’ll also need to use a different profile for your development version:</p>
<pre class="eval">firefox -P <em>development</em>
</pre>
<p>Where <em>development</em> is replaced with the name of the extra profile you’ve created. This will let you run both versions of Firefox simultaneously, saving you oodles of time over the course of the build/test cycle.</p>
<h4 id="No_Place_Like_Chrome" name="No_Place_Like_Chrome">No Place Like Chrome</h4>
<p>Yippee-ki-yay! Now you have an extension that does, well, absolutely nothing. It’s time to do something with those groovy components that you’ve implemented and registered. The simplest way to do this is to write some <a href="/cn/JavaScript" title="cn/JavaScript">JavaScript</a> and <a href="/cn/XUL" title="cn/XUL">XUL</a> code. At this point, it would be very helpful to have a bit of experience <a href="/cn/Extensions" title="cn/Extensions">writing "regular" extensions</a> (i.e. without using custom C++ components). If you’ve never done this, I strongly recommend that you think of a cool idea for something simple that you’ve always wanted to tweak in Firefox and write it. Just displaying a new menu item that opens a "Hello, World!" dialog box would be already be a great exercise to get warmed up with.</p>
<p>Assuming you know how to write XUL/JavaScript extensions, you’re aware that the most important stuff goes in the <code>chrome/</code> directory of your extension. Well, the fact that you’re also using C++ components doesn’t change that one whit. So now you need to create the normal <code>content/</code>, <code>locale/</code> and <code>skin/</code> directories in which to place your chrome files. Personally I like placing these directly under the root directory of my module, but I don’t suppose it makes any difference if you prefer putting them under a <code>chrome/</code> subdirectory or whatever. Let freedom reign!</p>
<p>Once you’ve written the necessary chrome files (for instance, an overlay that adds a menu item to instantiate and use one of your components), you need to package them up as part of your extension. This is accomplished through the use of a <a href="/cn/JAR_Manifests" title="cn/JAR_Manifests">JAR Manifest</a>. For our simple extension example, this file might look something like this:</p>
<pre>myextension.jar:
%  content myextension %content/
%  locale myextension en-US %locale/en-US/
%  skin myextension classic/1.0 %skin/classic/
%  overlay chrome://browser/content/browser.xul chrome://myextension/content/MyExtensionOverlay.xul
	content/MyExtensionOverlay.js		(content/MyExtensionOverlay.js)
	content/MyExtensionOverlay.xul		(content/MyExtensionOverlay.xul)
	locale/en-US/MyExtension.dtd		(locale/en-US/MyExtension.dtd)
	locale/en-US/MyExtension.properties	(locale/en-US/MyExtension.properties)
	skin/classic/MyExtension.css		(skin/classic/MyExtension.css)
</pre>
<p>Place this code in a file called <code>jar.mn</code> in the root directory of your extension, making sure that the paths in parentheses point to actual files (when interpreted relative to the root directory). You also have to make one small change to the makefile in the same directory, adding the following line:</p>
<pre class="eval">USE_EXTENSION_MANIFEST = 1
</pre>
<p>This tells <code>make</code> to create a single manifest file called <code>chrome.manifest</code> instead of creating separate manifests with goofy names for each package.</p>
<p>Now launch <code>make</code> again, and you should see a <code>chrome</code> subdirectory appear in your extension (<code>/mozilla/$(MOZ_OBJDIR)/dist/bin/extensions/<a class=" link-mailto" href="mailto:myextension@mycompany.com" rel="freelink">myextension@mycompany.com</a>/</code>). Note that the <code>chrome</code> directory contains a JAR (i.e. ZIP) file with all the chrome files listed in <code>jar.mn</code> as well as a complete directory structure mirroring that of the JAR file. The directory structure, however, is empty. Why? I don’t know. Don’t worry about this, the files in the JAR are the ones that are actually used.</p>
<h4 id="Keeping_it_Complex" name="Keeping_it_Complex">Keeping it Complex</h4>
<p>If you’re developing a really complex extension with lots of <a href="/cn/XPCOM" title="cn/XPCOM">XPCOM</a> components, you’ll probably want to divide your code up into smaller modules.</p>
<h5 id="Kinda.2C_Sorta_Complex_Extensions" name="Kinda.2C_Sorta_Complex_Extensions">Kinda, Sorta Complex Extensions</h5>
<p>For a moderately complex extension, it’s probably enough just to subdivide the code into a single level of modules. Let’s assume that you have a <code>base/</code> module that defines a bunch of basic XPCOM components and an <code>advanced/</code> module that defines some chrome as well as other components that use the basic components. Your complete directory structure will look something like this:</p>
<ul> <li>myextension <ul> <li>base <ul> <li>public</li> <li>src</li> </ul> </li> <li>advanced <ul> <li>content</li> <li>locale <ul> <li>en-US</li> <li>...other locales...</li> </ul> </li> <li>public</li> <li>skin <ul> <li>classic</li> <li>...other skins...</li> </ul> </li> <li>src</li> </ul> </li> </ul> </li>
</ul>
<p>Other than that, nothing really changes. The makefiles in the <code>base/</code> and <code>advanced/</code> directories should look more or less like your original root makefile, remembering to change the <strong>DEPTH</strong> variable to account for the fact that they’ve moved a level further away from the Mozilla root. You also need to remove the <strong>DIST_FILES</strong> variable since that’s going to be in the top-level makefile. Every makefile that generates anything should define the <strong>XPI_NAME</strong> variable to make sure generated files go into your extension and not into the global <code>components/</code> directory. In fact, just define this in every makefile to be safe. You can use the same <strong>MODULE</strong> in both <code>base/</code> and <code>advanced/</code> so that all the generated include files go into the same directory, but make sure that you don’t use the same <strong>XPIDL_MODULE</strong> in the two <code>public/</code> directories or one of the component type libraries (i.e. XPT files) will overwrite the other one and all hell will break loose.</p>
<p>Each module must also have a different value for the <strong>LIBRARY_NAME</strong> variable. This is the name of the generated dynamic library, so if we call the libraries "myBase" and "myAdvanced", we’ll end up with <code>myBase.dll</code> and <code>myAdvanced.dll</code> (on Windows, at least). And each of these modules is going to have a separate C++ file for registering components. So there will be two files that look like <code>myExtension.cpp</code> in the original example, say <code>Base.cpp</code> and <code>Advanced.cpp</code>. Finally, each module will obviously have its own <code>jar.mn</code>, though they can reference the same JAR filename and package name if you want all the chrome files to be organized in a single JAR file and package. The only file that really stays put is <code>install.rdf</code>, which still exists once and only once in the extension root directory.</p>
<p>As for the top-level makefile, it will now look like this:</p>
<pre>DEPTH		= ../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
	
DIRS		= base advanced
	
XPI_NAME               = myextension
INSTALL_EXTENSION_ID   = myextension@mycompany.com
XPI_PKGNAME		= myextension
	
DIST_FILES = install.rdf
	
include $(topsrcdir)/config/rules.mk
</pre>
<h5 id="Seriously_Complex_Extensions" name="Seriously_Complex_Extensions">Seriously Complex Extensions</h5>
<p>At some point, even a single module may grow to the point where you want to divide it further into submodules. The difference between having separate modules and having a single module with separate submodules is that the submodules all share the same file for registering components (the famous <code>myExtension.cpp</code> file), and when compiled they create a single dynamic library. The decision to split a module into submodules is all about code organization; it doesn’t really affect the final product at all.</p>
<p>To split a module into submodules, first create a subdirectory for each submodule. Then create an additional directory called <code>build/</code>. Each submodule will be configured to create a static library, and the <code>build/</code> directory will pull these libraries together to create a single dynamic component library. Confused? Here’s an example, showing just the <code>advanced/</code> subbranch of the <code>myextension/</code> directory:</p>
<ul> <li>advanced <ul> <li>build</li> <li>intricate <ul> <li>public</li> <li>src</li> </ul> </li> <li>multifarious <ul> <li>public</li> <li>src</li> </ul> </li> <li>content</li> <li>locale <ul> <li>en-US</li> <li>...other locales...</li> </ul> </li> <li>skin <ul> <li>classic</li> <li>...other skins...</li> </ul> </li> </ul> </li>
</ul>
<p>As you can see, we’ve split <code>advanced/</code> into two submodules: <code>intricate/</code> and <code>multifarious/</code>, and we’ve added an additional <code>build/</code> subdirectory. We’ve left the chrome directories directly under <code>advanced/</code>, since they aren’t tied to any specific submodule. This means that <code>jar.mn</code> will stay in the same place.</p>
<p>The <code>intricate/</code> and <code>multifarious/</code> makefiles will look a lot like the original <code>advanced/</code> makefile, but we’ll need to tweak them a bit. As always, we have to adjust the <strong>DEPTH</strong> variable since the makefiles are deeper in the directory structure. And we should change the <strong>LIBRARY_NAME</strong> to indicate that we’re generating a static library for each submodule. By convention the "_s" suffix is used for this purpose. So let’s call them "myIntricate_s" and "myMultifarious_s". Finally, we define the variable <strong>FORCE_STATIC_LIB</strong>, resulting in a makefile that starts something like this:</p>
<pre>DEPTH		= ../../../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
MODULE = myextension
LIBRARY_NAME = myIntricate_s
FORCE_STATIC_LIB = 1
USE_STATIC_LIBS = 1
	
XPI_NAME = myextension
	
...more stuff here...
</pre>
<p>The <code>build</code> makefile pulls together the static libraries generated by the submodules and creates a single (dynamic) component library:</p>
<pre>DEPTH		= ../../../..
topsrcdir	= @top_srcdir@
srcdir		= @srcdir@
VPATH		= @srcdir@
	
include $(DEPTH)/config/autoconf.mk
	
IS_COMPONENT = 1
MODULE = myextension
LIBRARY_NAME = myAdvanced
USE_STATIC_LIBS = 1
	
XPI_NAME = myextension
	
DEFINES += XPCOM_GLUE
	
SHARED_LIBRARY_LIBS = \
		$(DIST)/lib/$(LIB_PREFIX)myIntricate_s.$(LIB_SUFFIX) \
		$(DIST)/lib/$(LIB_PREFIX)myMultifarious_s.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)xpcom.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)nspr4.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)plds4.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)plc4.$(LIB_SUFFIX) \
		$(NULL)
	
REQUIRES	= \
		xpcom \
		string \
		$(NULL)
	
CPPSRCS		= \
		Advanced.cpp \
		$(NULL)
	
include $(topsrcdir)/config/rules.mk
	
LOCAL_INCLUDES += \
        -I$(srcdir)/../intricate/src \
        -I$(srcdir)/../multifarious/src \
        $(NULL)
</pre>
<p>The makefile in the <code>advanced/</code> directory should list the <code>intricate/</code>, <code>multifarious/</code> and <code>build/</code> directories in its <strong>DIRS</strong> variable. Make sure that <code>build/</code> comes last since it can’t create the component library until the other makefiles have completed.</p>
<h3 id="Other_Topics.28.E5.85.B6.E5.AE.83.29" name="Other_Topics.28.E5.85.B6.E5.AE.83.29">Other Topics(其它)</h3>
<h4 id="Adding_Data_Files_to_Your_Extensions.28.E6.B7.BB.E5.8A.A0.E6.95.B0.E6.8D.AE.E6.96.87.E4.BB.B6.E5.88.B0.E6.89.A9.E5.B1.95.29" name="Adding_Data_Files_to_Your_Extensions.28.E6.B7.BB.E5.8A.A0.E6.95.B0.E6.8D.AE.E6.96.87.E4.BB.B6.E5.88.B0.E6.89.A9.E5.B1.95.29">Adding Data Files to Your Extensions(添加数据文件到扩展)</h4>
<p>In some cases, you may wish to include additional files in your extension that don’t belong in the <code>chrome/</code> subdirectory. Examples might be database files or XML schemas. This can be achieved by adding a custom step to your makefile that copies the files from the source tree into the extension’s target directory.</p>
<h5 id="Copying_Data_Files_Into_Target_Directory.28.E5.A4.8D.E5.88.B6.E6.95.B0.E6.8D.AE.E6.96.87.E4.BB.B6.E5.88.B0.E7.9B.AE.E6.A0.87.E6.96.87.E4.BB.B6.E5.A4.B9.29" name="Copying_Data_Files_Into_Target_Directory.28.E5.A4.8D.E5.88.B6.E6.95.B0.E6.8D.AE.E6.96.87.E4.BB.B6.E5.88.B0.E7.9B.AE.E6.A0.87.E6.96.87.E4.BB.B6.E5.A4.B9.29">Copying Data Files Into Target Directory(复制数据文件到目标文件夹)</h5>
<p>Let’s say that you have some data files containing statistical information that you want to include in your extension and make available to your components. You’ve placed these files, which have the extension .TXT, into a <code>stats/</code> subdirectory under your extension directory in the source tree. The following makefile rule can be used to copy these files into the final target directory of the extension:</p>
<pre>libs::
	if test ! -d $(FINAL_TARGET)/stats; then \
		$(NSINSTALL) -D $(FINAL_TARGET)/stats; \
	fi
	$(INSTALL) $(srcdir)/*.txt $(FINAL_TARGET)/stats
</pre>
<h5 id="Accessing_Data_Files_From_Components.28.E6.8E.A7.E4.BB.B6.E8.AE.BF.E9.97.AE.E6.95.B0.E6.8D.AE.E6.96.87.E4.BB.B6.29" name="Accessing_Data_Files_From_Components.28.E6.8E.A7.E4.BB.B6.E8.AE.BF.E9.97.AE.E6.95.B0.E6.8D.AE.E6.96.87.E4.BB.B6.29">Accessing Data Files From Components(控件访问数据文件)</h5>
<p>The trick to accessing your data files is to figure out where the home directory of your extension is. Rumor has it that at some future date, this will possible through the <code><a href="/cn/NsIExtensionManager" title="cn/NsIExtensionManager">nsIExtensionManager</a></code> interface or something similar. In the meantime, there is a simple and reliable hack that can be used to achieve this. In the implementation of any JavaScript XPCOM component, there is a special <strong>__LOCATION__</strong> (two leading and two trailing underscores) symbol that points to the component’s implementation file. So you can write a simple component which deduces the root directory of your extensions by extrapolating from its location.</p>
<p><a class="external" href="http://www.builderau.com.au/program/soa/Creating_XPCOM_components_with_JavaScript/0,39024614,39206503,00.htm">This article</a> explains how to create an XPCOM component in JavaScript. You’ll need an IDL file for an interface that looks something like this:</p>
<pre>interface myILocation : nsISupports
{
    readonly attribute nsIFile locationFile;
};
</pre>
<p>Place the IDL file in the <code>public/</code> directory of your project or subproject. In the <code>src/</code> directory, place the JavaScript file that implements the component. The component implementation will include the methods for retrieving the path or file for the extension’s home directory:</p>
<pre>myLocation.prototype =
{
  QueryInterface: function(iid)
  {
    if (iid.equals(nsISupports))
      return this;
    if (iid.equals(myILocation))
      return this;
	
    Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
    return null;
  },
	
  get locationFile()
  {
     return __LOCATION__.parent.parent;
  }
}
</pre>
<p>This assumes that the component resides in a subdirectory of the extension directory (by convention, this directory is called <code>components/</code>). The <code>parent</code> property of <strong>__LOCATION__</strong> returns the <code>components/</code>, and the <code>parent</code> of this is the extension directory.</p>
<p>The last step is to modify the makefile of the source directory where you placed your JavaScript file so that it is copied into the appropriate location in the extension:</p>
<pre>libs::
	$(INSTALL) $(srcdir)/*.js $(FINAL_TARGET)/components
</pre>
<p>Now you can instantiate an instance of this component and use the <code>locationFile</code> property to get an <code><a href="/cn/NsIFile" title="cn/NsIFile">nsIFile</a></code> interface that points to your extension’s home directory.</p>
<h4 id="Using_Third-Party_Libraries" name="Using_Third-Party_Libraries">Using Third-Party Libraries</h4>
<p>For more sophisticated extensions, you may want to integrate third-party libraries that provide specialized functionality for database connectivity, image processing, networking and the like. If you want your extension to run on all Firefox platforms, you will need to have the source code for the library in question, so I assume that this is available.</p>
<p>The most convenient approach from the perspective of the development cycle is to create a Mozilla-style makefile for the library. This works well for libraries that have a straightforward make process without extensive configuration. A good example of this is the SQLite library included in the Mozilla build tree at <code>db/sqlite</code>. By adapting the makefile in this way, the library is created as part of the standard Mozilla build process, which eliminates additional build steps. The downside is that you will need to update the modified makefile any time a new version of the library is released.</p>
<p>For libraries that have complex configuration processes, use a non-standard compiler or have other special characteristics, it may be unfeasible to create a Mozilla-compliant makefile. In this case, I would recommend placing the entire library distribution inside the project or subproject that uses it. So if library <code>acmelib</code> is used inside the <code>multifarious/</code> subproject in the above example, it would be placed as a subdirectory underneath that subproject (at the same level as <code>public/</code> and <code>src/</code>).</p>
<p>Of course, this means that you will have to build <code>acmelib</code> manually on all platforms before launching the Mozilla build. But at least you can then refer to include files and import libraries from your component using relative paths.</p>
<h4 id="Building_for_Multiple_Platforms" name="Building_for_Multiple_Platforms">Building for Multiple Platforms(跨平台制作)</h4>
<p>TODO</p>
<p>  </p>
<div class="originaldocinfo">
<h2 id=".E5.8E.9F.E5.A7.8B.E6.96.87.E6.A1.A3.E4.BF.A1.E6.81.AF" name=".E5.8E.9F.E5.A7.8B.E6.96.87.E6.A1.A3.E4.BF.A1.E6.81.AF">原始文档信息</h2>
<ul> <li>作者: Matthew Gertner - July 26, 2005.</li> <li>Permission granted to migrate in Jan 2006, including permission to relicense under the CC:By-SA.</li> <li>Original Source: <a class=" external" href="http://www.allpeers.com/blog/creating-complex-firefox-extensions/" rel="freelink">http://www.allpeers.com/blog/creatin...ox-extensions/</a></li>
</ul>
</div>
<p>{{ languages( { "es": "es/Crear_una_extensi\u00f3n_personalizada_de_Firefox_con_el_Mozilla_Build_System", "fr": "fr/Cr\u00e9ation_d\'extensions_pour_Firefox_avec_le_syst\u00e8me_de_compilation_de_Mozilla", "it": "it/Creare_Estensioni_personalizzate_per_Firefox_con_il_sistema_di_sviluppo_di_Mozilla", "ja": "ja/Creating_Custom_Firefox_Extensions_with_the_Mozilla_Build_System" } ) }}</p>
恢复到这个版本