AppLauncher ver.0.4 〜 リスト形式 (可変個数) の設定を行う 〜

Firefox拡張機能 (add-on の一種) を作ってみよう、ということで作り始めた AppLauncher。 前回は、Firefoxコンテキストメニューから外部アプリケーションを起動するところまで作成しました。

今回はコンテキストメニューから起動する外部アプリケーションの設定を行うための、設定画面の作成と、設定の保存に関して作成しました。

AppLauncher ver.0.4 の作成

Firefox から外部アプリケーションを開く、という拡張機能 AppLauncher を作っていきます。

Ver.0.4 では、起動する外部アプリケーションの設定を行う部分を実装します。

なお、現時点 (ver.0.3) でのファイル構成は以下のようになっています。

[applauncher-0.3.xpi]
+--[chrome.manifest]
+--[install.rdf]
+--[content]
+--[launcher.xul]
+--[launcher.js]
+--[prefs.xul]

設定画面の作成

可変個数の設定を編集できるように、上図のような設定画面を作成します。 "prefs.xul" ファイルを下記のように変更しました。

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
<?xml-stylesheet href="chrome://applauncher/content/prefs.css" type="text/css"?>
<!-- 設定ウィンドウ
cf. dialog について: https://developer.mozilla.org/ja/XUL_Tutorial/Creating_Dialogs -->
<dialog id="info.vividcode.applauncher.prefwindow" title="AppLauncher の設定"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
buttons="accept,cancel">
<!-- JavaScript を読み込み -->
<script type="application/x-javascript" src="chrome://applauncher/content/AppLauncher.js" />
<script type="application/x-javascript" src="chrome://applauncher/content/prefs.js" />
<!-- 実際の表示を規定 -->
<vbox>

<listbox id="info.vividcode.applauncher.prefwindow.listbox">
<listhead>
<listheader label="Name" />
<listheader label="Path" />
<listheader label="Arguments" />
</listhead>
<listcols>
<listcol/>
<listcol flex="1"/>
<listcol flex="1"/>
</listcols>
</listbox>

<groupbox>
<caption label="adding your application" />
<vbox>
<grid>
<columns>
<column flex="1"/>
<column flex="2"/>
</columns>
<rows>
<row>
<label control="info.vividcode.applauncher.prefwindow.adding.name" value="Name" />
<textbox id="info.vividcode.applauncher.prefwindow.adding.name" />
</row>
<row>
<label control="info.vividcode.applauncher.prefwindow.adding.path" value="Path" />
<textbox id="info.vividcode.applauncher.prefwindow.adding.path" />
</row>
<row>
<label control="info.vividcode.applauncher.prefwindow.adding.args" value="Arguments" />
<textbox id="info.vividcode.applauncher.prefwindow.adding.args" />
</row>
</rows>
</grid>
<button label="add" id="info.vividcode.applauncher.prefwindow.adding.button" />
</vbox>
</groupbox>
</vbox>
</dialog>

新しい項目の追加や設定の保存など、動作部分の実装

設定用の画面は XUL で規定しましたが、「Add」 ボタンの動作や、「OK」 ボタンの動作などはまだ決まっていません。 それらの動作は JavaScript で実装します。

"prefs.js" を以下のように編集しました。 「OK」 ボタンが押されたときに、設定画面の要素を (DOM オブジェクトとして) 取得し、XMLSerializer を使って文字列に変換、その文字列を pref として保存する、という動作です。 逆に保存している設定を読み込むときは DOMParser を使用します。

// AppLauncher の設定に関する JavaScript

/**
* ユーザが設定画面に入力した値を XML 形式で保存する.
*/
AppLauncher.savePrefs = function() {
// 設定画面の要素を取得
var listbox = document.getElementById("info.vividcode.applauncher.prefwindow.listbox");
var items = listbox.getElementsByTagNameNS(this.XUL_NS, "listitem");
// 保存用の XML Document を新たに生成
var prefNode = document.implementation.createDocument(this.PREFS_NS, "appList", null);
// 保存用 XML に値を追加していく
for( var i = 0; i < items.length; i++ ) {
var cells = items[i].getElementsByTagNameNS(this.XUL_NS, "listcell");
var app = document.createElementNS(this.PREFS_NS, "app");
app.setAttribute( "name", cells[0].getAttribute("label") );
app.setAttribute( "path", cells[1].getAttribute("label") );
app.setAttribute( "args", cells[2].getAttribute("label") );
prefNode.documentElement.appendChild(app);
}
// DOM を XML 文にして保存
// cf. https://developer.mozilla.org/ja/XMLSerializer
var serializer = new XMLSerializer();
var prefStr = serializer.serializeToString(prefNode);
this.setCharPref("appList", prefStr);
window.alert("accept!:\n" + prefStr);
};

