株式会社KBMJ
もじら組組長 松澤 太郎 aka btm

本稿では拡張機能の開発を支援するツールを紹介します.

拡張を支援するツールたち

Javascript Debugger - Venkman

Venkman は Firefox 上で動作する本格的な Javascript デバッガです.一般的なデバッガの操作を網羅しており,拡張機能開発のみではなく Web 開発にも利用することが可能です.

また,拡張機能やFirefox内部でJavascriptが使われているため,デバッガを通して設計方法を学ぶ事ができます.

インストール

Mozilla Suite や現在の SeaMonkey には本体に付属していましたが,Firefox では拡張機能としてインストールをする事で利用可能になっています.
https://addons.mozilla.org/ja/firefox/addon/216

上記Webサイトから拡張機能をインストールして Firefox を再起動することで利用可能になります.

使用方法

ツールから 「Javascript Debugger」 を選択します.

JavaScriptをデバッグ対象にする

Venkman の初期起動時は,Webブラウザ内のファイルのみがデバッグ可能となっています.

これは,多くのJavascriptファイルを読み込むと動作が遅くなるためです.

拡張機能のデバッグの際は,この制限を解く必要があります.「Debug」 メニュー の 「Exclude Browser File」 のチェックを外します(図1).

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

QuickNoteの準備

今回は使用例として QuickNote [▽注1]のファイル保存の仕組みをデバッガでたどってみます.

下記URLから QuickNote をインストールして,Firefoxを再起動します.
https://addons.mozilla.org/firefox/46/

Firefox の起動後に QuickNote を起動します.

その後,[ツール] - [QuickNote] - [Float] を選択して,単独のWindow として表示します.

Venmkan の利用

ソースコードを読み取る

次に Venkman を起動し,画面左上の [Open Windows] - [quicknote.xul] - [Files] と開き, quicknote.js を選択してダブルクリックして, Source Code 画面に読みこみます(図2).

図2:ソースコード選択
[図2]
ブレークポイントの設置

まずは,ブレークポイントを設置を行います.354行目付近にある QuickNote_saveNote 関数内にブレークポイントを設置してください(図3).

図3:ブレークポイントの設定
[図3]
デバッグ開始

QuickNote の画面にて適当に文字を入力して,メニューから[Save Current Tab] を選択します.

すると,Venkman でブレークポイントを設置した箇所でプログラムが停止して,デバッグが開始されます.

Local Variables の画面ではステップ実行時の変数やオブジェクトがつねに表示されています.ただし,変数が多い関数などでは見づらくなりますので,必要な変数を監視対象に追加します(図4).

図4:監視対象に追加
[図4]

監視対象に追加した変数は, Watches タブに入り,評価が行なわれるごとに更新されていきます.

あとはステップ実行をしつつ,プログラムや変数の動きを見ていきます.

調査が終わったら, Continue を実行して止まっていたプログラムを実行させます.

筆者の場合ほどんど拡張機能開発を行なっていないため,ほぼいろいろな拡張機能の実装を調べるのに使っています.QuickNote の例でもあるように,まず1ヶ所ブレークポイントを設置し,あとはひたすらステップ実行をしていきます.

Step Over はなるべく使わず,Step Into で必ず呼びだしている関数の中に入っていき,関数の調査がひととおり終わったらStep Out をして,また Step Into で次の関数へということを繰り返していきます.

この時に気になる点やよく理解できなかった点があればブレークポイントを設置しておき,次の動作の際には Continue でその箇所まで移動して再度調査します.

MozUnit

MozUnit は Javascript 向けの単体テストを支援するツールです.Eclipse の JUnit を支援するテストランナーに似た GUI を提供しており,Javaプログラマなどにはとっつきやすいツールになっています.

インストール

MozUnit は Mozlab 拡張機能の一機能として提供されています.次のURLから Mozlab インストールして Firefox を再起動します.
http://beta.hyperstruct.net/projects/mozlabhttp://hyperstruct.net/projects/mozlab

再起動後に,テストを記述するためのエディタを設定します.

about:config 画面を開いて,extensions.mozlab.mozunit.editor のプロパティを編集します.

パラメータとしては表1が利用できます.例えばTerapad [▽注2] を利用しているのであれば,extensions.mozlab.mozunit.editor の値を次のように記述します.
C:¥app¥tpad090¥Terapad.exe /jl=%l %f

表1:MozUnitで利用できるパラメータ
パラメータ意味
%fファイル名
%l行番号
%cカラム番号

筆者の場合はMeadowを利用しており,gnuserv を利用して開くようにしています.
C:¥app¥Meadow¥bin¥gnuclientw.exe +%l:%c %f

なお,今回テストした限りでは, Program Files にあるエディタでは利用ができませんでした.空白があるファイル名はなるべく利用しない方がよいでしょう.

使用方法

