五味渕 大賀 GOMIBUCHI Taiga
http://www.xuldev.org/

これまでの章で,XUL,JavaScript,CSS,XPCOM といった技術について解説しました.本章ではこれらの技術を組み合わせ,実際に拡張機能開発を行います.

開発環境の準備

効率的な開発のための設定変更

拡張機能の開発を始める前に,いくつかのFirefoxの設定を変更します.この作業は拡張機能開発に必須ではありませんが,効率的に開発を行うために強くお勧めします.

設定変更をするには,Firefoxのロケーションバーに「about:config」と入力して設定画面を開き,表1の設定値を探 してダブルクリックします.設定値が表示されていない場合は,右クリックして「新規作成」→「真偽値」を選択し,設定名と値を入力します.

表1:XPConnectを使ってXPCOMの機能を呼び出す例
項目名解説設定値
nglayout.debug.disable_xul_cacheFirefoxには,一度読み込んだXULをキャッシュして二度目以降の表示を高速化する機能がある.このキャッシュ機能を無効にすることで,XULを再読み込みしたときにつねに最新の内容で表示されるようになるtrue
browser.dom.window.dump.enabledデバッグ用のdumpメソッドを使用可能にする.コラム「JavaScriptデバッグ方法」を参照true
javascript.options.showInConsoleJavaScriptのエラーをエラーコンソールへ出力するようにするtrue
javascript.options.strictJavaScriptのエラー出力を厳密にするtrue

DOMインスペクタのインストール

