Hatena::Groupmoz-addon

Ci.nsIZIGOROu

2008-05-23

Components.utils.import をエミュレート

| 16:26 |  Components.utils.import をエミュレート - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Components.utils.import をエミュレート - Ci.nsIZIGOROu

注意!

本物とだいぶ挙動が違うし、実現不可能だと分かったので参考にしないで下さい。

ソース

こんな感じかねぇ?

(function() {
   var nativeComponents = Components;

   var XPCComponents_Utils_ImportInternal = function() {
	 this.loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
	 this.ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
   };

   XPCComponents_Utils_ImportInternal.prototype = {
	 cache: {},
	 import: function(uriSpec, jsObj) {
	   var uri = this.ios.newURI(uriSpec, null, null);
	   try {
		 if (uri.scheme != "resource" && uri.scheme != "file") {
		   throw new Error("NS_ERROR_ILLEGAL_VALUE");
		 }

		 var exports = this.cache[uriSpec];

		 if (!exports) {
		   exports = {};
		   this.loader.loadSubScript(uriSpec, exports);
		 }

		 if (exports.EXPORTED_SYMBOLS) {
		   var ex_symbol;
		   for (var i = 0, l = exports.EXPORTED_SYMBOLS.length; i < l; i++) {
			 ex_symbol = exports.EXPORTED_SYMBOLS[i];
			 jsObj[ex_symbol] = exports[ex_symbol];
		   }
		 }
	   }
	   catch (e) {
		 throw e;
	   }
	   return;
	 }
   };

   var internal = new XPCComponents_Utils_ImportInternal();
   var XPCComponents_Utils_Wrapper = function() {};
   XPCComponents_Utils_Wrapper.prototype.import = function(uriSpec, jsObj) { internal.import(uriSpec, jsObj); };

   XPCComponents_Utils_Wrapper.prototype.toString = function() { return "[object nsXPCComponents]"; };

   var prop;
   for (prop in nativeComponents.utils)
	 XPCComponents_Utils_Wrapper.prototype[prop] = nativeComponents.utils[prop];
   var XPCComponents_Wrapper = function() { };

   for (prop in nativeComponents)
	 if (prop != "utils")
	   XPCComponents_Wrapper.prototype[prop] = nativeComponents[prop];

   XPCComponents_Wrapper.prototype.utils = new XPCComponents_Utils_Wrapper();
   XPCComponents_Wrapper.prototype.toString = function() { return "[object nsXPCComponents]"; };

   Components = new XPCComponents_Wrapper();
})();

問題点

  • Componentsごとまるっと変えちゃう所、危ない
  • importの挙動が本当に合ってるかどうか

こういう解法よりも、wrapper作って切り替えのがいいんだろうなぁ。

2008-05-22

MozLab の ModuleManager のソースを読む

| 13:31 |  MozLab の ModuleManager のソースを読む - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  MozLab の ModuleManager のソースを読む - Ci.nsIZIGOROu

js ってちょっと大きめの物を作ろうと思うととっちらかる傾向があるので、MozLab で使われてる ModuleManager を使ってみたいなーと思い、ソースコード読んで見る事にしました。

ちなみに読む前提知識は mozIJSSubScriptLoader の挙動を知っている事が最低限必要です。

ModuleManager(searchPath, suffixList) コンストラクタ

/**
 * @param Array searchPath
 * @param Array suffixList
 */
function ModuleManager(searchPath, suffixList) {
    this._searchPath = [];

    var pathItem;
    if(searchPath)
        for each(pathItem in searchPath) 
            if(pathItem.match(/^\./))
                this._searchPath.push(
                    Components.stack.caller.filename.replace(/\/[^/]+$/, '/' + pathItem));
            else
                this._searchPath.push(pathItem);
    
    this._suffixList = suffixList || ['.js'];
    this._loader = Components
        .classes['@mozilla.org/moz/jssubscript-loader;1']
        .getService(Components.interfaces.mozIJSSubScriptLoader);
    this._requireCache = {};
}

ModuleManager のコンストラクタですけど、

searchPath
モジュール検索対象パスを配列で渡す。省略可能。
suffixList
検索対象拡張子を配列で渡す、省略時は [".js"]
searchPath

こちらは "." で始まる場合は呼び出し元のスクリプトファイルからの位置と見なされるみたいですね。