/**
* ユーザの設定画面に以前の設定を読み出す.
*/
AppLauncher.loadPrefs = function() {
// 以前の設定を DOM として取得
var appListStr = this.getPref("appList");
var parser = new DOMParser();
var appList = parser.parseFromString(appListStr, "text/xml");
var items = appList.getElementsByTagNameNS(this.PREFS_NS, "app");
// 設定画面の要素を取得
var listbox = document.getElementById("info.vividcode.applauncher.prefwindow.listbox");
//var items = listbox.getElementsByTagNameNS(this.XUL_NS, "listitem");
// 設定画面に要素を追加していく
for( var i = 0; i < items.length; i++ ) {
var item = document.createElementNS(this.XUL_NS, "listitem");
var cell = document.createElementNS(this.XUL_NS, "listcell");
cell.setAttribute( "label", items[i].getAttribute("name") );
item.appendChild(cell);
cell = document.createElementNS(this.XUL_NS, "listcell");
cell.setAttribute( "label", items[i].getAttribute("path") );
item.appendChild(cell);
cell = document.createElementNS(this.XUL_NS, "listcell");
cell.setAttribute( "label", items[i].getAttribute("args") );
item.appendChild(cell);
item.addEventListener("dblclick", this.elDeletingApp, false);
listbox.appendChild(item);
}
};

AppLauncher.addNewApprication = function() {
var listitem = document.createElementNS(AppLauncher.XUL_NS, "listitem");
var listcell = document.createElementNS(AppLauncher.XUL_NS, "listcell");
listcell.setAttribute("label", document.getElementById("info.vividcode.applauncher.prefwindow.adding.name").value);
listitem.appendChild(listcell);
var listcell = document.createElementNS(AppLauncher.XUL_NS, "listcell");
listcell.setAttribute("label", document.getElementById("info.vividcode.applauncher.prefwindow.adding.path").value);
listitem.appendChild(listcell);
var listcell = document.createElementNS(AppLauncher.XUL_NS, "listcell");
listcell.setAttribute("label", document.getElementById("info.vividcode.applauncher.prefwindow.adding.args").value);
listitem.appendChild(listcell);
listitem.addEventListener("dblclick", this.elDeletingApp, false);
document.getElementById("info.vividcode.applauncher.prefwindow.listbox").appendChild(listitem);
};

AppLauncher.deleteSelectedApprication = function() {
var listbox = document.getElementById("info.vividcode.applauncher.prefwindow.listbox");
var items = listbox.selectedItems;
// 選択項目が無い
if( items.length == 0 ) {
throw new Error("選択されている項目がありません.");
}
// 2 つ以上選択されている
else if( items.length > 1 ) {
throw new Error("2 つ以上のアイテムが選択されています.");
}
listbox.removeChild( items[0] );
/*
var listitem = document.createElementNS(AppLauncher.XUL_NS, "listitem");
var listcell = document.createElementNS(AppLauncher.XUL_NS, "listcell");
listcell.setAttribute("label", document.getElementById("info.vividcode.applauncher.prefwindow.adding.name").value);
listitem.appendChild(listcell);
var listcell = document.createElementNS(AppLauncher.XUL_NS, "listcell");
listcell.setAttribute("label", document.getElementById("info.vividcode.applauncher.prefwindow.adding.path").value);
listitem.appendChild(listcell);
var listcell = document.createElementNS(AppLauncher.XUL_NS, "listcell");
listcell.setAttribute("label", document.getElementById("info.vividcode.applauncher.prefwindow.adding.args").value);
listitem.appendChild(listcell);
document.getElementById("info.vividcode.applauncher.prefwindow.listbox").appendChild(listitem);
*/
};

AppLauncher.elAddingApp = function(evt) {
AppLauncher.addNewApprication();
};

AppLauncher.elDeletingApp = function(evt) {
AppLauncher.deleteSelectedApprication();
};