DOMインスペクタとは,HTMLやXULなどのDOMツリー構造,JavaScriptのオブジェクトやCSSのプロパティなどを調べるための拡張機能です.拡張機能の開発に必須ではありませんが,インストールされていると便利です.もしFirefoxの「ツール」メニューに「DOMInspector」が表示されていなければ,Firefoxを再インストールする必要があります.インストール手順の途中で「セットアップの種類」から「カスタムインストール」を選択し,「追加コンポーネント」で「DOMインスペクタ」を選択してください.Firefox 3ではMozilla Add-ons(https://addons.mozilla.org/)からインストールしてください.

開発用プロファイルの作成

もし,ふだん使用しているFirefoxの環境と開発用のFirefoxの環境とを分けたいのであれば,開発用のプロファイルを別途作成しましょう(▽注1).

拡張機能開発の予備知識

拡張機能を開発するにあたって知っておくべきクロムの概念などについて解説します.

クロム

クロムとは何か?

クロムとは,XULアプリケーションのGUI構成部分全体を表す概念です.たとえば,Firefoxのブラウザウィンドウのうち,コンテントエリア内に表示されたWebページを除いた部分がクロムです.また,拡張機能もクロムを持ったXULアプリケーションだと言えます.

クロムを構成する3種類のパッケージ

クロムは,以下の3種類のパッケージから構成されます.

contentパッケージ

XULやJavaScriptといったメインのソースファイルを格納するためのパッケージです.ほとんどの拡張機能はただひとつのcontentパッケージを持っています(▽注2).

localeパッケージ

翻訳が可能な言語データを格納するためのパッケージです.拡張機能のGUIを多言語に対応させるには,言語別にlocaleパッケージを複数持たせることになります.

skinパッケージ

スタイルシートや画像といったGUIの装飾に関するソースファイルを格納するためのパッケージです.ほとんどの拡張機能はたいていひとつのskinパッケージしか持ちませんが,複数のskinパッケージを持たせてテーマ別にGUIの装飾を変化させることも可能です.

Chrome URL

クロムのパッケージをFirefoxへ登録して使用するためには,クロムマニフェストと呼ばれるファイルを使います.パッケージを登録すると,パッケージ内のソースファイルのパスがChrome URLと呼ばれる特殊なURIへとマッピングされます.Chrome URLは次のような構文となります.
chrome://[パッケージ名]/[パッケージの種類]/[ソースファイルの相対パス]

たとえば,Firefoxのブラウザウィンドウを表すChrome URLは,以下のようになります.
chrome://browser/content/browser.xul

これはパッケージ名が「browser」のcontentパッケージ内にある「browser.xul」というソースファイルに対応していることになります.Chrome URLによって表されたソースファイルは,UniversalXPConnect権限を持って動作します.したがって,本特集4章「ローカルファイルでXPConnectを使う」にあるような特権の取得をすることなく,XPCOMの機能を利用することができるようになります(図1).

図1:クロムのパッケージとクロムの登録
[図1]

クロスパッケージオーバーレイ

本特集3章で解説したオーバーレイでは,オーバーレイされる側のXUL文書中にxul-overlay処理命令を記述する必要がありました.これに対し,xul-overlay処理命令が記述されていないXUL文書に対して強引にオーバーレイすることを,とくにクロスパッケージオーバーレイと呼びます(図2).拡張機能がFirefoxのブラウザウィンドウへメニューやボタンを追加するためには,このクロスパッケージオーバーレイが必須になります.クロスパッケージオーバーレイを実行するためには,クロムマニフェストを使います.

図2:クロムのパッケージとクロムの登録
[図2]

まとめ

クロムの概念は,はじめはあまり理解できないと思います.最低限以下の3点だけを念頭におき,次の「拡張機能開発基礎編」で,具体的なイメージを掴んでください.

  1. 拡張機能のGUI,すなわちクロムは,content,locale,skinの3種類のパッケージから構成される
  2. Firefoxのブラウザウィンドウへメニュー項目やボタンなどを追加するためには,browser.xulへクロスパッケージオーバーレイする
  3. クロムマニフェストには,クロムのパッケージの登録と,クロスパッケージオーバーレイの実行という2つの重要な役割がある

拡張機能開発基礎編 ~ Hello world 拡張機能の作成~

「拡張機能開発基礎編」では,時計を表示するだけの非常に単純 なHello world拡張機能を開発します(▽注3).

フェーズ①̶テストインストール

 まずはFirefoxの「ツール」メニューへ新たなメニュー項目を追加するだけの最小構成でテストインストールすることを目標に,開発をスタートします(図3).

図3:フェーズ①完了後のイメージ
[図3]

ソースファイルの配置

まず,拡張機能のソースファイルを配置するための作業用フォルダを作ってください.フォルダの場所はどこでもかまいませんが,ここでは「C:¥extensions¥helloworld」を作業用フォルダとします.作業用フォルダ内には図4の構成でフォルダやファイルを配置してください.また,フェーズ①で作成する各ファイルの役割を,表2に示します.

図4:フェーズ①のフォルダ構成
[図4]
表2:フェーズ①で作成する各ファイルの役割
ファイル名役割
install.rdfインストールマニフェストと呼ぶ.拡張機能に関する基本的な情報が記述されており,拡張機能をFirefoxへインストールするために必須
chrome.manifest「拡張機能開発の予備知識」で説明したクロムマニフェスト.パッケージの登録およびクロスパッケージオーバーレイを行う
overlay.xulFirefoxのブラウザウィンドウへオーバーレイし,メニュー項目やボタンを追加するためのXUL
clock.xul
clock.js
時計を表示するウィンドウのXULおよび動作制御のためのJavaScript(これらのファイルは フェーズ②で作成する)

インストールマニフェストの作成

install.rdfへ,リスト1 のように記述します(▽注4).

クロムマニフェストの作成

chrome.manifestへリスト2のように記述します.

content パッケージの登録(content 命令)

「content」で始まる1行目は,クロムのcontentパッケージを登録するための記述です.「helloworld」はパッケージ名を表し,「content/」はソースファイルを格納するフォルダへの相対パスを表します.contentパッケージを登録することで,contentフォルダ内のoverlay.xulは「chrome://helloworld/content/overlay.xul」というChrome URLでアクセスできるようになります.

XUL のクロスパッケージオーバーレイ(overlay 命令)

「overlay」で始まる2行目は,overlay.xulをFirefoxのブラウザウィンドウである「chrome://browser/content/browser.xul」へクロスパッケージオーバーレイさせるための記述です.

オーバーレイのマージポイントを探し出す

次に,オーバーレイする際に2つのXULを結合させるマージポイントを探します.探し方はいろいろありますが,ここではDOMインスペクタを使った手順を紹介します(図5).

図5:DOMインスペクタでオーバーレイのマージポイントを探す
[図5]
  1. 「ツール」→「DOM Inspector」からDOMインスペクタを起 動する.
  2. ウィンドウ上部のURL入力欄へ「chrome://browser/content/browser.xul」と入力し,「Inspect」ボタンをクリックする. するとウィンドウ下部にブラウザウィンドウが表示される.
  3. 図5の①②を順番にクリックすると,③のmenu要素が選択された状態となる.
  4. ③を展開すると④があり, その中にいくつかのmenuitem要素が並んでいる様子がわかる. したがって, 新しいメニュー項目を追加するマージポイントは④のmenupopup要素であり, そのidは「menu_ToolsPopup」だとわかる.
  5. menupopup要素内の各menuitem要素を調べ, どの位置へ要素を追加するかを決める. 今回は図5の⑤の位置へ追加することにする.
リスト1: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>[email protected]</em:id> <!-- 拡張機能を一意に識別するためのID.
                「[email protected]」のようなメールアドレス形式か,GUID形式のいずれか -->
        <em:type>2</em:type> <!-- このアドオンが拡張機能であることを表す -->
        <em:name>Hello, World!</em:name>
            <!-- アドオンマネージャに表示される拡張機能の名前 -->
        <em:version>0.1</em:version>
            <!-- 拡張機能のバージョン.バージョンの付け方には規則がある -->
        <em:description>My first extension.</em:description>
            <!-- アドオンマネージャに表示される拡張機能の簡単な説明 -->
        <em:creator>Gomita</em:creator> <!-- 拡張機能のメイン開発者名.自分の名前に変更 -->
        <em:homepageURL>http://www.xuldev.org/helloworld/</em:homepageURL>
            <!-- 拡張機能を配布したりするためのWebページアドレス -->
        <em:targetApplication> <!-- 拡張機能が対応するアプリケーション. -->
            <Description>    <!-- 今回はFirefox 2へ対応させる -->
                <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
                <em:minVersion>2.0</em:minVersion>
                <em:maxVersion>2.0.0.*</em:maxVersion>
            </Description>
        </em:targetApplication>
    </Description>
</RDF>
リスト2:chrome.manifestへの記述
content  helloworld  content/
overlay  chrome://browser/content/browser.xul  chrome://helloworld/content/overlay.xul

ブラウザウィンドウへのオーバーレイ

オーバーレイのマージポイントがわかったので,次にオーバーレイさせるoverlay.xulを作成します(リスト3).4行目のmenupopup要素がオーバーレイのマージポイント,5~7行目のmenuitem要素が新たに追加するメニュー項目です.insertbefore属性によって要素の追加位置を指定しています.

リスト3:overlay.xulへの追加
1: <?xml version="1.0"?>
2: <overlay id="helloworldOverlay"
3:         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
4:     <menupopup id="menu_ToolsPopup">
5:         <menuitem id="helloworldMenuitem"
6:             label="Hello, World!"
7:             insertbefore="sanitizeSeparator" />
8:     </menupopup>
9: </overlay>

テストインストールして動作確認

ここまでできたら,いよいよHello world拡張機能をFirefoxへインストールします.ただし,インストールといっても,通常の拡張機能のようにXPI形式のインストーラによるインストールは手間がかかるため行いません.開発中はソースファイルのままでのテストインストールを行います.

ポインタファイルの配置

まず,現在使用しているプロファイルのプロファイルフォルダ(▽注5)内の「extensions」というフォルダへ,インストールマニフェストへ記述したHelloworld拡張機能のID「[email protected]」をファイル名として,新規ファイルを作成します.次に,そのファイルに拡張機能のソースファイルを配置した作業用フォルダのフルパス「C:¥extensions¥helloworld」を記述します.

再起動

Firefoxを再起動すると,インストールマニフェストやクロムマニフェストが読み込まれ,インストールが実行されます.Firefoxメニューバーの「ツール」→「アドオン」からアドオンマネージャを開き,「Hello, World!」が追加されていれば,無事インストール成功です.「Hello, World!」メニュー項目が追加されていることも確認してください.

フェーズ②̶時計表示機能を追加

フェーズ②では,フェーズ①で作成した「Hello, World!」メニュー項目をクリックしたときに,ウィンドウを開いて時計を表示するようにします(図6).

図6:フェーズ②の完成イメージ
[図6]

イベントハンドラを追加

まずは,メニュー項目へウィンドウを開くためのイベントハンドラを追加します(リスト4).

リスト4:overlay.xulへの記述
oncommand="window.openDialog('chrome://helloworld/content/clock.xul','Clock','chrome,centerscreen,modal');"

時計ウィンドウ

時計を表示するためのウィンドウ(リスト5)およびその動作制御(リスト6)を作成します.dialog要素についてはすでに3章で説明しました.今回は「OK」ボタンのみを表示させたいので,buttons属性は「accept」にします.ウィンドウ内にはlabel要素とtextbox要素をhbox要素で囲んで横に並べて配置します.

リスト5:clock.xulへの記述
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/"?>
<dialog id="clockDialog"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        title="Clock"
        buttons="accept"
        onload="initClock();">
    <script type="application/x-javascript" src="chrome://helloworld/content/clock.js" />
    <hbox align="center">
        <label value="Current Time:" />
        <textbox id="currentTime" />
    </hbox>
</dialog>
リスト6:clock.jsへの記述
function initClock() {
    showCurrentTime();
    window.setInterval(showCurrentTime, 1000);
}
function showCurrentTime() {
    var textbox = document.getElementById("currentTime");
    textbox.value = new Date().toLocaleTimeString();
    textbox.select();
}

動作確認

ソースファイルへの変更が正しく反映されているかどうか動作確認します.ここで重要なことは,「開発環境の準備」で述べた設定変更によってXULのキャッシュを無効にしていれば,overlay.xulやclock.xulへの変更が反映されているかを確認するために,わざわざFirefoxを再起動したり,拡張機能を再インストールしたりする必要はないということです.overlay.xulであれば,そのオーバーレイ先であるbrowser.xulを再読み込みすれば変更が反映されるので,新しいブラウザウィンドウを開いて動作確認をすることができます.clock.xulやclock.jsは時計ウィンドウを開くたびに読み込まれますので,時計ウィンドウをもう一度開き直すことで動作確認できます.開発中はこのようにソースファイル修正から動作確認までの最短手順を見極めることが重要です.ソースファイル修正から動作確認までの一般的な手順をコラム「修正したソースファイルの動作を確認するには」にまとめました.

うまくいかないときは?

もし,拡張機能が意図したどおりに動作しなかった場合,まずはエラーコンソールの「エラー」パネルへ何か疑いのあるエラーが出力されていないかを確認すると良いでしょう.「開発環境の準備」で述べた設定変更が行われていれば,XULやJavaScriptのエラーが出力されます.また,JavaScriptをデバッグする際に役立つ方法をコラム「JavaScriptデバッグ方法」にまとめましたので,参考にしてください(▽注7).もしFirefoxが無反応な状態に陥ったら,タスクマネージャなどでFirefoxのプロセスを終了させてから再起動してください.

フェーズ③̶多言語対応化

フェーズ②で作成した時計ウィンドウは,画面に表示する文言はすべて英語でした.フェーズ③では,日本語環境のFirefox のユーザが使うときは「現在時刻:」,英語環境のFirefoxユーザが使うときは「Current Time:」といったように,多言語に対応させることを目標とします(図7).

図7:フェーズ③の完成イメージ
[図7]

フォルダ構成

多言語に対応させるには,クロムへlocaleパッケージを追加します.まず,図8に示すように,helloworldフォルダへ新たにlocaleフォルダなどを追加してください.また,新たに作成する各ファイルの役割を表3に示します.

図8:localeパッケージ追加後のフォルダ構成
[図8] □の中 localeパッケージ
表3:フェーズ③で作成する各ファイルの役割
ファイル名役割
locale¥en-US¥clock.dtdclock.xul用の実体参照を定義したDTD(英語用)
locale¥ja-JP¥clock.dtdclock.xul用の実体参照を定義したDTD(日本語用)

localeパッケージの追加

locale パッケージの登録(locale 命令)

「locale」で始まる各行は,クロムのlocaleパッケージを登録するための記述です.「helloworld」はパッケージ名を表し,「locale/en-US/」「locale/ja-JP」はソースファイルを格納するフォルダへの相対パスを表します.また,「en-US」「ja-JP」は各localeパッケージが英語用,日本語用であることを表します.

リスト7:chrome.manifestへの追加
locale helloworld  en-US  locale/en-US/
locale helloworld  ja-JP  locale/ja-JP/

XUL内の文字列を実体参照へ置き換え

多言語対応とするために,clock.xul中にハードコーディングされた文字列をDTDファイルへと分離させます.DTDファイルはlocaleパッケージ内に配置することで,ユーザの言語設定に合わせて適切なファイルが選択されるようになります.clock.xulをリスト8のように修正してください.DTDファイルを参照するためのDOCTYPE宣言の追加と,「Clock」「Current Time」の実体参照への置き換えをします.

リスト8:clock.xulへの修正
1:     <?xml version="1.0"?>
2:     <?xml-stylesheet href="chrome://global/skin/"?>
3:     <!DOCTYPE dialog SYSTEM "chrome://helloworld/locale/clock.dtd">
4:     <dialog id="clockDialog"
5:           xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
6:           title="&helloworld.clock;"
7:           buttons="accept"
8:           onload="initClock();">
9:         <script type="application/x-javascript" src="chrome://helloworld/content/clock.js" />
10:        <hbox align="center">
11:            <label value="&helloworld.currentTime;:" />
12:            <textbox id="currentTimeTextbox" />
13:        </hbox>
14:    </dialog>

実体参照を定義するDTDを作成

clock.xulから参照されるDTDファイルを作成します(リスト9).なお,ja-JPフォルダ内のclock.dtdは,必ず文字コードをUTF-8としてください.

リスト9:clock.dtdへの記述
●locale¥en-US¥clock.dtd:
<!ENTITY helloworld.clock "Clock">
<!ENTITY helloworld.currentTime "Current Time">

●locale¥ja-JP¥clock.dtd:
<!ENTITY helloworld.clock "時計">
<!ENTITY helloworld.currentTime "現在時刻">

動作確認

ここまでできたら動作確認します.今回はクロムマニフェストを修正したので,コラム「修正したソースファイルの動作を確認するには」で述べたとおり,一度Firefoxを再起動する必要があります.Firefoxを再起動し,時計ウィンドウが日本語で表示されることを確認してください.

さらに, 英語でも正しく表示されることを確認します.そのためには「about:config」の設定ウィンドウを開き,設定値「general.useragent.locale」の値を「ja」から「en」に変更したうえで再度時計ウィンドウを開いてみてください.

JavaScript内の文字列を多言語対応化

今回のHello world拡張機能では使用していませんが,もしJavaScriptのコード内に表示文言などの文字列が含まれる場合,それらはJava言語で使われるようなpropertiesファイルへと分離させることで多言語対応させることができます.propertiesファイル内のローカライズされた文字列は,XUL内に配置したstringbundle要素か,またはnsIStringBundleServiceインタフェースを利用して取り出すことができます.

フェーズ④̶ツールバーボタンの追加

フェーズ④では,画像やスタイルシートを使い,時計ウィンドウを開くためのツールバーボタンを追加することを目標とします(図9).

図9:フェーズ④の完成イメージ
[図9]

フォルダ構成

スタイルシートや画像は,通常はクロムのskinパッケージ内に配置します.もちろんcontentパッケージ内に配置しても問題なく動作しますが,skinパッケージ内に配置することで,いろいろなFirefoxテーマに合わせたGUIの装飾をしたり,テーマの開発者が拡張機能専用のアイコンなどを作成しやすくする利点が生まれます.

まず,図10に示すように,helloworldフォルダへ新たにskinフォルダなどを追加します.また,新たに作成する各ファイルの役割を表4に示します.なお,icon.png,icon-small.pngは(▽注7)のURLからダウンロードして配置してください.

図10:skinパッケージ追加後のフォルダ構成
[図10]
表4:フェーズ④で作成する各ファイルの役割
ファイル名役割
icon.png通常サイズのツールバーボタンのアイコン画像
icon-small.png小さいサイズのツールバーボタンのアイコン画像.
overlay.cssツールバーボタンへのアイコン画像割り当てを記述したスタイルシート. ブラウザウィンドウおよび「ツールバーのカスタマイズ」ウィンドウの両方へオーバーレイする.

skinパッケージの追加

chrome.manifestへリスト10の内容を追加してください.

リスト10:chrome.manifestへの追加
1. skin  helloworld   classic/1.0          skin/classic/
2. style chrome://browser/content/browser.xul     chrome://helloworld/skin/overlay.css
3. style chrome://global/content/customizeToolbar.xul chrome://helloworld/skin/overlay.css
skin パッケージの登録(skin 命令)

「skin」で始まる1行目は,クロムの内のskinパッケージを登録するための記述です.「helloworld」はパッケージ名を表し,「skin/classic/」はソースファイルを格納するフォルダへの相対パスを表します.また,「classic/1.0」はこのskinパッケージがFirefoxの標準テーマ用であることを表します.

スタイルシートのクロスパッケージオーバーレイ(style 命令)

フェーズ①で,XULをクロスパッケージオーバーレイさせる際にoverlay命令を使いましたが,スタイルシートをクロスパッケージオーバーレイさせる際にはstyle命令を使います.2~3行目は,overlay.cssをブラウザウィンドウと「ツールバーのカスタマイズ」ウィンドウの両方へオーバーレイさせています(▽注8).

ツールバーボタンの追加

ブラウザウィンドウへ新たにツールバーボタンを追加するために,overlay.xulをリスト11のように修正してください.ツールバーボタンをユーザが好きな場所へ配置できるようにするためには,idが「BrowserToolbarPalette」のtoolbarpaletteという特殊な要素をマージポイントとして新たなtoolbarbutton要素を追加します.また,今回新たにcommand要素を追加し,ツールバーボタンとメニュー項目をクリックしたときの処理を共通化させています.

リスト11:overlay.xulへの修正
<?xml version="1.0"?>
<overlay id="helloworldOverlay"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
    <commandset id="mainCommandSet">
        <command id="helloworldCommand"
                oncommand="window.openDialog(
                'chrome://helloworld/content/clock.xul',
                'Clock','chrome,centerscreen,modal');" />
    </commandset>
    <toolbarpalette id="BrowserToolbarPalette">
        <toolbarbutton id="helloworldButton"
                label="Hello, World!"
                class="toolbarbutton-1"
                command="helloworldCommand" />
    </toolbarpalette>
    <menupopup id="menu_ToolsPopup">
        <menuitem id="helloworldMenuitem"
                label="Hello, World!"
                insertbefore="sanitizeSeparator"
                command="helloworldCommand" />
    </menupopup>