Components.stack.caller.filename が呼び出し元のスクリプトファイルのパス文字列です。

あとは mozIJSSubScriptLoader を作って、_requireCache を初期化して終了。

require(type, logicalUrl) メソッド

元は prototype プロパティメソッドがまとめて定義してあるので、個別定義に書き換えてありますが内容は同じです。

/**
 * @param String type
 * @param String logicalUrl
 * @return Object
 */
ModuleManager.prototype.require = function(type, logicalUrl) {
    var directoryOfCaller = Components.stack.caller.filename.replace(/\/[^/]+$/, '');
    var realUrl = this._locate(
        logicalUrl,
        [directoryOfCaller].concat(this._searchPath),
        this._suffixList);

    if(realUrl)
        switch(type) {
        case 'class_p': // DEPRECATED
            return this._loadClassPrivateEnv(realUrl);
            break;
        case 'class':
            return this._loadClassSharedEnv(realUrl);
            break;
        case 'package':
            return this._loadPackage(realUrl);
            break;
        default:
            throw new Error('Unknown module type. (' + type + ')');
        }
    else
        throw new Error('No script with given logical URL available. (' + logicalUrl + ')');

    return null;
};

コンストラクタの searchPath を省略した際は呼び出し元スクリプトが存在する場所が検索パスとして採用されるのがthis._locate()の第二引数から分かります。

_locate()メソッドは後の処理から類推すると文字列であろう事が推測出来ます。

typeに応じて呼び出すメソッドを切り替えて、その返り値をそのまま返すって事ですね。

DEPRECATED を除けば、

type method
class _loadClassSharedEnv(realUrl)
package _loadPackage(realUrl)

と言う対応になります。

_loadClassSharedEnv(realUrl) メソッド

/**
 * @param string realUrl
 * @return Function
 */
ModuleManager.prototype._loadClassSharedEnv = function(realUrl) {
    var cacheKey = ['class', realUrl];

    var classConstructor = this._requireCache[cacheKey];
    if(!classConstructor) {
        var proto = {
            module: this
        };
        classConstructor = function() {
            if(proto.constructor)
                proto.constructor.apply(this, arguments);
        };

        this._requireCache[cacheKey] = classConstructor;

        this._loader.loadSubScript(realUrl, proto);

        for(var name in proto) 
            if(name != 'inheritor' &&
               name != 'constructor')
                classConstructor.prototype[name] = proto[name];

        if(proto.inheritor)
            classConstructor.prototype = proto.inheritor();
        
    }
    return classConstructor;
};

呼び出し元は require("class", realUrl) ですね。呼び出しはキャッシュされます。

キャッシュに存在しない場合、prototype オブジェクトに代入するひな形として、proto を作り、そこの module プロパティに ModuleManager のインスタンスを代入していると。

そしてコンストラクタですが、constructor() メソッドの wrapper として実装しています。

mozIJSSubScriptLoader::_loadSubScript()で、この prototype のひな形に realUrl で定義されている内容を書き込みます。

この書き込まれた状態の proto から inheritor, constructor を除いた全てのプロパティが実際の prototype プロパティに代入されます。

最後に inheritor ですが、単なる関数呼び出しとしてオブジェクトを返すようにしてやれば、それが prototype プロパティに上書きされると言う物ですね。従って inheritor が定義してあって、constructor 以外のプロパティが定義してあったとしても、残念ながら上書きされて消されてしまいます。

実際には、

var foo;

function constructor() {
  // なんか処理
}

function getFoo() { return foo; }
function setFoo(aFoo) { foo = aFoo; }

みたいに "class" を記述するんだと思います。(多分、後で確かめる)

_loadPackage(realUrl) メソッド

ModuleManager.prototype._loadPackage = function(realUrl) {
    var cacheKey = ['package', realUrl] // BUG
    var pkg = this._requireCache[cacheKey];
    if(!pkg) {
        pkg = {
            module: this
        };
        this._requireCache[cacheKey] = pkg;

        this._loader.loadSubScript(realUrl, pkg);
    }
    return pkg;
};

require("package", realUrl) の時に呼び出されます。

