Mozilla ビルドシステムによるカスタム Firefox 拡張機能の作成

この翻訳は不完全です。英語から この記事を翻訳 してください。

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

From Firefox 53 onwards, no new legacy add-ons will be accepted on addons.mozilla.org (AMO).

From Firefox 57 onwards, WebExtensions will be the only supported extension type, and Firefox will not load other types.

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

Firefox 用の拡張機能を作成するための情報は豊富にあります。しかし現時点でこれら全てのドキュメントは、あなたが XUL または JavaScript のみを用いて拡張機能を作成することを想定しています。複雑な拡張機能を作成するためには、これらを実現するための C++ で作成されたコンポーネントを利用する必要が出てくることでしょう。あなたの作成する拡張機能に C++ によるコンポーネントを含めなければならないケースは次のとおりです。

  • JavaScript のコードよりも高いパフォーマンスが要求される場合。
  • C または C++ で書かれたサードパーティ製のライブラリを利用する場合。
  • XPCOM (例: NSPR) によって公開されていない Mozilla のインタフェースを利用する場合。
Note: Gecko の JIT による最新の JavaScript エンジンと js-ctypes により、より多くの拡張機能は JavaScript のみで記載できます。ネイティブな C++ コードをあなたの拡張機能で利用することを決断する前に、他の選択肢を十分検討してください。バイナリのコンポーネントの場合 Firefox のメジャーリリースの度に再コンパイルが必要となり面倒です。

この記事では上記に記載したような条件の下、大規模で複雑な Firefox 拡張を作成するための開発環境をセットアップする方法について述べます。なお、 Mozilla 用の C++ コンポーネントを作成するために Mozilla をビルドする必要は無く、 Mozilla ビルドシステムを利用すればよいことを強調しておきます。あなたが単に XPCOM コンポーネント等の作成方法について調べているのであれば、本記事の内容はおそらく過剰であり、それよりもこちらのガイドを見るべきでしょう。言い換えると、あなたが個人またはチームでの開発経験者であり、かつ大規模で複雑な拡張機能を作成しようとしているのであれば、本記事に書かれている手法を検討することで、うまく作業を進めることができるでしょう。

メモ: ここでは Firefox でのみこれらの手法の動作確認をしましたが、これらは追加の変更を加えずとも、おそらく大体の Gecko ベースのプラットフォーム (Thunderbird や Seamonkey など) で動作するでしょう。

バンビ、モジラに会う

臆病者の話ではありません。とりわけ最初のステップは、非常に大規模―そう、途方もなく―である Mozilla プロジェクトのビルドの話へと巻き込みます。多くの賢い開発者は、一度はそのビルドに挑戦し、精神異常の瀬戸際を経験してきました。もしあなたが C++ 開発者を経験していないなら、あなたを悩ませたくありませんので、 JavaScript を利用することをおすすめします。

Windows 環境

私はこのガイドを見て初めて Mozilla のビルドを実施しました。なぜかは思い出せませんが、幾度となくスタックしてしまい、これらの一連の事件は最初に思っていたよりも実に長い時間を掛けて終えました。多くの家具が破壊され、髪は殆ど根っこから抜け落ちました。あなたがお探しの高評価の付いた分かりやすいガイドはこちらにあります。几帳面に各ステップを実施していけば、おそらくうまく行くでしょう。あなたがきちんと動作するビルド環境を一度得られたのであれば、ある時点からはおそらく何の努力もせずにビルドできるようになったのでしょう。多分。

その他のプラットフォーム

Linux や MacOS などの他のプラットフォームでは、手順は幾分簡単です。ビルドに必要な全てのツール群は最初から組み込まれ利用可能な状態になっており、したがって、あなたがやるべきことは端末上でいくつかコマンドを実行するのみです。殆どの OS 向けの完全な手順はこちらから参照できます。

プロジェクトの構築