RPN計算機

今回は簡単なRPN計算機[▽注3]をJavaScriptで作成する過程から,どのように利用するかを見ていきます.

プログラム側では RpnCalc というクラスを作成します.まずは簡単なインターフェイスだけを記述したものを用意しておきます(リスト1).

リスト1:calc.js (第1段階)
function RpnCalc() {
}

RpnCalc.prototype = {
    init: function() {
    },
    push: function(val) {
    },
    plus: function() {
    },
    pop: function() {
    }
}

プラス処理部分の実装

テストケースの作成

 テストケースの作成を始めます.

まず,メニューから [ツール] - [Mozlab] - [Open MozUnit Runner] を選択して, MozUnit の GUI を立ちあげます.[File] - [New] を選択し,テスト対象のファイルと同じフォルダにtest_calc.js として保存します.

そして画面のEditメニューを押してテストケースを編集します.今回は 「2 1 +」 のテストを記述します(リスト2).

リスト2:test_calc.js (1つ目のテストケース)
var TestCase = mozlab.mozunit.TestCase;
var assert = mozlab.mozunit.assertions;

var tc = new TestCase('Rpn Calc Testcase');

var module = new ModuleManager();
var rpncalc = module.require('package', 'calc');

tc.tests = {
    '2 1 +': function() {
        var calc = new rpncalc.RpnCalc();
        calc.init();
        calc.push(2);
        calc.push(1);
        calc.plus();
        assert.equals(calc.pop(), 3);
    }
}

このファイルで,メソッド名がテストの名前になっていることに注目してください.

JunitなどではtestHogeという先頭にtestが入ったメソッドをテストとして扱います.MozUnit でも同じように記述は可能ですが,文字列をメソッド名にすることも可能で,ここにテストの内容を記述することでドキュメンテーションの手間が減ります.なかなかおもしろいアプローチと言えます.

エラーが出ることを確認

この段階で1度テストを実行すると,赤いバーが表示され,テストが失敗したことがわかります.

calc.jsの実装

では,calc.js の実装を開始します.今回はリスト3のように実装しました.

リスト3:calc.js 実装後
function RpnCalc() {
    this.stack = new Array();
}

RpnCalc.prototype = {
    init: function() {
        this.stack = new Array();
    },

    push: function(val) {
        this.stack.push(Number(val));
    },
    _letfunc: function(func) {
        a = this.pop();
        b = this.pop();
        this.push(func(a, b));
    },
    plus: function() {
        return this._letfunc(this._plus);
    },
    _plus: function(a, b) {
        return a + b;
    },
    pop: function() {
        return this.stack.pop();
    }

}
テストの実行

テストを実行すると緑色のバーが表示されました.テストは成功です.

マイナス処理部分の実装

では,次にマイナスを実装します.

テストケースの作成

最初にマイナスのメソッドのテストケースを追加します.test_calc.js を開いてリスト4のメソッドを追加します.

リスト4:test_calc.js (テストケース追加例)
'2 1 -': function() {
    var calc = new rpncalc.RpnCalc();
    calc.init();
    calc.push(2);
    calc.push(1);
    calc.minus();
    assert.equals(calc.pop(), 1);
},
calc.jsの実装

次に実装を記述します.プラスと同じように実装をしてみます.calc.js を開いてリスト5のメソッドを追加します.

リスト5:calc.js (マイナスの実装追加)
minus: function() {
    this._letfunc(this._minus);
},
_minus: function(a, b) {
    return a - b;
},
テストの実行

これでテストを実行すると赤いバーが表示されました.どこが問題であったのでしょうか?

画面には「Expected -1 , got 1 .」と表示されています.どうやら pop() で取ってきた順番と関数の引数の順番がおかしくなっていたようです._letfunc関数をリスト6のように修正します.

リスト6:calc.js (マイナスの実装追加を修正)
_letfunc: function(func) {
    // pop する順番を訂正
    b = this.pop();
    a = this.pop();
    this.push(func(a, b));
},

再度テストを実行すると,緑色のバーが表示されました.正しく実装できたようです.

テストのメリット

このようにテストを記述しながら実装をすることで,メソッドが正しく動作することが確認でき,実装に対して自信をつくのがテストの良いポイントです.

かけ算や割り算も同じように実装ができますし,たとえば不正な文字列が入った場合にどういう挙動をするべきか?スタックに何も入っていない状態で実行したらどういう結果が返るべきか?など,実際に起こり得る動作を考えながら作りこむと良いでしょう.

ソースコードを知る

OSSの世界ではソースコードから動作を知ることが可能ですが,Firefoxのような巨大なプロジェクトではソースコード閲覧ツールを使うのが一般的です.

Mozilla Cross-Reference

