Hatena::Groupmoz-addon

hogezilla RSSフィード

当ページに書かれているコードは、修正BSDライセンスのもと、再頒布して頂いて構いません。

|

2013-02-11

Firefox 21 以降 XBML 定義のプロパティ/メソッドの上書きに注意

| 22:53 | はてなブックマーク - Firefox 21 以降 XBML 定義のプロパティ/メソッドの上書きに注意 - hogezilla

この変更により、XBL 定義のプロパティ/メソッドのデスクリプタがwritable: falseとなる

JavaScriptの環境が、Strictモードなら例外が出る。

"use strict";
gBrowser.addTab = function (url, option) { ... }
// TypeError: "addTab" is read-only

Strictモードでない場合は、例外は出ないものの、上書きされていない結果となる。

どうしても上書きしたい場合は、

Obejct.defineProperty(gBrowser, "addTab", {
  configurable: true,
  value: function (url, option) { ... },
});

Object.defineProperty等を使わなくてはならない。

追記(2013-02-14)

情報元は839792 – From add-on, TypeError: "_appendCurrentResult" is read-onlyなんだけど、パッチが投下されてる。

詳細は良く分かってないけど、同XBL内からなら普通に代入できるようになったのかも。

UTF-8以外の文字列のURIエンコード/デコード

| 22:35 | はてなブックマーク - UTF-8以外の文字列のURIエンコード/デコード - hogezilla

XMLHttpReuqest の GET でクエリー文字列にUTF-8以外のURIエンコードされたものを使用したい場合がある。

例えば、Web辞書から検索してきたいが、相手はShift_JISだったり、EUC-JPだったりする場合だ。

そんな時にこれを使う。

const TTSU = Cc["@mozilla.org/intl/texttosuburi;1"].getService(Ci.nsITextToSubURI);

/**
 * @param {String} aUnicodeString
 * @param {String} [aCharset="UTF-8"]
 * @return {String} encoded ascii string
 */
function encodeURIComponent (aUnicodeString, aCharset = "UTF-8") {
  return TTSU.ConvertAndEscape(aCharset, aUnicodeString);
}

/**
 * @param {String} aUnicodeString
 * @param {String} [aCharset="UTF-8"]
 * @return {String} decoded Unicode string
 */
function decodeURIComponent (aEncodedString, aCharset = "UTF-8") {
  return TTSU.UnEscapeAndConvert(aCharset, aEncodedString);
}
var query = encodeURIComponent("あいうえお", "Shift_JIS");
// "%82%A0%82%A2%82%A4%82%A6%82%A8"

var str = decodeURIComponent(query, "Shift_JIS");
// "あいうえお"

その他

nsITextToSubURIには他にも

  • unEscapeNonAsciiURI(charset, str)
  • unEscapeURIForUI(charset, str

がある。

どちらも似たような動きだが、後者のunEscapeURIForUIはデコードに失敗しても例外を返さない特徴がある。

var url = TTSU.unEscapeNonAsciiURI("Shift_JIS", "http://example.com/?q=%82%A0%82%A2%82%A4%82%A6%82%A8");
// "http://example.com/?q=あいうえお"
url = TTSU.unEscapeNonAsciiURI("UTF-8", "http://example.com/?q=%82%A0%82%A2%82%A4%82%A6%82%A8");
// 例外: NS_ERROR_ILLEGAL_INPUT

url = TTSU.unEscapeURIForUI("UTF-8", "http://example.com/?q=%82%A0%82%A2%82%A4%82%A6%82%A8");
// "http://example.com/?q=%82%A0%82%A2%82%A4%82%A6%82%A8"

piro_orpiro_or2013/02/12 01:55検証しないままコメントするんですが、writable:trueで上書きしてしまうと元のセキュリティ脆弱性が再導入されてしまうということはないでしょうか?(といっても、元のバグが非公開のようなので検証しようがないんですけど……)

teramakoteramako2013/02/12 12:38あ、はい、おっしゃるとおりですね...。何となくwritable: trueにしてしまいました。
どちらかと言うと、configurable: true のみ(enumerableは任意)にするのが良かったですね。(変更予定がないならconfigurableすら不要)

セキュリティ脆弱性についてですが、詳細が分からないので、そもそも何故、普通の代入を不可にしてObject.definePropertyは許可で脆弱性を回避できるのかさっぱりです。RESOLVED FIXEDされたセキュリティバグって公開にならないんでしたっけ?

トラックバック - http://moz-addon.g.hatena.ne.jp/teramako/20130211

2012-12-26

enigmailのipcモジュール subprocess.jsm が便利!

| 22:39 | はてなブックマーク - enigmailのipcモジュール subprocess.jsm が便利! - hogezilla

一応 XPConnect で nsIProcess を使用してプロセス実行することは可能だけれども、標準入力、標準出力、標準エラーを取るには、一度ファイルへ書きだしておいてリダイレクトとか、リダイレクトでファイルに書き出すようにしないといけなかった。

var args = ["<", "/tmp/stdin.txt", ">", "/tmp/stdout.txt"];
var exe = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
exe..initWithPath("/bin/cat");
var proc = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
proc.init(exe);
proc.runAync(args, args.length);

な感じ。

これが、

// test-resource は各自で登録したresource名を
Cu.import("resource://test-resource/subprocess.jsm");

subprocess.call({
  command: "/bin/cat",
  arguments: [],
  workdir: "/home/teramako",
  charset: "UTF-8",
  stdin: function (stdin) {
    stdin.write("data...");
    stdin.close();
  },
  stdout: function (data) {
    log(data);
  },
  stderr: function (data) {
    Cu.reportError(data);
  },
  done: function (result) {
    switch (result.exitCode) {
      case 0:
        // 正常終了
        break;
      default:
        // 異常終了
    }
  },
});

みたいに書けちゃう。stdout, stderrは非同期で取れるし、素晴らしい。

中身は、js-ctypes を使って Windows なら "kernel32.dll", Darwin なら "libc.dylib", linux なら "libc.so.6" を直接叩くみたいな感じ。

昔、enigmail は nsIIPCService ってやつを使って、stdout, stderr のリスナを登録する感じだったんだけど、これもプロセスが終了しないと結果が得られない代物だった。これが随分と進化したものだ。

追記

stdout, stderr から得られるデータは、charset指定のもので、nsIScriptableUnicodeConverter にかけられてしまう。純粋にバイナリデータを取りたい時は注意すべきかもしれない

トラックバック - http://moz-addon.g.hatena.ne.jp/teramako/20121226

2012-12-09

windowを開いた親windowを得る

| 08:24 | はてなブックマーク - windowを開いた親windowを得る - hogezilla

ちょっとタイトルが分り難いなw えーと、ダイアログとかタブとかサイドバーに新たなXUL Windowを開いた時、その新たなコンテンツから親となるルートのウィンドウ(navigator:browser)を得たい場合のこと。

通常なら以下のコードで良い。

Cu.import("resource://gre/modules/Services.jsm");

var parentWindow = Services.wm.getMostRecentWindow("navigator:browser");

しかし、例えばタイマーによる発動でウィンドウが開いた場合、親のウィンドウがアクティブであるかの保障はなく、別のウィンドウが得られてしまうかもしれない。

また、以下の方法もあるが、browser要素やiframe要素にtype="content"等の属性が付いていると親ウィンドウを得ることはできない。

var parentWindow = window.top;

ということでアレコレ試しつつ悩んでいた。が、ようやく発見した。

var rootWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIWebNavigation)
                       .QueryInterface(Ci.nsIDocShellTreeItem)
                       .parent /* rootTreeItem にすると最上位windowになる */
                       .QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIDOMWindow);