まぁ、定義そのものを返すって感じですね。一つだけ例外を言えば、その定義されたオブジェクトプロパティにやはり ModuleManager のオブジェクトが入ってるって事です。また class と同様にキャッシュされます。

inject() メソッド

ModuleManger.prototype.inject = function(logicalUrl, target) {
    var directoryOfCaller = Components.stack.caller.filename.replace(/\/[^/]+$/, '');
    var realUrl = this._locate(
        logicalUrl,
        [directoryOfCaller].concat(this._searchPath),
        this._suffixList);

    if(realUrl)
        this._loader.loadSubScript(realUrl, target);
    else
        throw new Error('No script with given logical URL available. (' + logicalUrl + ')');        
}

これは mixin 用ですね。特定のオブジェクトに対して loadSubScript する wrapper ですが、相対パスが使えるのが違う所ですね。

まとめ

ModuleManager ベースの開発だと二通りの定義方法がある。

class
普段の JavaScriptclass 定義とはちょっと異なっていて、constructor と inheritor と言う新しい定義方法になってる。また定義時は loadSubScript 経由なんで prorotype とか不要
package
旧来の定義方法だが、module プロパティが突っ込まれる
inject
特定のオブジェクトをダイナミックに拡張したい場合に使う

って感じかなぁ。

凄い使えそう。と言うか使おう。

piro_orpiro_or2008/05/22 13:49UxUではModule Manager自体の使い方は結局よく分からないままとりあえず流用してるという状態で、メリットとか全然生かせてないですね……
あとFirefox 3なら標準の機能であるJavaScript Code Moduleを使う方が良いような気がします。

ZIGOROuZIGOROu2008/05/22 15:29piro兄さんきた−!

> Firefox 3なら標準の機能であるJavaScript Code Moduleを使う方が良いような気がします。