</overlay>

スタイルシートの作成

overlay.cssへリスト12のように記述してください.1~3行目が通常サイズ時のアイコン画像割り当て,4~6行目が「小さいアイコンを使用」したときのアイコン画像割り当てです.

リスト12:overlay.cssへの記述
#helloworldButton {
    list-style-image:
        url(chrome://helloworld/skin/icon.png);
}
toolbar[iconsize="small"] #helloworldButton {
    list-style-image:
        url(chrome://helloworld/skin/icon-small.png);
}

動作確認

今回もフェーズ③同様クロムマニフェストを修正したので,一度Firefoxを再起動する必要があります.Firefoxを再起動し,ツールバー上で右クリックして「ツールバーのカスタマイズ」ウィンドウを開き,新たに追加したツールバーボタンが存在することを確認してください.次に,ツールバーボタンをドラッグ&ドロップしてツールバー上に配置し,クリックして正しく動作することを確認してください.

コラム

修正したソースファイルの動作を確認するには

拡張機能の開発を効率良く行うためには,ソースファイルを修正してから動作確認するまでの手順をできるだけ短縮す ることが重要です.ソースファイルのタイプ別に,修正から動作確認までの手順を整理しました.ただし,「開発環境の 準備」で述べた設定変更によってXULのキャッシュを無効にしていることを前提とします.