/**
* OK ボタンの実行をキャッチするイベントリスナ.
*/
AppLauncher.accept = function(evt) {
AppLauncher.savePrefs();
};

AppLauncher.cancel = function(evt) {
window.alert("cancel...");
};

AppLauncher.initPref = function(evt) {
var item = document.getElementById("info.vividcode.applauncher.prefwindow");
if( item ) {
// イベントリスナの追加
item.addEventListener("dialogaccept", AppLauncher.accept, false);
item.addEventListener("dialogcancel", AppLauncher.cancel, false);
}
item = document.getElementById("info.vividcode.applauncher.prefwindow.adding.button");
if( item ) {
// イベントリスナの追加
item.addEventListener("command", AppLauncher.elAddingApp, false);
}
AppLauncher.loadPrefs();
};

// 初期化関数をロード時に実行する
window.addEventListener("load", AppLauncher.initPref, false);

その他の変更箇所

あとはコンテキストメニューを設定したとおりに動作するようにするなど、ちゃんと動くために必要な変更を行いました。

一段落?

今回の ver.0.4 で個人的に欲しかった機能 (Firefoxコンテキストメニューから IE やら IETester を起動させるという機能) が完成しました。 まだまだ改良の余地はありますけどちまちま改造していこうかと思います。

AppLauncher ver.0.3 〜 XPCOM を利用して外部アプリケーションを起動する 〜

Firefox拡張機能 (add-on の一種) を作ってみよう、ということで作り始めた AppLauncher。 前回は、コンテキストメニューに AppLaunhcer の項目を追加しました。

今回はいよいよ外部アプリケーションの起動を行うということをします。

AppLauncher ver.0.3 の作成

Firefox から外部アプリケーションを開く、という拡張機能 AppLauncher を作っていきます。

Ver.0.3 では、外部アプリケーションを起動する部分を実装します。 外部アプリケーションを開くなど、OS と関係してくる処理は XPCOM が担ってくれます。 JavaScript から XPCOM を利用するためには、XPConnect という技術を用います。

今回は外部アプリケーションを起動するわけですが、起動対象のアプリケーションは設定画面にパスを入力することで指定することにします。 ということで、まずは設定の取得 (これも XPCOM で行います)、その後外部アプリケーションの起動、という風にプログラムを書いていきます。

なお、現時点 (ver.0.2) でのファイル構成は以下のようになっています。

[applauncher-0.2.xpi]
+--[chrome.manifest]
+--[install.rdf]
+--[content]
+--[launcher.xul]
+--[launcher.js]
+--[prefs.xul]

設定の取得

まずは設定した値を取得します。 基本的に MDC の設定システムに関する文書 どおりです。

"content/launcher.js" ファイルに、以下の getPref 関数を新たに追加しました。

(省略)
/**
* Preference 情報を取得する関数 (設定の情報)
* @param prefString 取得するキー
*/
// cf.https://developer.mozilla.org/Ja/Code_snippets/Preferences
getPref: function( prefString ) {
var branchString = "extensions.applauncher.";
// 設定の情報を取得する XPCOM オブジェクトの生成
var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
var prefBranch = prefSvc.getBranch(branchString);
// タイプ別に取得する関数を分ける
switch( prefBranch.getPrefType(prefString) ) {
// 設定値が文字の場合
case Components.interfaces.nsIPrefBranch.PREF_STRING:
return prefBranch.getCharPref(prefString);
break;
// 設定値が数値の場合
case Components.interfaces.nsIPrefBranch.PREF_INT:
return prefBranch.getIntPref(prefString);
break;
// 設定値が真偽値の場合
case Components.interfaces.nsIPrefBranch.PREF_BOOL:
return prefBranch.getBoolPref(prefString);
break;
}
},
(省略)

外部アプリケーションを起動する

次に、外部アプリケーションの起動を行います。 これも MDC の外部アプリケーションを起動する方法を書いた文書 どおりです。

"content/launcher.js" ファイルの launch 関数を以下のように変更しました。