今知った件><
勉強してきm(ry

2008-05-21

piroたんのコード

| 19:30 |  piroたんのコード - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  piroたんのコード - Ci.nsIZIGOROu

激しく参考にしたいので、所在メモ。

あとその過程で xpcom 実装サンプルも見つけた。

SEE ALSO

KerriannKerriann2011/12/30 22:46I'm not easily impressed but you've done it with that potisng.

brvdbmcbrvdbmc2011/12/31 17:59oLl2bZ <a href="http://gfiwwowzzlji.com/">gfiwwowzzlji</a>

jyxjibjyxjib2012/01/01 22:51f1lPhg <a href="http://aqeowglrnkaq.com/">aqeowglrnkaq</a>

tbomhzurunjtbomhzurunj2012/01/02 04:025Tv1fJ , [url=http://bvohidnwzpxo.com/]bvohidnwzpxo[/url], [link=http://cwvxvbhfccgx.com/]cwvxvbhfccgx[/link], http://whhabqvrspfa.com/

2008-05-20

特定の拡張機能の情報を得る

| 16:14 |  特定の拡張機能の情報を得る - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  特定の拡張機能の情報を得る - Ci.nsIZIGOROu

nsIExtensionManager を使う。

例えばインストールされている場所が知りたい場合、

var exmanager = Cc["@mozilla.org/extensions/manager;1"].createInstance(Ci.nsIExtensionManager);
exmanager.getInstallLocation('foobar@art-code.org').location.path; 

などで取得出来る。locationプロパティはnsIFile型。

SQLiteのdatetime型とDateオブジェクトの相互変換ライブラリ

| 15:22 |  SQLiteのdatetime型とDateオブジェクトの相互変換ライブラリ - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  SQLiteのdatetime型とDateオブジェクトの相互変換ライブラリ - Ci.nsIZIGOROu

ソース

var DateTimeFormat = {
  patten: /^(19[7-9]\d|[2-9]\d{3})-(0\d|1[0-2])-(0[1-9]|[12]\d|3[01]) ([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/,
  format_datetime: function(aDate) {
	return ([aDate.getUTCFullYear(), aDate.getUTCMonth(), aDate.getUTCDate()]).map(function(item) { return DateTimeFormat.format(item, 2); }).join('-') + ' ' + ([aDate.getUTCHours(), aDate.getUTCMinutes(), aDate.getUTCSeconds()]).map(function(item) { return DateTimeFormat.format(item, 2); }).join(':');
  },
  parse_datetime: function(aDateStr) {
	var match;
	if (match = DateTimeFormat.patten.exec(aDateStr)) {
	  var args = Array.prototype.splice.call(match, 1, 6);
	  var aDate = new Date(Date.UTC.apply(Date, args));
	  return aDate;
	}
	else {
	  return;
	}
  },
  format: function(num, size) {
	var fill = size - num.toString().length;
	if (fill > 0)
	  return Array(fill + 1).join('0') + num.toString();
	else
	  return num.toString();
  }
};

2008-05-19chanserv

Storage 用の ResultSet 展開ライブラリ作ってみた

| 19:34 |  Storage 用の ResultSet 展開ライブラリ作ってみた - Ci.nsIZIGOROu を含むブックマーク はてなブックマーク -  Storage 用の ResultSet 展開ライブラリ作ってみた - Ci.nsIZIGOROu

ソースコード

var StorageResultSetExtractor = function(stmt) {
  this.stmt = stmt;
};

StorageResultSetExtractor.prototype = {
  rowInfoCache: null,
  rowData: null,
  getRowInfo: function() {
	if (this.rowInfoCache) {
	  return this.rowInfoCache;
	}

	var rowInfo = [];
	for (var idx = 0, colCnt = this.stmt.columnCount; idx < colCnt; idx++) {
	  rowInfo.push({ name: this.stmt.getColumnName(idx), type: this.stmt.getTypeOfIndex(idx) });
	}

	this.rowInfoCache = rowInfo;
	return this.rowInfoCache;
  },
  next: function() {
	if (!this.stmt.executeStep())
	  return false;

	var rowInfo = this.getRowInfo();
	var rowData = {};

	for (var cidx = 0, clen = rowInfo.length; cidx < clen; cidx++) {
	  switch (rowInfo[cidx].type) {
		case this.stmt.VALUE_TYPE_NULL:
		  rowData[rowInfo[cidx].name] = null;
		  break;
		case this.stmt.VALUE_TYPE_INTEGER:
		  rowData[rowInfo[cidx].name] = this.stmt.getInt32(cidx);
		  break;
		case this.stmt.VALUE_TYPE_FLOAT:
		  rowData[rowInfo[cidx].name] = this.stmt.getDouble(cidx);
		  break;
		case this.stmt.VALUE_TYPE_TEXT:
		  rowData[rowInfo[cidx].name] = this.stmt.getUTF8String(cidx);
		  break;
		case this.stmt.VALUE_TYPE_BLOB:
		  var dataSize = { aDataSize: null };
		  var data = { aData: null };
		  this.stmt.getBlob(cidx, dataSize, data);
		  rowData[rowInfo[cidx].name] = data.aData;
		  break;
		default:
		  ;
	  }
	}

	this.rowData = rowData;
	return true;
  },
  item: function() {
	return this.rowData;
  }
};

使い方

var storage = Cc["@mozilla.org/storage/service;1"].getService(Ci.mozIStorageService);
var conn = storage.openDatabase(aDBFile);
var stmt = conn.createStatement("SELECT * FROM foo;");

var extractor = new StorageResultSetExtractor(stmt);
var result = [];

while (extractor.next()) {
  result.push(extractor.item());
}

result[0].colname; // これで値取れる

んー、まぁ今はここまで。本当はinflate/deflateとか出来るべきなんだろうなぁ。

ChristopherChristopher2012/09/28 18:11There is a critical shortage of infrotmiave articles like this.

bcskfetxmwbcskfetxmw2012/09/29 05:20wPzYXA <a href="http://mskjgsyfulye.com/">mskjgsyfulye</a>

xoicravirjxoicravirj2012/09/30 01:09R8uDa7 , [url=http://gbqpulptaobp.com/]gbqpulptaobp[/url], [link=http://yzjkswqqoywo.com/]yzjkswqqoywo[/link], http://jvhkpyeexcvu.com/

ysjvlscewysjvlscew2012/09/30 11:32PNyyAc <a href="http://kwjpltbwouwo.com/">kwjpltbwouwo</a>

yelvcsfuymjyelvcsfuymj2012/10/02 02:50LQci7N , [url=http://onwtzvmvqkct.com/]onwtzvmvqkct[/url], [link=http://pwzbknltpxfr.com/]pwzbknltpxfr[/link], http://chfaxyypvvmi.com/