Hatena::Groupmoz-addon

Ci.nsIZIGOROu

2008-05-28

Components.utils.getWeakReference() がウマーな件

| 19:13 |  Components.utils.getWeakReference() がウマーな件 - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Components.utils.getWeakReference() がウマーな件 - Ci.nsIZIGOROu

JS XPCOM などで呼び出し元の JS のコンテキストに対して、Object 型の値を返すとか、あるいは Object 型をそのまま渡すとかっていう事は原則として出来ませんでした。

Firefox 3からある Components.utils.getWeakReference() を使うとそれが出来るようになります。

nsIObserverService を使った例

例えば次のようなソースを用意して実行します。(どこかで)

var EXPORTED_SYMBOLS = ["testObserval"];

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

var testObserval = (function() {
  var observerService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  var constructor = function() {
  };

  var TEST_TOPIC = "test-observer-notify";

  constructor.prototype = {
	notify: function(aData) {
	  observerService.notifyObservers({ foo: 1 }, TEST_TOPIC, aData);
	},
	notifyJSObject: function(aData) {
	  observerService.notifyObservers(Components.utils.getWeakReference({ foo: 1 }), TEST_TOPIC, aData);
	},
	register: function(aObserver) {
	  observerService.addObserver(aObserver, TEST_TOPIC, false);
	},
	remove: function(aObserver) {
	  observerService.removeObserver(aObserver, TEST_TOPIC);
	}
  };

  return new constructor();
})();

これは Components.utils.import で読み込んで下さい。JS XPCOM 相当と言う事を確認する為です。

次に呼び出し側の ChromeWindow の JS のコンテキストで、

var testObserver = (function() {
  var constructor = function() {
	this.subject = null;
	this.topic = "";
	this.data = "";
  };

  constructor.prototype = {
	logger: Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService),
	observe: function(aSubject, aTopic, aData) {
	  this.log(aTopic);
	  this.subject = aSubject;
	  this.topic = aTopic;
	  this.data = aData;
	},
	interfaces: function() {
	  var ifaces = [];
	  for (var iface in Ci) {
		if (this.subject instanceof Ci[iface]) {
		  ifaces.push(iface);
		}
	  }
	  return ifaces;
	},
	log: function(msg) {
	  this.logger.logStringMessage(msg);
	}
  };

  return new constructor();
})();

を定義しておきます。

次に testObserval の register で testObserver を登録します。

testObserval.register(testObserver);

次に普通にnotifyしてみます。

testObserval.notify("bar");

中では無理矢理 JSObject 型を subject としています。

testObserver.interfaces().toString(); // nsISupports 

なので、これ以上何も出来ません><

getWeakReference を使うとどうなるか

今度は testObserval.notifyJSObject を代わりに使ってみます。

testObserval.notifyJSObject("bar");

同じように実装しているインターフェースを見てみます。

testObserver.interfaces().toString(); // xpcIJSWeakReference,nsISupports

xpcIJSWeakReference が実装されたオブジェクトが返ってきます。

従って subject の QueryInterface() でキャストして、ここで使えるget()メソッドを呼び出してみると、

testObserver.subject.QueryInterface(Ci.xpcIJSWeakReference).get().foo; // 1

のようにぶち込んだ Objectプロパティアクセス出来ます。

まとめ

今まで XPCOM 的なプリミティブな値か XPCOM オブジェクトしか XPConnect 経由で渡せなかったけど、Componsnts.utils.getWeakReference() を使えば、JSオブジェクトそのままで渡す事が可能になります。

すげー。

呼び出し元では QueryInterface() して get() と言う余計な手続きはありますが、そのまま扱えるのは非常に大きなメリットだなと。この辺りはgetter/setterとかで何とかしちゃえば良さそうだし。

追記 (2008-06-01T02:01:08+09:00)

d:id:fls さんから教えて頂いた wrappedJSObject なら全然問題無いとの事でした。ほぉー。

piro_orpiro_or2008/05/28 19:53要点だけ抜き出すと

var observerService = Cc["@mozilla.org/observer-service;1"]
    .getService(Ci.nsIObserverService);

// XPCOMのインターフェースを通と生のJSオブジェクトにアクセスできない
var observer = {
observe : function(aSubject, aTopic, aData) {
alert(aSubject.foo); // undefined
}
};
observerService.addObserver(observer, 'test-topic', false);
observerService.notifyObservers(
{ foo : 1 },
'test-topic',
null
);
observerService.removeObserver(observer, 'test-topic');

// XPCOMのインターフェース越しで生のJSオブジェクトにアクセスする
var observer = {
observe : function(aSubject, aTopic, aData) {
aSubject = aSubject.QueryInterface(Ci.xpcIJSWeakReference).get(); //****追加****
alert(aSubject.foo); // 1
}
};
observerService.addObserver(observer, 'test-topic', false);
observerService.notifyObservers(
Components.utils.getWeakReference( //****追加****
{ foo : 1 }
), //****追加****
'test-topic',
null
);
observerService.removeObserver(observer, 'test-topic');

てことでしょうか?
第1引数のsubjectはあくまでsubject(主語)としてのみ使うものだ、という思い込みがあったので、こういう使い方は思いつきませんでしたね……

ZIGOROuZIGOROu2008/05/29 11:11> てことでしょうか?

て事ですね。

> 第1引数のsubjectはあくまでsubject(主語)としてのみ使うものだ

第一引数のsubjectを何に使うべきかと言う議論もあるかもしれませんが、言いたい事はそこじゃなくて、xpcIJSWeakReferenceとしてやりとりすれば素のJSオブジェクトでやり取り出来るという事だけですね。

flsfls2008/05/30 21:00wrappedJSObject を使えば簡単にJSオブジェクトでやり取りできますよ

function SubjectWrapper(aObject){
this.wrappedJSObject = aObject;
}

var observer = {
observe : function(aSubject, aTopic, aData) {
alert(aSubject.wrappedJSObject.foo);
}
};

observerService.addObserver(observer, 'test-topic', false);
observerService.notifyObservers(new SubjectWrapper({ foo:1 }), 'test-topic', null);
observerService.removeObserver(observer, 'test-topic');

ZIGOROuZIGOROu2008/06/01 01:57> d:id:fls さん

うへw 知らなかった><
ありがとうございます。勉強になります。

NanaNana2012/07/23 13:02This is an article that makes you think "never touhght of that!"

ivdpvghivdpvgh2012/07/23 22:06z9fRrF <a href="http://dhhlfemkguyw.com/">dhhlfemkguyw</a>

tbsgzgtbsgzg2012/07/24 11:43Ynms0P , [url=http://tvzpvwukgsei.com/]tvzpvwukgsei[/url], [link=http://qdfuftfcfvqc.com/]qdfuftfcfvqc[/link], http://ptogkdwzynow.com/

htuuvzjhtuuvzj2012/07/25 22:35yAH5UE <a href="http://pwhtacsynlwv.com/">pwhtacsynlwv</a>

bekljzllabekljzlla2012/07/26 22:591z5SBX , [url=http://irtffefggpcq.com/]irtffefggpcq[/url], [link=http://zchnxrdznina.com/]zchnxrdznina[/link], http://zgnrqcekjqgd.com/