(省略)
/**
* 外部アプリケーションを起動する関数
* 現在は、テスト用のアラートを表示するだけ.
*/
launch: function( evt ) {
try {
// 設定値の取得
var path = AppLauncher.getPref("path");
window.alert( "launching \"" + path + "\"..." );
// cf.https://developer.mozilla.org/ja/Code_snippets/Running_applications
// 実行可能ファイルに対する nsILocalFile を作成する
var file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(path);
if( ! file.exists() ) {
throw new Error("The File \"" + path + "\" doesn't exist!");
}
// nsIProcess を作成する
var process = Components.classes["@mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(file);
// プロセスの起動
// 最初のパラメータが true なら、スレッドはプロセスが終わるまでブロックされる。
// 2 つ目と 3 つ目のパラメータは、プロセスのコマンドライン引数
// var args = ["argument1", "argument2"];
var args = []; // とりあえず今は引数なし
process.run(false, args, args.length);
} catch(e) {
window.alert("[EXCEPTION] An exception occured in \"AppLauncher\": \n" + e.message);
}
},
(省略)

これで、設定画面に記述したパスの外部ファイルが起動するようになりました。

展望

これで一応外部アプリケーションを起動できるようになったので、後はどのアプリケーションをどのような引数で起動するのか、といった設定をどのように記述するのか考えていきたいと思います。

AppLauncher ver.0.2 〜 コンテキストメニューへの追加と動的な変更 〜

Firefox拡張機能 (add-on の一種) を作ってみよう、ということで作り始めた AppLauncher。 前回は、ステータスバーに新たなパネルを追加し、それをクリックすると警告ウィンドウが表示されるというものを作りました。

今回はステータスバーではなく、コンテキストメニュー (右クリックをしたときに表示されるメニュー) に AppLauncher の項目を追加したいと思います。

AppLauncher ver.0.2 の作成

Firefox から外部アプリケーションを開く、という拡張機能 AppLauncher を作っていきます。

Ver.0.2 では、コンテキストメニューに AppLauncher の項目を追加します。 AppLauncher の項目を選ぶとサブメニューが開き、将来的にはそこから外部アプリケーションを起動するようにします。 そのため、サブメニュー内の項目は固定ではなく、動的に変更できるようにしておきます。

今回は、XUL のオーバーレイにより、サブメニュー内に 1 つの固定項目を作り、さらに JavaScript で動的に 1 つの項目を追加します。

なお、現時点 (ver.0.1) でのファイル構成は以下のようになっています。

[applauncher-0.1.xpi]
+--[chrome.manifest]
+--[install.rdf]
+--[content]
+--[launcher.xul]
+--[launcher.js]
+--[prefs.xul]

XUL のオーバーレイで、コンテキストメニューに要素を追加する

まずは固定のメニューを作成します。

XUL のオーバーレイを使用して、簡単にコンテキストメニューに要素を追加することが出来ます。 MDC のこの文書 が参考になります。

"content/launcher.xul" ファイルを以下のように書き換えました。

<?xml version="1.0" encoding="UTF-8"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

<!-- JavaScript を読み込み -->
<script type="application/x-javascript" src="chrome://applauncher/content/launcher.js" />

<!-- コンテキストメニューへの追加: cf. https://developer.mozilla.org/ja/XUL/PopupGuide/Extensions -->
<popup id="contentAreaContextMenu">
<menuseparator id="info.vividcode.applauncher.contextmenu.separator" />
<menu id="info.vividcode.applauncher.contextmenu" label="AppLauncher">
<menupopup id="info.vividcode.applauncher.contextmenu.items">
<menuitem id="info.vividcode.applauncher.contextmenu.item1" label="item1" />
<!-- JavaScript ("launcher.js") によってこの部分に要素を追加する -->
</menupopup>
</menu>
</popup>

</overlay>

この XUL ファイルの変更だけを行って Firefox拡張機能を再インストールすると、以下のように、コンテキストメニューに要素が追加されます。 現時点ではクリックしても何も起こりません。

JavaScript を使って、要素を動的に変更する

さて、固定的な要素の配置は XUL で行うことが出来ました。 次は JavaScript を使って動的に要素の追加を行います。

さきほど "content/launcher.xul" ファイルに書いたのを見てもらえばわかると思いますが、サブメニュー全体を表す要素が "menupopup" で、その中の項目が "menuitem" 要素です。 よって、新たに項目を加えるには、JavaScript 上で DOM を使って "menupopup" 要素を取得し、その子要素として新たに作成した "menuitem" 要素を加えてやることになります。

DOM は結構一般的な規格なので知っている人は知っていると思いますが、知らない人は XML のインターフェイスを動的に変更する方法の説明 (MDC) を読んでください。

また、メニューの項目をクリックしたときに JavaScript の関数を呼び出したいので、addEventListener 関数を使ってイベントリスナの登録も行います。 この関数も DOM の一部なので説明は省きますが、知らない方は addEventListener の説明 (MDC) を読んでください。

これらの内容を実装すると、"content/launcher.js" ファイルは以下のようになりました。

// AppLauncher の JavaScript
var AppLauncher = {

/**
* 外部アプリケーションを起動する関数
* 現在は、テスト用のアラートを表示するだけ.
*/
launch: function( evt ) {
window.alert( "Hello AppLauncher! (" + evt.currentTarget.getAttribute("label") + ")" );
},

/**
* 初期化関数
* AppLauncher に関するコンテキストメニューの変更を行う.
*/
init: function() {
// XUL で追加済みの "menuitem" 要素を取得し、イベントリスナを追加する
var item = document.getElementById("info.vividcode.applauncher.contextmenu.item1");
if( item ) {
// イベントリスナの追加
item.addEventListener("command", AppLauncher.launch, false);
}
// コンテキストメニュー内の、AppLauncher に関する "menupopup" 要素 (id で指定) を取得
var menupopup = document.getElementById("info.vividcode.applauncher.contextmenu.items");
if( menupopup ) {
// "menuitem" 要素の作成
item = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
// "label" 属性に "adding item!" という値を設定
item.setAttribute("label", "item2");
// イベントリスナの追加
item.addEventListener("command", AppLauncher.launch, false);
// "menupopup" 要素の子ノードに追加
menupopup.appendChild(item);
}
menupopup = null;
item = null;
},

}

// 初期化関数をロード時に実行する
window.addEventListener("load", AppLauncher.init, false);

ここまでが ver.0.2 の内容です。 これを Firefox にインストールすると、コンテキストメニューが動的に書き換えられているはずです。 また、イベントリスナの追加を行ったので、クリックすると警告ウィンドウが開きます。

展望

今回は見た目の変更だけでしたが、次回はいよいよ外部アプリケーションの起動を行いたいと思います。

XUL のコメント内にハイフンを連続で書いてはいけない

Firefox拡張機能の開発時に、何故か XUL ファイルに書いた通りに動かなくて困った時の話。

  1. 色々試してみて、コメントを全て消すと動くことに気づく。
  2. あれ・・・? コメントを書いたらだめってわけじゃないよなぁ。
  3. あ、もしかしてダブルハイフン (2 連続のハイフン: "--") はコメントに書いたらダメなのか。
  4. ビンゴ!

というわけで XUL ファイルのコメント内にダブルハイフンは書いたらダメみたいです。 というか、ダメです。 XML 1.0 勧告 (Comments 節) にしっかりと書いてました。

長らく XML は触ってきたけど、コメント内のハイフンの連続は 「推奨されない」 だと思ってました。 「推奨されない」 ではなくて 「してはいけない」 なのですね。。 恥ずかしい(/∀\)

AppLauncher ver.0.1 〜 Firefox 拡張機能作り方メモ 〜

この記事の内容は古くなってしまっていて、最近だと別の Firefox 拡張の開発方法もある (もちろんこの記事に書かれている方法でも開発できます) ので、2013 年における Firefox 拡張機能の開発方法について別の記事にまとめました。 そちらもあわせてご覧ください。

前々から Firefox拡張機能 (extension: アドオン add-on の一種) を作りたいなーと思っていたので、今回実行に移すことにしました。 作成する拡張機能は、コンテキストメニューから外部アプリケーションを起動するというもの。 ありがちですけどね、最終的な目標としては現在開いているページを外部ブラウザで開けたらいいかなーと。

ちなみに同じような機能を持つ拡張機能としては既に "Launchy" というものがあります。

以下、拡張機能の作り方メモ。

AppLauncher ver.0.1 の作成

とりあえず初めての拡張機能ということで、インストールが出来るかどうか、というところから。 Ver.0.1 では中身がほとんどない拡張機能を作成し、正常にインストールでき、動作することを確認します。

参考にした web ページ

作成の流れは以下のページを参考にしました。 基本的には 「作り方メモ」 に沿っています。

開発環境

私の環境です。 別の OS でも大体の流れは一緒だと思います。 Firefox のバージョンは、3 以上であれば一緒だと思いますが、Firefox 2 とか Firefox 1.5 だと違う部分があるかもしれません。

拡張機能の作成を行う準備

さて、まずは拡張機能の作成を行う準備です。

  • 新しい Firefox プロファイルの作成
  • 開発用に Firefox の設定変更
  • 開発用のフォルダ作成

を行います。 ここは 作り方メモ と全く同じように行いました。 ただし、開発用のフォルダ名は "applauncher" としました。

開発中の拡張機能Firefox にインストールする方法は 作り方メモ での方法と異なり、いちいち xpi ファイルを作成する方法を採りました。 うちの環境では Windows の右クリックメニューから ZIP 圧縮ができるためさほど手間ではないと感じたためです。

クロムマニフェスト (chrome.manifest) の作成

準備が出来たので、さっそく拡張機能の開発を始めましょう!

開発用フォルダ "applauncher" のすぐ下に "chrome.manifest" というファイルを作成します。 私の場合は以下のように記述しました。

# パッケージの定義
# 下記形式で記述する.
# content [packagename] [path/to/files]
# locale [packagename] [localename] [path/to/files]
# skin [packagename] [skinname] [path/to/files]
content applauncher content/
# => "content" ディレクトリに "chrome://applauncher/content" という
# パスでアクセスできるようになる.

# 他のパッケージのオーバーレイ
overlay chrome://browser/content/browser.xul chrome://applauncher/content/launcher.xul

インストールマニフェスト (install.rdf) の作成

アドオン (ここでは拡張機能) が Firefox などのアプリケーションにインストールされるときに必要な情報を "install.rdf" に記述します。

開発用フォルダ "applauncher" のすぐ下に "install.rdf" というファイルを作成し、以下のように記述しました。

<?xml version="1.0" encoding="UTF-8"?>
<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">

<!-- 拡張機能を一意に識別するための ID. メールアドレス形式か GUID 形式 -->
<em:id>applauncher@vividcode.info</em:id>
<!-- アドオンのタイプ. 拡張機能であれば 2 -->
<em:type>2</em:type>
<!-- 拡張機能の名前 -->
<em:name>AppLauncher</em:name>
<!-- 拡張機能のバージョン. 規則あり -->
<em:version>0.1</em:version>
<!-- 拡張機能の説明 -->
<em:description>This extension lets you launch an outer application by context menu.</em:description>
<!-- 拡張機能の開発者 -->
<em:creator>nobuoka</em:creator>
<!-- 拡張機能を配布したりするための web ページアドレス -->
<em:homepageURL>http://www.vividcode.info/firefox_addon/myextensions/applauncher/</em:homepageURL>
<!-- 拡張機能の設定に関する XUL ファイル. 無くても良い -->
<em:optionsURL>chrome://applauncher/content/prefs.xul</em:optionsURL>

<!-- 各地域の言語情報. 無くても良い -->
<em:localized>
<Description>
<em:locale>ja</em:locale>
<em:name>AppLauncher</em:name>
<em:description>この拡張機能を使うと、コンテキストメニューから外部アプリケーションを起動できます。</em:description>
<em:creator>nobuoka</em:creator>
<em:homepageURL>http://www.vividcode.info/firefox_addon/myextensions/applauncher/</em:homepageURL>
</Description>
</em:localized>

<!-- 拡張機能の対応アプリケーション. 下の例では Firefox 3.0 に対応 -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>3.0</em:minVersion>
<em:maxVersion>3.0.*</em:maxVersion>
</Description>
</em:targetApplication>

</Description>
</RDF>

拡張機能の中身を作成

肝心の中身の部分を作成します。 まず開発フォルダ "applauncher" の下に "content" というフォルダを作成します。

"chrome.manifest" に書いたパッケージの定義により、"chrome.manifest" や "install.rdf" 内の "chrome://applauncher/content" というパスはこの "content" フォルダを指すことになります。

"launcher.xul" ファイルの作成

"applauncher/content" フォルダの下に "launcher.xul" という XUL ファイルを作成します。 ファイル名は何でもいいのですが、"chrome.manifest" に書くパッケージのオーバーレイの定義で、このファイル名を指定してください。

中身は 作り方メモ とほとんど同じようにしておきました。

<?xml version="1.0" encoding="UTF-8"?>
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<!-- JavaScript を読み込み -->
<script type="application/x-javascript" src="chrome://applauncher/content/launcher.js" />
<!-- ステータスバーに独自のパネルを追加 -->
<statusbar id="status-bar">
<!-- パネルには "Hello, Firefox extension!!" と表示され、その部分をクリックすると
JavaScript の AppLauncher.alert 関数が呼び出される -->
<statusbarpanel id="info.vividcode.applauncher.my-panel"
label="Hello, Firefox extension!!" onmousedown="AppLauncher.alert('Hello');" />
</statusbar>
</overlay>

"chrome.manifest" に、このファイルをオーバーレイとするように記述しているため、上のように書いておくとステータスバーに独自のパネルが追加されます。

"launcher.js" ファイルの作成

"applauncher/content" フォルダの下に "launcher.js" という JavaScript ファイルを作成します。 この JavaScript ファイルは、上で作成した "launcher.xul" で読み込まれるものです。

今回は、単にアラートウィンドウを開く関数だけ記述しています。

// AppLauncher の JavaScript
var AppLauncher = {
alert: function( msg ) {
window.alert(msg);
}
}

"prefs.xul" ファイルの作成

最後に、設定ウィンドウの表示を記述するための "prefs.xul" ファイルを作成します。 このファイル名は "install.rdf" ファイルで指定したものです。

とりあえず、テキストボックスを 1 つ持つ設定ウィンドウを記述しました。

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
<!-- 設定ウィンドウ -->
<prefwindow id="info.vividcode.applauncher.prefwindow" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="AppLauncher の設定" bottons="accept,cancel">
<!-- 設定ペイン -->
<prefpane id="info.vividcode.applauncher.prefpane">
<!-- 必ず 1 つ以上の "preferences" 要素を含む -->
<preferences>
<!-- 設定項目 -->
<preference id="info.vividcode.applauncher.pref.path" name="extensions.applauncher.path" type="string" />
</preferences>
<!-- 実際の表示を規定 -->
<hbox>
<!-- preference 属性は、preference 要素の id 属性と一致させる -->
<textbox preference="info.vividcode.applauncher.pref.path" />
</hbox>
</prefpane>
</prefwindow>

XPI ファイルの作成

以上で、中身の作成は終わりました。 次に Firefox にインストールするため、作成したファイル群を XPI ファイルにまとめます。

XPI ファイルの実体は ZIP ファイルなので、作成したファイル群をとりあえず ZIP 圧縮します。 このとき注意するべきことは、開発用のフォルダごと圧縮するのではなく "chrome.manifest" ファイル、"install.rdf" ファイル、そして中身を含めた "content" フォルダの 3 つを圧縮する、ということです。 つまり、XPI ファイルの中身は以下のようになっているはずです。

[launcher.xpi]
+--[chrome.manifest]
+--[install.rdf]
+--[content]
+--[launcher.xul]
+--[launcher.js]
+--[prefs.xul]

"content" フォルダなどを JAR ファイルにまとめる等の方法を採ることが一般的なようですが、必ずしもそれは必要ないようです。

ZIP 圧縮ができたら、後は拡張子を "xpi" に変更すれば XPI ファイルの完成です。

インストール、動作確認

インストールは簡単で、作成した XPI ファイルを Firefox の画面上にドラッグアンドドロップするだけです。 自動的にインストールウィンドウが開き、インストールされます。 Firefox を再起動するとインストールが完了しているはずです。

インストールしたら、メニューの 「ツール」 から 「アドオン」 を選び、拡張機能の一覧の中に自作の拡張機能があるか確認してください。 もし無ければなんらかの問題が発生している可能性があります。 エラーコンソールの確認をしたり、コードに問題が無いか確認してください。 また、そのままインストールしなおそうとしても出来ない可能性があるので、新たにプロファイルを作成しなおしてください。

拡張機能の一覧の中に自作の拡張機能がある場合は、多分問題なくインストールできています。 「設定」 ボタンを押してみたり、(今回の場合) ステータスバーに追加したパネルがちゃんと表示されているか、動作はするかなど確認してみてください。

再インストール

正常にインストールできている場合は、再インストールは簡単にできます。 XPI ファイルを再度ドラッグアンドドロップすると自動的に再インストールができます。 元々の拡張機能を削除したりする必要はありませんでした。

展望

拡張機能のインストールができ、きちんと動作することが確認できました。

次回以降は外部アプリケーションの起動に向けて、中身を作っていくことになります。