Mozilla Cross-Referece とは mozilla.org がホスティングをしているソースコードの全文検索サービスで, Linux のソースコードを閲覧する目的で作成された LXR (http://sourceforge.net/projects/lxr) を利用しています.

起動方法

次のURLにアクセスしてください。
http://lxr.mozilla.org/seamonkey/

左のメニューに検索の起点となるリンクやフォームが表示されています(図5).

図5:lxr.mozilla.org
[図5]

使用方法

何を調べるか

闇雲にソースコードを覗いたところで,何か発見があるというわけではありません.とりあえず,何を起点にするかを検討します.

仮に,リスト7のようなソースを見つけたとします.名前からは大体ファイルを扱っているものとはわかりますが,次のような疑問がでてきたとします.

リスト7:ソースコードの断片
var file = Components.classes['@mozilla.org/file/local;1']
        .createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(hoge);

これらの疑問を1つずつ調べていきます.

何をするのか?何を引数とするのか?

まずはコンポーネントの定義を探します.

Text Search の欄で 「interface nsILocalFile 」 と入力して検索をします.ここでのポイントは最後にスペースを入れている事です.

コンポーネントのほとんどは 「nsISupports」 を継承して作られています.そのため,スペースを入れて検索する事で継承の箇所が引っかかり,簡単に特定する事が可能になります(図6、図7).[▽注4

図6:スペースを入れない場合の検索結果
[図6]
図7:スペースを入れた場合の検索結果
[図7]

これでファイルが nsILocalFile.idl と特定できたので,それを表示してみます.各メソッドごとに説明が記述されています.

initWithPath では,nsILocalFile オブジェクトを初期化し,今までの情報もすべてリセットすると書かれています.また注意点として実体を示さないパスを与えると問題がある事が書かれています[▽注5].

引数としては,フルパスを指定する必要があり,相対パスではエラーとなると書かれています.

これでinitWithPath の仕様が理解できました.

実際の実装はどうなっているのか?

さて,実装についてはどのように調べれば良いでしょうか?まず nsILocalFile の 「I」を取り除き,その文字に先頭を大文字にした関数名をつけて検索をします.今回は「nsLocalFile::InitWithPath」 という文字で検索をします.

すると nsLocalFileWin.cpp など OS ごとのファイルが見つかります.試しに nsLocalFileWin.cpp を見ると,例えばドライブ名を示す 「:」 の有無などをチェックしているのがわかります.

また,WinCEとはネットワークパスの扱いを変えていたりするのもわかります.InitWithPath を見る限りでは,パス名が正しいかどうかのチェック程度のようです.

終わりに

本稿で紹介したツールの他にもさまざまなツールが提供されています.

もし,こんなツールがあったらなぁと思ったら,探すだけではなく本特集を参考に作ってしまうのも良いでしょう.本稿が拡張機能開発の一助になれば幸いです.

コラム

gonzui

gonzui[▽注A]はソースコードの全文検索エンジンで,Namazu の開発者として有名な高林哲氏が中心となり開発をしています.

インストール方法

Windows で利用する場合は, 松本宗太郎氏が Windows で単体で動作するようにした「gonzui for win32」を利用すると簡単に導入が可能です.
http://soutaro.com/gonzui-win32/index.ja.html

導入方法は以下の様に行います.

  1. gonzui-win32-1.2.2.zip をダウンロードして適当なフォルダへ展開する.
  2. Firefox のソースコードをダウンロードして,gonzuiを展開したフォルダ内に展開する(同じフォルダへ展開するのはファイルの指定が簡単なためです).
  3. コマンドプロンプトを起動して,gonzui のあるフォルダへ移動する.
  4. 次のコマンドを実行しソースコードをインポートする.
    > gonzui-import.exe mozilla
  5. インポートが完了したら以下のコマンドを実行して,gonzuiのサーバを起動する.
    > gonzui-server.exe

あとはWebブラウザを起動し次のURLを開きます.
http://localhost:46984/

そして,全パッケージ一覧のリンクを辿り,さらにmozillaのリンクを辿った箇所が検索の起点となります(図A).

図A:gonzui
[図A]
△注1http://quicknote.mozdev.org/
△注2http://www5f.biglobe.ne.jp/~t-susumu/
△注3RPNとは 逆ポーランド記法 と呼ばれる数式の記述方法です.たとえば 「(1 + 2) * (3 - 4)」という数式があるとします.この数式は逆ポーランド記法では「1 2 + 3 4 - *」のように記述します.カッコがなく,また日本語の流れに近い形で記述できるのが特徴です.また,プログラムが書きやすいのも特徴です.
△注4gonzuiではスペースを入れての検索では上手くいきませんので,高度な検索画面から件数を多めにして検索したのち,Webブラウザの検索機能で[ : ] を探しています.
△注5MacOSX などでフォルダ名を国際化したりするものではないかと思われますが,確証はありません.
△注Ahttp://gonzui.sorceforge.net