Hatena::ブログ(Diary)

Yet Another Hackadelic

2007-07-30 食生活を改善したい!

mozIJSSubScriptLoaderを使って現在の実行コンテキストに外部ファイルを読み込ませる

まぁこれ、識者の間ではどうも当たり前の模様です。

mozIJSSubScriptLoaderを使う

var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript(url, ctx);

とするとctxオブジェクトにwithした状態と同等で外部のソースを引っ張る事が出来ます。

ctxを省略すると、現在のコンテキストになります。

サンプル

/home/zigorou/Foo.jsなどに、

var Foo = function() {};
Foo.prototype = {
  name: function() { alert("ZIGOROu"); }
};

/home/zigorou/FooSubScript.jsなどに

function url() {
  alert("http://d.hatena.ne.jp/ZIGOROu/");
}

などとして、

loader.loadSubScript("file:///home/zigorou/Foo.js");
loader.loadSubScript("file:///home/zigorou/FooSubScript.js", Foo.prototype);

なんてやるとprototype拡張を行う事が出来ます。Foo.prototype.toSource()などで確認してみて下さい。prototypeにurlと言うfunctionが追加されているはずです。

何がメリットか

  • XPCOMをJSで作る際のデバッグのしやすさ(単発で記述して単発でテストとか出来そう)
  • ファイルの肥大化を防ぐ

とかですかね。

参考リンク

2007-06-19 スルー力鍛錬中

nsIWindowWatcherを使って全てのダイアログを捕捉する

相当ニッチな用途ですけど、

  • window.alert()
  • window.confirm()
  • window.open();
  • window.openDialog();
  • window.print();

などなど、特別なダイアログが開いた事を検出するにはnsIWindowWatcherを使うと出来るようです。

var wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
var observer = {
  observe: function(aSubject, aTopic, aData) {
    if (aTopic != "domwindowopend")
      return;

    aSubject.addEventListener("load", function() {
      aSubject.close();
    }, false);
  }
};

wwatch.registerNotification(observer);

のようにobserverを作って登録してあげると、alert等のダイアログが全てclose()されます。

ちなみに、

aSubject
ChromeWindow or Window
aTopic
イベント名
aData
恐らくnull

のようにobserverに渡される模様。

例えば、もう余り見ないけどalertとかのブラクラとかはこの手の記述である程度防げるんじゃないかなーと。

参考にしたのは、

辺りです。

2007-06-14 お腹空いた

ディレクトリ指定とOS判別

やっとXPConnect経由でXPCOM叩くのに慣れてきました。

var Cc = Components.classes;
var Ci = Components.interfaces;

だと思って読んで下さい。

replはMozReplです。

nsIPropertiesを利用したディレクトリの指定

no titleに詳しいのですが、

例えば、デフォルトのダウンロード先ディレクトリを参照させたければ、

var file = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("DfltDwld", Ci.nsIFile);
repl.print(file.path);

のようにして参照します。

しかしLinuxの場合はこれでは参照出来ません。Deskなどに変えて分岐処理が必要になります。

nsIXULRuntimeを利用したOSの判別

Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;

これでOSの識別子が返ってきます。

WindowsXP
WinNT
MacOSX
Darwin
FedoraCore 6
Linux

のように返って来ます。

2007-06-07 うーん

nsIWritableVariant, nsIWritablePropertyBag2について

最近XPCOMにハマってる*1訳ですけど、

いまいち分からん点があります。その前にタイトル通りnsIWritableVariantnsIWritablePropertyBag2について現状試した結果についてつらつらとメモしておきます。

その前にXPConnect/XPCOMの説明

JavaScriptから見たXPConnect(Componentsオブジェクト)

軽く試したいならばMozRepl必須です。

XPConnectはざっくり言えばJSとXPCOMの橋渡しをする仕組みで、JS側から見るとComponentsオブジェクトにその機能が集約されます。

repl> repl.print(Components);
[object nsXPCComponents]
repl> repl.inspect(Components);
<object>.interfaces=[object]
<object>.classes=[object]
<object>.results=[object]
<object>.ID=[function]
<object>.stack=[object]
<object>.utils=[object]
<object>.lookupMethod=[function]
<object>.isSuccessCode=[function]
<object>.QueryInterface=[function]
<object>.interfacesByID=[object]
<object>.classesByID=[object]
<object>.manager=[object]
<object>.Exception=[function]
<object>.Constructor=[function]
<object>.reportError=[function]