トラックバック - http://moz-addon.g.hatena.ne.jp/teramako/20121209

2012-09-13

“can’t access dead object” エラーを体験する

| 21:29 | はてなブックマーク - “can’t access dead object” エラーを体験する - hogezilla

Firefox 15 からコンテンツがアンロードされたとき、コンテンツ内のデータ(例えばDOM要素)へのリファレンスが強制的に削除されるようになった。これによりメモリに不用意に残り続けていたものが消えて省メモリ化しますよ、となったわけだ。

そして、削除されたオブジェクトのプロパティにアクセスしようとすると、“can’t access dead object”のエラーが出るようになった。

これをワザと出してみようと試みた。

体験するためにスクラッチパッドを使用する。

  1. about:config から devtools.chrome.enabledtrue にする。
  2. スクラッチパッドを起動する
  3. 実行環境メニューから「ブラウザ」を選ぶ
  4. 以下を記入して実行
var tab = gBrowser.loadOneTab("about:blank", { inBackground: false });
tab.linkedBrowser.addEventListener("load", function(e){
    this.removeEventListener("load", arguments.callee, true);
    var doc = this.contentDocument;
    gBrowser.removeTab(tab);
    setTimeout(function(){
        Cu.reportError(doc.title);
    }, 2 * 1000);
}, true);

エラーコンソールに以下の様にでるはず。

時刻: 2012/09/13 21:25:39
エラー: TypeError: can't access dead object
ソースファイル: Scratchpad
行: 7

上記では、var doc = this.contentDocument と Documentオブジェクトへの参照を保持するようにしたが、DOM要素以外でも、var obj = new this.contentWindow.wrappedJSObject.Array("a","b")等の普通のJSオブジェクトでも構わない。

2012-08-22

__exposedProps__ プロパティ

| 21:14 | はてなブックマーク - __exposedProps__ プロパティ - hogezilla

Firefox 17 から(?) コンテンツにデータを突っ込む時にアクセス制御が可能になった。

Addon側のコード

content.window.wrappedJSObject.foo = {
  bar: "OK"
};

コンテンツ側のコード

foo.bar

上記の場合、今までは普通にアクセスできた。が、これからはできなくなる。foo オブジェクトへのプロパティ書き込みも不可

Addon側のコード

content.window.wrappedJSObject.foo = {
  bar: "OK",
  __exposedProps__: {
    bar: "r"
  }
};

と、__exposedProps__オブジェクトを作って、プロパティに対する読み取りや書き込みを許可することでアクセスが可能になる。

値は

r
読み取り
w
書き込み
rw
読み取りと書き込み

アドオン側からコンテンツに対してデータを突っ込む時は大抵、書き込みアクセスは避けたい場面が多いだろう。"r" のみを与えることで、可能になるので、すこし面倒だし、変なプロパティを付けなければならなくて嫌な感じもするが、歓迎である。

トラックバック - http://moz-addon.g.hatena.ne.jp/teramako/20120822
|