新しいウィンドウを開くXUL

ウィンドウを閉じ,もう一度開けば変更が確認できます.サイドバーとして表示するXUL であれば,サイドバーを開き直します.XULから参照されるJavaScript,DTD,スタイルシートなどについても同様です.

他のウィンドウへオーバーレイさせるXUL

オーバーレイ先のXULを再読み込みする必要があります.たとえばブラウザウィンドウへオーバーレイさせるXULであれ ば,新しいブラウザウィンドウを開くことで変更が確認できます.

propertiesファイル

localeパッケージ内のpropertiesファイルへの修正は,Firefoxを再起動するまで反映されません.

componentsフォルダ内のXPCOMコンポーネント

プロファイルフォルダ内の「compreg.dat」「xpti.dat」を削除し,Firefoxを再起動する必要があります.

defaults¥preferencesフォルダ内のデフォルト設定ファイル

Firefoxを再起動する必要があります.

chrome.manifest

Firefoxを再起動する必要があります.

install.rdf

一度拡張機能をアンインストールしてから再インストールする必要があります.つまり,ポインタファイルを退避させてFirefox再起動→ポインタファイルを元に戻して再々起動という手順が必要です.ただし,Linuxであればtouchコマンドでポインタファイルが指し示す先のフォルダの最終更新日を変更し,再起動するだけでかまいません.

