本稿では拡張機能の開発を支援するツールを紹介します.
Venkman は Firefox 上で動作する本格的な Javascript デバッガです.一般的なデバッガの操作を網羅しており,拡張機能開発のみではなく Web 開発にも利用することが可能です.
また,拡張機能やFirefox内部でJavascriptが使われているため,デバッガを通して設計方法を学ぶ事ができます.
Mozilla Suite や現在の SeaMonkey には本体に付属していましたが,Firefox では拡張機能としてインストールをする事で利用可能になっています.
https://addons.mozilla.org/ja/firefox/addon/216
上記Webサイトから拡張機能をインストールして Firefox を再起動することで利用可能になります.
ツールから 「Javascript Debugger」 を選択します.
Venkman の初期起動時は,Webブラウザ内のファイルのみがデバッグ可能となっています.
これは,多くのJavascriptファイルを読み込むと動作が遅くなるためです.
拡張機能のデバッグの際は,この制限を解く必要があります.「Debug」 メニュー の 「Exclude Browser File」 のチェックを外します(図1).
今回は使用例として QuickNote [▽注1]のファイル保存の仕組みをデバッガでたどってみます.
下記URLから QuickNote をインストールして,Firefoxを再起動します.
https://addons.mozilla.org/firefox/46/
Firefox の起動後に QuickNote を起動します.
その後,[ツール] - [QuickNote] - [Float] を選択して,単独のWindow として表示します.
次に Venkman を起動し,画面左上の [Open Windows] - [quicknote.xul] - [Files] と開き, quicknote.js を選択してダブルクリックして, Source Code 画面に読みこみます(図2).
まずは,ブレークポイントを設置を行います.354行目付近にある QuickNote_saveNote 関数内にブレークポイントを設置してください(図3).
QuickNote の画面にて適当に文字を入力して,メニューから[Save Current Tab] を選択します.
すると,Venkman でブレークポイントを設置した箇所でプログラムが停止して,デバッグが開始されます.
Local Variables の画面ではステップ実行時の変数やオブジェクトがつねに表示されています.ただし,変数が多い関数などでは見づらくなりますので,必要な変数を監視対象に追加します(図4).
監視対象に追加した変数は, Watches タブに入り,評価が行なわれるごとに更新されていきます.
あとはステップ実行をしつつ,プログラムや変数の動きを見ていきます.
調査が終わったら, Continue を実行して止まっていたプログラムを実行させます.
筆者の場合ほどんど拡張機能開発を行なっていないため,ほぼいろいろな拡張機能の実装を調べるのに使っています.QuickNote の例でもあるように,まず1ヶ所ブレークポイントを設置し,あとはひたすらステップ実行をしていきます.
Step Over はなるべく使わず,Step Into で必ず呼びだしている関数の中に入っていき,関数の調査がひととおり終わったらStep Out をして,また Step Into で次の関数へということを繰り返していきます.
この時に気になる点やよく理解できなかった点があればブレークポイントを設置しておき,次の動作の際には Continue でその箇所まで移動して再度調査します.
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
パラメータ | 意味 |
---|---|
%f | ファイル名 |
%l | 行番号 |
%c | カラム番号 |
筆者の場合はMeadowを利用しており,gnuserv を利用して開くようにしています.
C:¥app¥Meadow¥bin¥gnuclientw.exe +%l:%c %f
なお,今回テストした限りでは, Program Files にあるエディタでは利用ができませんでした.空白があるファイル名はなるべく利用しない方がよいでしょう.
今回は簡単なRPN計算機[▽注3]をJavaScriptで作成する過程から,どのように利用するかを見ていきます.
プログラム側では RpnCalc というクラスを作成します.まずは簡単なインターフェイスだけを記述したものを用意しておきます(リスト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).
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 の実装を開始します.今回はリスト3のように実装しました.
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のメソッドを追加します.
'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 を開いてリスト5のメソッドを追加します.
minus: function() { this._letfunc(this._minus); }, _minus: function(a, b) { return a - b; },
これでテストを実行すると赤いバーが表示されました.どこが問題であったのでしょうか?
画面には「Expected -1 , got 1 .」と表示されています.どうやら pop() で取ってきた順番と関数の引数の順番がおかしくなっていたようです._letfunc関数をリスト6のように修正します.
_letfunc: function(func) { // pop する順番を訂正 b = this.pop(); a = this.pop(); this.push(func(a, b)); },
再度テストを実行すると,緑色のバーが表示されました.正しく実装できたようです.
このようにテストを記述しながら実装をすることで,メソッドが正しく動作することが確認でき,実装に対して自信をつくのがテストの良いポイントです.
かけ算や割り算も同じように実装ができますし,たとえば不正な文字列が入った場合にどういう挙動をするべきか?スタックに何も入っていない状態で実行したらどういう結果が返るべきか?など,実際に起こり得る動作を考えながら作りこむと良いでしょう.
OSSの世界ではソースコードから動作を知ることが可能ですが,Firefoxのような巨大なプロジェクトではソースコード閲覧ツールを使うのが一般的です.
Mozilla Cross-Referece とは mozilla.org がホスティングをしているソースコードの全文検索サービスで, Linux のソースコードを閲覧する目的で作成された LXR (http://sourceforge.net/projects/lxr) を利用しています.
次のURLにアクセスしてください。
http://lxr.mozilla.org/seamonkey/
左のメニューに検索の起点となるリンクやフォームが表示されています(図5).
闇雲にソースコードを覗いたところで,何か発見があるというわけではありません.とりあえず,何を起点にするかを検討します.
仮に,リスト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]
これでファイルが nsILocalFile.idl と特定できたので,それを表示してみます.各メソッドごとに説明が記述されています.
initWithPath では,nsILocalFile オブジェクトを初期化し,今までの情報もすべてリセットすると書かれています.また注意点として実体を示さないパスを与えると問題がある事が書かれています[▽注5].
引数としては,フルパスを指定する必要があり,相対パスではエラーとなると書かれています.
これでinitWithPath の仕様が理解できました.
さて,実装についてはどのように調べれば良いでしょうか?まず nsILocalFile の 「I」を取り除き,その文字に先頭を大文字にした関数名をつけて検索をします.今回は「nsLocalFile::InitWithPath」 という文字で検索をします.
すると nsLocalFileWin.cpp など OS ごとのファイルが見つかります.試しに nsLocalFileWin.cpp を見ると,例えばドライブ名を示す 「:」 の有無などをチェックしているのがわかります.
また,WinCEとはネットワークパスの扱いを変えていたりするのもわかります.InitWithPath を見る限りでは,パス名が正しいかどうかのチェック程度のようです.
本稿で紹介したツールの他にもさまざまなツールが提供されています.
もし,こんなツールがあったらなぁと思ったら,探すだけではなく本特集を参考に作ってしまうのも良いでしょう.本稿が拡張機能開発の一助になれば幸いです.
gonzui[▽注A]はソースコードの全文検索エンジンで,Namazu の開発者として有名な高林哲氏が中心となり開発をしています.
Windows で利用する場合は, 松本宗太郎氏が Windows で単体で動作するようにした「gonzui for win32」を利用すると簡単に導入が可能です.
http://soutaro.com/gonzui-win32/index.ja.html
導入方法は以下の様に行います.
あとはWebブラウザを起動し次のURLを開きます.
http://localhost:46984/
そして,全パッケージ一覧のリンクを辿り,さらにmozillaのリンクを辿った箇所が検索の起点となります(図A).
△注1: | http://quicknote.mozdev.org/ |
△注2: | http://www5f.biglobe.ne.jp/~t-susumu/ |
△注3: | RPNとは 逆ポーランド記法 と呼ばれる数式の記述方法です.たとえば 「(1 + 2) * (3 - 4)」という数式があるとします.この数式は逆ポーランド記法では「1 2 + 3 4 - *」のように記述します.カッコがなく,また日本語の流れに近い形で記述できるのが特徴です.また,プログラムが書きやすいのも特徴です. |
△注4: | gonzuiではスペースを入れての検索では上手くいきませんので,高度な検索画面から件数を多めにして検索したのち,Webブラウザの検索機能で[ : ] を探しています. |
△注5: | MacOSX などでフォルダ名を国際化したりするものではないかと思われますが,確証はありません. |
△注A: | http://gonzui.sorceforge.net |