詳しくはMDC内のXPConnect辺りを見て下さい。

で特に頻繁に使うのがComponents.classes, Components.interfacesで、これらは大概、

const Cc = Components.classes;
const Ci = Components.interfaces;

なんて宣言をされている事が多いです。*2

class, interfaceってのは何かと言えば、XPCOMの概念でしてXPCOMのComponentってのは、

予め決められたidlで定めたinterfaceをclassと言う形で実装して行く事になってます。

まぁJavaっぽぃですね。ふんふん。

具体的には次で説明しまっす。

XPCOMとは何ぞや

MozillaなアプリにおいてC++によって拡張出来るコンポーネントシステムの事で、

他にもPerl, Ruby, Python, Javaでも拡張可能です。*3

またJavaScriptでも記述出来ます。

createInstance()でXPCOM Componentを使う

例えば今回話す予定のnsIWritableVariantを実装したクラスを使いたければ、@mozilla.org/hash-property-bag;1がそれを実装したクラスなので、

下記のように宣言します。

var aVariant = Cc["@mozilla.org/variant;1"].createInstance(Ci.nsIWritableVariant);

このcreateInstance()の引数中に実際に実装しているinterfaceのID*4を突っ込むとそのインターフェースで定義された型が使えるようになります。

ちなみにcreateInstanceの引数のiidをnsIWritableVariantの親インターフェースであるnsIVariant、さらには全ての基底インターフェスであるnsISupports、また省略した場合を試してみましょう。

repl> Cc["@mozilla.org/variant;1"].createInstance(Ci.nsIWritableVariant);
[xpconnect wrapped nsIWritableVariant]
repl> Cc["@mozilla.org/variant;1"].createInstance(Ci.nsIVariant);
[xpconnect wrapped nsIVariant]
repl> Cc["@mozilla.org/variant;1"].createInstance(Ci.nsISupports);
[xpconnect wrapped nsISupports]
repl> Cc["@mozilla.org/variant;1"].createInstance();
[xpconnect wrapped nsISupports]

このようになります。

getService(), QueryInterface()に関してはまた今度書くかも。

nsIWritableVariantを試してみる

そもそもこのnsIWritableVariantって汎用的に使える値なのかなと思ったんですが、

少なくともJS側で実際に入っている値が取得出来ない気がします。

と言うのも、

var _test = Cc["@mozilla.org/variable;1"].createInstance(Ci.nsIWritableVariant);
_test.setAsAString("ZIGOROu");

と突っ込んでもZIGOROuと言う値がどうしても取れない。。。親インターフェースの定義を見ると確かに各種getterがある物の、使おうとしても使えない。多分[noscript], [noxpcom][notxpcom]とかついてるせいだと思うんですけども。

用途と使い方がいまいち理解出来ない訳なのです。

こうして書いたら識者がヒントをくれる事を祈って書いてみるテスト。(ぇ

nsIWritablePropertyBag2を試す

似たような形でやってみましょう。

repl> var _test = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag2);
repl> _test
[xpconnect wrapped nsIWritablePropertyBag2]
repl> _test.setPropertyAsAString("name", "zigorou");
repl> _test.getPropertyAsAString("name");
zigorou

と言う感じできちんと値が取得出来ます。

そしてnsIWritablePropertyBag2の祖先インターフェースに当たるnsIPropertyBagにある、getProperty()も使ってみましょう。

repl> _test.getProperty("name");
zigorou

nsIVariantを返す事になってるはずなのに、普通に文字列として取れるじゃないですか。。。

ここで試しにQueryInterfaceを用いてnsIWritablePropertyBag*5に型を変えてみます。

repl> _test.QueryInterface(Ci.nsIWritablePropertyBag);
[xpconnect wrapped (nsISupports, nsIWritablePropertyBag2, nsIWritablePropertyBag)]
repl> _test.setProperty("company", "Cybozu Labs.");
repl> _test.getProperty("company");
Cybozu Labs.

これもvalueの受け渡しはnsIVariantなのに問題無く設定と取得が出来てる。謎だ。

次は敢えてnsIWritableVariantで文字列を突っ込んで同等の事をしてみる。

repl> var _val = Cc["@mozilla.org/variant;1"].createInstance(Ci.nsIWritableVariant);
repl> _val.setAsAString("xpcom");
repl> _test.setProperty("study", _val);
repl> _test.getProperty("study");
[xpconnect wrapped nsIWritableVariant]