フェーズ⑤̶ XPI パッケージング

フェーズ④にてHello world拡張機能は完成しました.しかし,ソースファイルのままでは他のユーザへ配布することができません.拡張機能を配布するためには,XPI形式のインストーラを作成する必要があります.

XPIファイルのフォルダ構成

XPIは,実はソースファイルをZIP圧縮しただけのものですが,一般的には,XPIファイル内部のフォルダ構成は開発中のフォルダ構成と異なります(図11).

図11:XPIファイル内部のフォルダ構成
[図11]

パッケージング手順

開発中のソースファイルのフォルダ構成を維持しつつ,図11で示すようなフォルダ構成でXPI パッケージングを行うためには,次のような手順が必要となります.

  1. helloworldフォルダ内に新たにchromeフォルダを作成する
  2. content,locale,skinの3フォルダをZIP圧縮(▽注9)してhelloworld.jarというファイル名にし,1.の手順で作成したchromeフォルダ内に配置する
  3. chrome.manifestをコピーし,chrome.manifest.bakなどへリネームしてバックアップする
  4. chrome.manifestの内容をリスト13のように変更する
  5. install.rdf,chrome.manifest,chromeフォルダの3つをZIP圧縮してhelloworld.xpiというファイル名にする
リスト13:chrome.manifestへの修正
content helloworld                                     jar:chrome/helloworld.jar!/content/
overlay   chrome://browser/content/browser.xul          chrome://helloworld/content/overlay.xul
locale    helloworld en-US                              jar:chrome/helloworld.jar!/locale/en-US/
locale    helloworld ja-JP                              jar:chrome/helloworld.jar!/locale/ja-JP/
skin      helloworld classic/1.0                        jar:chrome/helloworld.jar!/skin/classic/
style     chrome://browser/content/browser.xul          chrome://helloworld/skin/overlay.css
style     chrome://global/content/customizeToolbar.xul  chrome://helloworld/skin/overlay.css

