これまでの章で,XUL,JavaScript,CSS,XPCOM といった技術について解説しました.本章ではこれらの技術を組み合わせ,実際に拡張機能開発を行います.
「拡張機能開発応用編」では,Firefox 2の新機能の1つであるセッションストアAPIを利用し,ユーザが好きなタイミングでセッション(ブラウザウィンドウの状態)を保存したり復元したりすることができる実用的な拡張機能を開発します.
セッション管理拡張機能のGUIを図12に示します.「Session Store」メニュー内には,現在のセッションを保存するための「Save Session」,現在保存されているすべてのセッションを削除するための「Clear Sessions」の2つのメニュー項目があり,両者の間には現在保存されているセッションを復元するためのメニュー項目が新しいものから順番に配置されています.
コラム「セッションストアAPI」にあるように,セッションはJSON文字列として表されます.セッション管理拡張機能では,プロファイルフォルダ下にセッション保存用のフォルダ(フォルダ名はsessionstore)を作り,その中に個々のセッションをテキストファイルとして保存します(図13).
フェーズ①でのフォルダ構成を図14に示します.
作業用フォルダを作成し,インストールマニフェストをリスト1を参考に作成してください.今回はem:id を「[email protected]」,em:name を「Session Store」とします.
リスト2を参考にしてクロムマニフェストを作成してください.
オーバーレイのマージポイントおよび要素の追加位置は,Helloworld拡張機能と同じです.
ブラウザウィンドウへオーバーレイさせるoverlay.xulをリスト14のように記述してください.
<?xml version="1.0"?> <overlay id="sessionstoreOverlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script type="application/x-javascript" src="chrome://sessionstore/content/overlay.js" /> <menupopup id="menu_ToolsPopup"> <menu label="Session Store" insertbefore="sanitizeSeparator"> <menupopup> <menuitem label="Save Session" /> <menuseparator /> <!-- ここへmenuitemを動的に生成して配置する --> <menuseparator /> <menuitem label="Clear Sessions" /> </menupopup> </menu> </menupopup> </overlay>
「拡張機能開発基礎編」で行ったようにポインタファイルを配置してテストインストールします.「ツール」メニューへ「Session Store」メニューが追加されるはずです.
フェーズ②では,JavaScriptによってセッションストアAPIを利用したセッション保存や復元機能の実装をします.
機能を実装するにあたって必要な処理ごとにメソッド名や変数名を決め,overlay.jsへリスト15のように記述します.
var gSessionStore = { // セッション保存用フォルダ(nsILocalFile) _dir: null, // 初期化処理 init: function() { }, // 終了処理 uninit: function() { }, // セッションを保存する(イベントハンドラ) save: function(event) { }, // セッションを復元する(イベントハンドラ) restore: function(event) { }, // セッションを削除する(イベントハンドラ) clear: function(event) { }, // メニュー項目を動的に生成する(イベントハンドラ) createMenu: function(event) { }, // ファイルを読み込む _readFile: function(aFile) { }, // ファイルへ書き込む _writeFile: function(aFile, aData) { }, }; window.addEventListener("load", function(){ gSessionStore.init(); }, false); window.addEventListener("unload", function(){ gSessionStore.uninit(); }, false);
リスト15では,いろいろなメソッドや変数をgSessionStoreという1個のオブジェクトのプロパティとしてまとめて定義していることに着目してください.拡張機能でブラウザウィンドウへオーバーレイする際に気を付けたいことは,browser.xulへオーバーレイさせるXULから読み込まれるJavaScript内で定義したグローバル変数や関数は,すべてbrowser.xulのwindowオブジェクトのプロパティとなることです.したがって,browser.xulのwindowオブジェクトのプロパティには,Firefox 自身や色々な拡張機能によって定義された変数や関数が多く混在した状態となります.独立性を高めて拡張機能どうしの衝突が起きにくくするためには,上記のように1個のオブジェクトにまとめるか,あるいはクラスを定義すると良いでしょう.
拡張機能では,ブラウザウィンドウを開いたときに何らかの初期化処理を実行したいケースがよくあります.そのようなときは,windowオブジェクトをターゲットとしてload イベントに対するイベントリスナを追加します.リスト15ではウィンドウを開いたときにinitメソッドを実行し,ウィンドウを閉じたときにuninitメソッドを実行します.
overlay.jsで定義した4つのイベントハンドラをoverlay.xulへ追加します(リスト16).ここではイベントのバブリング(▽注10)を利用し,menuitemが並ぶ階層よりも1つ上のmenupopup要素にrestoreイベントハンドラを追加しています.それに伴い,save,restore の両イベントハンドラの処理内で,イベントのバブリングを阻止する必要があります.
<menupopup onpopupshowing="gSessionStore.createMenu(event);" oncommand="gSessionStore.restore(event);"> <menuitem label="Save Session" oncommand="gSessionStore.save(event);" /> <menuseparator /> <!-- ここへmenuitemを動的に生成して配置する --> <menuseparator /> <menuitem label="Clear Sessions" oncommand="gSessionStore.clear(event);" /> </menupopup>
それでは,gSessionStoreオブジェクトに定義された各メソッドを1つずつ実装していきましょう.なお,誌面の都合上,すべてのソースコードは掲載することができません.下記のURL にて,掲載できなかったソースコードをご覧になることができます.
http://www.xuldev.org/samples/sd/
initメソッドをリスト17のように記述します.initメソッドでは,まずプロファイルフォルダの中にある「sessionstore」フォルダをnsILocalFile形式で取得し,今後使いまわしができるように変数_dirとして保持します(3~6行目).もしフォルダが存在しなければ作成します(7~8行目)(▽注11).
1: init: function() 2: { 3: var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"] 4: .getService(Components.interfaces.nsIProperties); 5: this._dir = dirSvc.get("ProfD", Components.interfaces.nsILocalFile); 6: this._dir.append("sessionstore"); 7: if (!this._dir.exists()) 8: this._dir.create(this._dir.DIRECTORY_TYPE, 0700); 9: },
uninitメソッドをリスト18のように記述します.initメソッドで生成した変数_dirを破棄するだけです.この処理は実際には意味がありません.
1: uninit: function() 2: { 3: this._dir = null; 4: },
saveメソッドをリスト19のように記述してください.3行目は,イベントのバブリングによってmenuitem要素の親のmenupopup要素のoncommandイベントハンドラが呼び出されてしまうのを防ぐための処理です.4~6行目で,nsISessionStoreインターフェースのgetBrowserStateメソッドを使い,現在開いているすべてのウィンドウの状態を表すJSON文字列を取得します.7~9行目で,_dirプロパティから複製することによって保存先ファイルのnsIFileオブジェクトを生成します.10行目で,後述する_writeFileメソッドを使ってセッションを表すJSON文字列をファイルへ書き込みます.
1: save: function(event) 2: { 3: event.stopPropagation(); 4: var ss = Components.classes["@mozilla.org/browser/sessionstore;1"] 5: .getService(Components.interfaces.nsISessionStore); 6: var state = ss.getBrowserState(); 7: var fileName = "session_" + Date.now() + ".js"; 8: var file = this._dir.clone(); 9: file.append(fileName); 10: this._writeFile(file, state); 11: },
後述するcreateMenuメソッドによって動的に生成されたメニュー項目に対するイベントハンドラです(リスト20).3~6行目で,createMenuメソッドにて付加された独自の属性「fileName」からファイル名を取得し,後述する_readFileメソッドによってファイルからJSON文字列を読み込みます.7~9行目で,nsISessionStoreインターフェースのsetWindowStateメソッドを使って,現在のウィンドウを基点にセッション復元を実行します.その際,現在開いているタブを上書きしてセッション復元します.
1: restore: function(event) 2: { 3: var fileName = event.target.getAttribute("fileName"); 4: var file = this._dir.clone(); 5: file.append(fileName); 6: var state = this._readFile(file); 7: var ss = Components.classes["@mozilla.org/browser/sessionstore;1"] 8: .getService(Components.interfaces.nsISessionStore); 9: ss.setWindowState(window, state, false); 10: },
本特集4章にて説明した「フォルダの辿り方」を参考に,nsIFileインターフェースのdirectoryEntriesプロパティを使って取得したフォルダ内の全ファイルを削除します.
引数で渡したnsIFile型オブジェクトのファイルを読み込み,その内容を文字列として返します.また,ファイルを読み込んだ際に,文字コードをUTF-8からUnicodeへ変換します.
引数で渡したnsIFile型オブジェクトのファイルを新規作成し,引数で渡した文字列を書き込みます.また,書き込む際に文字コードをUnicodeからUTF-8へ変換します.
「Session Store」メニューのポップアップを開くタイミングで呼び出されるイベントハンドラです.まず,前回ポップアップを開いたときに動的に生成したまま残っているmenuitem要素を削除します.その後,セッション保存先フォルダ内の全ファイル名を元に,menuitem要素を動的に生成して挿入しています.
ここまでで機能の実装は完了です.新しいウィンドウを開き,セッション保存,セッション復元,セッション削除の3つの機能が正常に動作するか確認してください.
拡張機能の設定をユーザが変更可能にするために,iniファイルやレジストリなどを利用する必要はありません.Firefoxには設定値を読み書きするための便利なXPCOMサービスとGUIの部品が揃っています.フェーズ③ではこれらを実際に使います(図15).
今回紹介したnsISessionStore インターフェースについては,幸いMozilla Development Center に詳しいドキュメントがあります(▽注A).しかし,一般的には,XPCOMのインターフェースの詳細(IDL)は,XULPlanet(▽注B)Mozilla Cross-Reference(▽注C)でのソースコード全文検索にて見つけることができます.
△注A: | 「nsISessionStore - MDC」(https://developer.mozilla.org/ja/ |
△注B: | 「XULPlanet」(http://www.xulplanet.com/) |
△注C: | 「Mozilla Cross-Reference」(http://mxr.mozilla.org/) |
フェーズ③でのフォルダ構成を図16に示します.また,フェーズ③で新たに作成するファイルの役割を,表5に示します.
ファイル名 | 役割 |
---|---|
sessionstore-prefs.js | デフォルトの設定値を定めたファイル. 必ずdefaults\preferencesフォルダへ配置してください. |
prefs.xul | 設定パネル |
まず,追加する設定の設定名/型/値といった仕様を決めます(表6).設定名は,拡張機能固有の接頭辞(ここでは「extensions.sessionstore.」)を付加しましょう.
設定名 | 型 | 値 |
---|---|---|
extensions.sessionstore .warnOnClear | 真偽値 |
セッション削除時の動作。 true: 確認ダイアログを表示してから実行する false: すぐに実行する |
extensins.sessionstore .replaceTabs | 数値 |
セッション復元時の動作。 0: 現在のタブを残す 1: 現在のタブへ上書きする 2: 毎回確認する |
defaults¥preferences¥ フォルダ内のsessionstoreprefs.jsへ,表6の設定値およびデフォルト値の定義を記述します(リスト21).
pref("extensions.sessionstore.warnOnClear", true); pref("extensions.sessionstore.replaceTabs", 2);
prefs.xulへリスト22のように記述してください.prefwindow要素は設定パネルを作るための特殊なルート要素で,その下にprefpane要素を配置することができます.prefpane要素を複数配置すると,Firefoxのオプションウィンドウのように,ボタンを押してパネルを切り替えることができるGUIを作ることができます.preference要素は,設定パネルによって読み書きする設定値で,name属性が設定名,type属性が型となります.また,preference要素のidと,checkboxやradiogroupのpreference属性とを一致させることで,チェックボックスのオンオフやラジオグループの選択によって設定値を変更できるようになります.インストールマニフェストへリスト23の内容を追加し,アドオンマネージャから設定パネルを開くことができるようにします.
<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <prefwindow id="sessionstorePrefs" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" title="Session Store Preferences"> <prefpane> <preferences> <preference id="extensions.sessionstore.warnOnClear" name="extensions.sessionstore.warnOnClear" type="bool" /> <preference id="extensions.sessionstore.replaceTabs" name="extensions.sessionstore.replaceTabs" type="int" /> </preferences> <checkbox label="Confirm before clearing sessions" preference="extensions.sessionstore.warnOnClear" /> <groupbox> <caption label="When restoring session:" /> <radiogroup preference="extensions.sessionstore.replaceTabs"> <radio value="0" label="Keep current tabs" /> <radio value="1" label="Replace current tabs" /> <radio value="2" label="Ask me every time" /> </radiogroup> </groupbox> </prefpane> </prefwindow>
<em:optionsURL>chrome://sessionstore/content/prefs.xul</em:optionsURL>
設定値によってはセッションを削除しようとしたときに確認ダイアログを表示させるように処理を追加します(リスト24).JavaScriptから設定値を取得するには,XPCOMサービスであるnsIPrefServiceインターフェースを利用し,設定値の型に合わせてgetBoolPref,getIntPrefなどのメソッドを使います.リスト24では,設定名が接頭辞「extensions.sessionstore.」で始まるすべての設定値を読み書きするためのnsIPrefBranchオブジェクトを取得してから,getBoolPrefメソッドで設定値の取得をしています.
var prefSvc = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService); var prefBranch = prefSvc.getBranch("extensions.sessionstore."); if (prefBranch.getBoolPref("warnOnClear") == true) { if (window.confirm("Really?") == false) return; }
今回は,インストールマニフェストを修正したので,拡張機能を再インストールする必要があります(コラム「修正したソースファイルの動作を確認するには」参照).再インストールしたら,まず「about:config」を開き,表6の設定値が既定値で設定されていることを確認します.次に,アドオンマネージャから「Session Store」を選択し,「設定」ボタンをクリックして設定パネルが表示されることを確認します.さらに,設定を変更することでセッション復元時などの動作が変化することを確認します.
手順は「拡張機能開発基礎編」のフェーズ⑤と変わりありませんが,defaultsフォルダ以下も含める点に気を付けてください.
本章では拡張機能開発のごく基本的な部分を解説しましたが,拡張機能やXULにはまだまだ多くの魅力的な機能があります.この機会にぜひ拡張機能開発の世界へと足を踏み入れてみてはいかがでしょうか.
△注10: | ある要素上で発生したイベントが,その親要素からルート要素までを伝播していくしくみを,イベントのバブリングと言います. |
△注11: | プロファイルフォルダのような特殊なフォルダを取得する方法については,「Code snippets:File I/O - MDC」(https://developer.mozilla.org/En/Code_snippets:File_I/O#Getting_special_files)を参照してください. |