Mozilla には、そのビルド過程へ統合されている大量の複雑な拡張機能が含まれています。したがって、XPCOM コンポーネントの作成や登録、 JAR ファイルやマニフェストのビルド、 Firefox の extension/ ディレクトリへの多数のファイルのインストールなどの全ての問題を解決する必要があります。すなわち、これらの問題を解決することと拡張機能をビルドするための開発環境は、切っても切れない関係であると言えます。

まず最初に、あなたの拡張機能に付けるキャッチーな名前を考えてください。そして、その名前のディレクトリを /mozilla/extensions/ ディレクトリの下に作成します。名前には小文字のみ利用可能です。ビルドツリーの同じ階層には inspector/, reporter/ などの仲間のディレクトリも確認できるはずです。

ビルドを行う前に、 Mozilla ビルドシステムは Makefile.in と呼ばれるメイクファイルのテンプレートを元にしてビルド時に実際に利用するメイクファイルを生成するコンフィギュレーションと呼ばれる処理を実施します。実際のメイクファイルはテンプレートと類似もしくは一致していることがしばしばありますが、動的に生成されたメイクファイルを利用することで得られる柔軟性が、ビルドシステムをより強力にする要素の一つとなっています。

単純な C++ 拡張機能の分析

ここではあなたが C++ または JavaScript の両方から利用可能な XPCOM コンポーネントを記述するために C++ を利用することを想定します。コンポーネントを作成するプロセスは、 Mozilla ビルドシステムを利用した場合、実際のところ比較的簡単です。

最も単純なケースでは、コンポーネントは public/ と src/ の 2 つのサブディレクトリを持つ、単一のメインディレクトリから成ります。メインディレクトリと各サブディレクトリには必ず Makefile.in を含める必要があります (これ以降は単にこれらのファイルをメイクファイルと呼びますが、本当のところは真のメイクファイルを生成するために利用されるファイルであることを忘れないようにしてください) 。このメイクファイルは 2 つのことを宣言します。 1 つ目は、拡張機能の生成先であるサブディレクトリをリスティングしています。そのためビルドシステムは、追加のメイクファイルの検索場所を知る必要があります。 2 つ目は、ビルドシステムに対して新しい拡張機能を作成する方法 (コンポーネントのディレクトリを Firefox のバイナリのあるディレクトリへコピーする、などではなく) を指示します。拡張機能を利用する主な利点は、全てをパッケージングしてそれを他のマシンへインストールできることです。

それでは、以下に基礎的なごく普通の最上位のメイクファイルを示します (拡張機能のメインディレクトリ内にある Makefile.in) 。

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

このメイクファイルの主な機能を表現するメイク処理の詳細な記述については こちら から確認できます。 MODULE と XPI_NAME の両方は、あなたの拡張機能の名前に設定します。これは、 XPI と同じ場所 (下記参照) へ全てのファイルを配置するために、全てのプロジェクトのメイクファイルで繰り返し記述しなければなりません。 INSTALL_EXTENSION_ID はあなたの拡張機能のユニークな ID です。これは GUID でもよいですが、上に示したような ID の方がかなり、そして見たときに思い出しやすいです。 XPI_PKGNAME は記述する必要がありませんが、もし配布に適した XPI を利用するならば、この値は自動的に XPI と同じ場所に作成されます (/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>

install.rdf ファイルフォーマットの詳細についてはこちらから確認できます。メイクファイルの中で DIST_FILES 変数を指定すると、ファイルを拡張機能のディレクトリと (任意の) XPI ファイルへコピーするよう make へ伝えることができます。

Public Interfaces

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/ 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/. 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

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

include $(topsrcdir)/config/rules.mk

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

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

This article explains how to register XPCOM components in Gecko 2.0 and later.

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/
  • 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. 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)xul.$(LIB_SUFFIX) \
                $(DIST)/lib/$(LIB_PREFIX)nss3.$(LIB_SUFFIX) \
		$(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:

export::
	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:

export::
	$(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

Original Document Information

 

ドキュメントのタグと貢献者

 最終更新者: kdaiki211,