以上でXPIパッケージング作業が完了です.ただし,手順4.にてクロムマニフェストへ記述された各パッケージの参照先フォルダのパスが,圧縮ファイルの内部へと変更されてしまう点に注意してください.

コラム

セッションストアAPI

セッションストアAPI(注A)は,Firefox 2で搭載された拡張機能開発者向けの新機能の1つで,XPCOMサービスであるnsISessionStoreインターフェースの各種メソッドを利用することで,セッションの保存や復元などを簡単に行うことができます.閉じたタブを元に戻す/クラッシュ後の再起動時に前回の状態を復元するといったFirefoxの機能は,すべてこのAPI を利用して実現されています.

nsISessionStoreインタフェースの中で,今回開発で使用する2つのメソッドの使い方を見てみましょう.

getBrowserState()

現在開いているすべてのブラウザウィンドウの状態を表すJSON 形式(注B)の文字列を返します.このJSON 文字列 には,開いているウィンドウやタブの情報,各タブの戻る/進むの履歴,ページのスクロール位置,文字サイズの拡大縮 小状態,フォームへ入力中の文字列など,色々な情報が含まれています.

setWindowState(aWindow, aState, aOverwrite)

引数aWindowで渡したブラウザウィンドウ上で,引数aStateで渡したウィンドウの状態を復元します.引数aOverwriteをtrueにすると現在開かれているタブを上書きして状態を復元し,falseにすると新しいタブを追加しながら状態を復元します.