うむ、仕様通りに返って来た。でも中身に入れたxpcomが参照出来ない事実は変わらず。

と言う訳で

多分idlで定義してあるnoscript, noxpcomnotxpcomと言う指定の意味が分かればそれで良いとは思うんだけど、

もしご存知の方が居れば是非アドバイス下さい。

追記

404: Page Not Found ? Mozillaが一番詳しいかなぁ。

notxpcomは規則違反みたいな感じで書かれてる。ますます謎だ。

*1:楽しんでるし、苦しんでるの両方の意味

*2:MozReplではrepl.Cc, repl.Ciがそれ相当

*3:Perlでの拡張の話はいつか書く予定

*4:iidって書く場合が多いみたいです

*5:今まで使ってたのはnsIWritablePropertyBag2

2007-05-30

XULRunnerのビルドとxpidlのテスト

自分で拡張を作る際に、好き勝手にglobalな領域を侵すのも当然自由ではある物の、ある程度の汎用性を持たせたcomponentを開発しようと思ったらやはりXPCOM componentにしないとと言うことで、おもむろに調べて確かめてみた事のメモです。

でXPCOMはJavaScript/C++で記述出来るので、今回はJavaScriptで記述する場合の話です。

但し激しくIntelMacの話です。w

始めに

XPCOM componentって何かと言えば、Components.classesで呼び出せる奴です。

詳細については僕もまだ良く分からないので、ここらへんから情報を漁りましょう。

でXPCOM componentの開発の仕方ですが、

  • idlを定義する
  • xpidlコマンドを使ってxptと言うバイナリ形式のファイルを作る
  • idlで定義したインターフェースをjsで実装する

と言う手順になるようです。

今回はxpidlコマンドがあるsdkのビルドと、xpidlを使ってidlファイルをxptファイルに変換するまでです。

XULRunnerのビルド

で、何故XULRunnerなのかと言えば、

  • IntelMacだとsdkは自分でビルドしなきゃダメ(via no title)
  • no titleによれば、XULRunnderをビルドすると割と簡単との事

と言う訳なので、XULRunnerのビルドをしてみます。

XULRunnerのソースコードを持って来る

no titleを見てソースを持って来ましょう。

$ wget http://ftp.mozilla.org/pub/mozilla.org/xulrunner/releases/1.8.0.4/source/xulrunner-1.8.0.4-source.tar.bz2
$ tar xjf xulrunner-1.8.0.4-source.tar.bz2
$ cd mozilla

解凍したらmozillaディレクトリが出来るのでそこまではとりあえず移動しておきます。

mozconfigを持って来る

no titleによれば、.mozconfigによってconfigureの結果が変わるそうなので、仕方ないので指示通りに。

$ cp xulrunner/config/mozconfig .mozconfig
ビルド
$ ./configure

ここでlibIDLが無いよって怒られる人が居るかもしれないので、MacPortで

$ sudo port install libidl

とかやってあげれば多分上手く行くかと。

あとはいつも通り、

$ make 
$ sudo make install

でインストール自体は完了。*1

試してみよう

sdk自体はビルドしたディレクトリから見て dist/sdk 以下にあります。

今回はMozLabの中のcomponentディレクトリにあるMozRepl.idlをxptに変換してみます。

$ mv MozRepl.xpt MozRepl.xpt.back
$ /usr/local/src/mozilla/dist/sdk/bin/xpidl -m typelib -w -v -I /usr/local/src/mozilla/dist/sdk/idl -e MozRepl.xpt MozRepl.idl

これで生成されているはず。

$ md5 MozRepl.xpt
MD5 (MozRepl.xpt) = 1c63dca76b1174697a0443d2439f9928
$ md5 MozRepl.xpt.back 
MD5 (MozRepl.xpt.back) = 1c63dca76b1174697a0443d2439f9928

うむ。出来たらしい。

まとめ

  • インストールはめんどい
  • IDLの書き方も覚えるの面倒だし、component化する際にjsの方も長々と記述が必要な模様。(factory作ったりとか)

とは言え、迂闊にglobal空間を汚染するのもなぁ。。。

と言うかMozilla上の汎用化されたライブラリ群ってXPCOMしか無いのかな。良く分からん。

追記

no titleに割とまとまってた。。。

Mozillaの情報って氾濫しまくりんぐ。orz...

*1:もの凄い時間掛かる