△注A「Session store API - MDC」(https://developer.mozilla.org/ja/Session_store_API
△注BJSONとはJavaScript Object Notationの略で、JavaScriptによって容易に読み書きができるデータ形式です

XPIパッケージング作業完了後に引き続き開発作業を行う場合,手順4.で作成したクロムマニフェストを再び手順3.でバックアップしたものへと戻す作業が必要となります.もしこの作業を忘れると,その後の開発中にソースファイルへの変更がいっこうに反映されないトラブルに見舞われます.一連の作業は,バッチファイルによって自動化させるべきでしょう.あるいは次のような手順で簡易的にXPIパッケージングを行う方法もあります.

install.rdf,chrome.manifest,content,locale,skinの5つをZIP圧縮し,helloworld.xpiというファイル名にする

この方法では,クロムのパッケージをhelloworld.jarとしてZIP圧縮する手順が省略されます.このようにして作成したXPIファイルでも動作に問題はありませんが,Firefox起動時の動作が若干遅くなるという欠点があるようです.

XPIの動作確認

XPIファイルを作成したら,動作確認をします.現在開発環境として使用しているプロファイルはすでにHello world拡張機能がテストインストールされた状態ですので,別のプロファイルにてFirefoxを起動してください(注1参照).XPI ファイルをブラウザウィンドウへドラッグ&ドロップすれば,インストールが開始されるはずです.

単純なHello world拡張機能の開発を通じ,パッケージングまでの一連の流れを解説しました.

コラム

JavaScriptデバッグ方法

alert

JavaScriptのデバッグとして用いられる最も手軽な手段は,window.alertメソッドを使って変数などの内容をダイアログ表示させる方法です(リストA).ダイアログが表示される間は,非同期的な処理を除く他の処理が一時停止しますので,処理を止めながら変数の値を追っていくようなデバッグにはalertメソッドが適しているでしょう.

リストA:変数fooの値を調べる
alert(foo);

dump

Windows環境であれば,Firefoxを起動時に「-console」と引数を付けることで,ダンプ用コンソールが起動します.dumpメソッドによって,ダンプ用コンソールへ文字列を出力できるようになります(リストB).コンソールへの文字列出力は他の処理を停止することなく行われますので,大量のメッセージを出力させるようなデバッグにはdumpメソッドが適しているでしょう.なお,dumpメソッドを有効にするためには,「開発環境の準備」で述べた設定変更が必要です.また,Windows環境ではダンプ用コンソール上で日本語が文字化けします.

リストB:objのプロパティを列挙
for (var i in obj) {
    dump(i + " : " + obj[i] + "\n");
}

エラーコンソール

nsIConsoleServiceインターフェース

XPCOM サービスであるnsIConsoleServiceインターフェースのlogStringMessageメソッドによって,エラーコンソールの「メッセージ」パネルへ文字列を出力させることができます.日本語の出力も問題なく可能です.エラーコンソールへのメッセージ出力を頻繁に行う場合,リストCのような関数を定義しておくと便利です.

リストC:エラーコンソールへメッセージを出力する関数
function log(aText) {
    var console = Components.classes["@mozilla.org/consoleservice;1"]
            .getService(Components.interfaces.nsIConsoleService);
    console.logStringMessage(aText);
}
Components.utils.reportErrorメソッド

同じくエラーコンソールへ出力させるための手段として,Components.utils.reportErrorメソッドもあります.こちらを使った場合,エラーコンソールの「エラー」パネルへ出力されます.

拡張機能開発応用編 ~セッション管理拡張機能~

△注1注1 開発用のプロファイル名を「Test User」とすると,「firefox -p "Test User" -no-remote」へのショートカットを作成しておけば,ふだん使用しているプロファイルのFirefoxを起動中でも,別途開発用のプロファイルでFirefoxを起動させることができます.詳しくは「Firefox Help: プロファイルの管理」(http://www.mozillajapan.org/support/firefox/profile)を参照してください.
△注2言語パックはlocaleパッケージのみを持った拡張機能(アドオン),テーマはskinパッケージのみを持った拡張機能(アドオン)だといえます.
△注3なお,本誌では,Windows上でFirefoxを動作させていますが,LinuxやMac OSでも,ほぼ同一の手順になります.
△注4なお,本誌では,Windows上でFirefoxを動作させていますが,LinuxやMac OSでも,ほぼ同一の手順になります.
△注5「Firefox Help: プロファイルの管理」(http://support.mozilla.com/ja/kb/Profiles
△注7より一般的な拡張機能開発におけるトラブルシューティングについては,https://developer.mozilla.org/ja/Extension_Frequently_Asked_Questionsを参照してくだ さい.
△注8browser.xulへのoverlay.cssのクロスパッケージオーバーレイは,overlay.xulへxml-stylesheet処理命令を追加してoverlay.cssを参照させることでも実現可能です.
△注9Windows XPでは圧縮フォルダ機能を使ってZIP圧縮できますが,「7-Zip」のような圧縮率を指定できるツールをインストールすることをお勧めします.jarファイルを作成するときは圧縮率を低くし,xpiファイルを作成するときは圧縮率を高くすると良いでしょう.