Home > Latest topics

Latest topics 近況報告

たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。

萌えるふぉくす子さんだば子本制作プロジェクトの動向はもえじら組ブログで。

宣伝1。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能! シス管系女子って何!? - 「シス管系女子」特設サイト

宣伝2。Firefox Hacks Rebooted発売中。本書の1/3を使って、再起動不要なアドオンの作り方のテクニックや非同期処理の効率のいい書き方などを解説しています。既刊のFirefox 3 Hacks拡張機能開発チュートリアルと併せてどうぞ。

Firefox Hacks Rebooted ―Mozillaテクノロジ徹底活用テクニック
浅井 智也 池田 譲治 小山田 昌史 五味渕 大賀 下田 洋志 寺田 真 松澤 太郎
オライリージャパン

Page 12/239: « 8 9 10 11 12 13 14 15 16 »

nsIHttpProtocolHandlerに対するプロキシとなるXPCOMコンポーネントを実装してみた - Jun 08, 2009

ローカルプロキシっぽいことをローカルプロキシを立てずにやろうとして挫折したことのまとめを書いたら、thorikawaさんが別のアプローチを提示してくださったので、その方向で頑張ってみた。

結論から言うと、URIの置き換え(特定のURIにアクセスしようとした時に、別のURIのリソースの内容を返す)についてはできるようになった。成果はUxU 0.6.0組み込んである。ただし、他のアドオンとの競合の可能性があるので、初期状態では機能を無効にしてある。

実装がどうなっているかはGlobalService.jsの中のProtocolHandlerProxyHttpProtocolHandlerProxyHttpsProtocolHandlerProxyの各クラスを参照のこと。thorikawaさんのエントリに挙げられている課題は、一応解決されているはず。

thorikawaさんによるコードからの違いは以下の点だ。

変更点1:元のコンポーネントが実装しているインターフェースは全部備えることにした

一つは QueryInterfaceの部分で、QueryInterfaceの部分を置換前のXPCOMに丸投げしてしまっているので、その後の個別の処理も全て置換前XPCOMで行われてもよいはずです。だけど実際にはnewURI,newChannelといったメソッドは置換後のXPCOMのものが呼び出されます。

QueryInterface()は、実はメソッドの返り値を見るまでもなく、メソッドを実行した時点でそのラッパーオブジェクト自体が変更される。なので、そのせいじゃないかと思う。例えば

var pref = Cc['@mozilla.org/preferences;1']
            .getService(Ci.nsIPrefBranch);
var value = pref.getBoolPref(...);

このコードは

var pref = Cc['@mozilla.org/preferences;1']
            .getService();
pref.QueryInterface(Ci.nsIPrefBranch);
var value = pref.getBoolPref(...);

と書いてもちゃんと動く。後者のような使い方をされている限りは、QueryInterface()で何を返していようと関係ない、ということではないのかなあ。

C++で書かれたコンポーネントの中ではdo_QueryInterface()という関数?がよく使われているようで、こいつが中で何をやってるのかまでは僕にはよく分かってないけど、上記の例の前者ではなく後者に相当するものなんだったら、引用部のような現象が起こるのではないかと思う。

ということでそういう場合に備えて、ProtocolHandlerProxyクラスは、コンポーネント「{4f47e42e-4d23-4dd3-bfda-eb29255e9ea3}」が備えているすべてのインターフェースを実装しておくようにした。具体的には、nsIHttpProtocolHandler、nsIProtocolHandler、nsIProxiedProtocolHandler、nsIObserver、nsISupports、nsISupportsWeakReferenceの各インターフェース。といっても、やってることとしてはやっぱり単に元のコンポーネントに処理を丸投げしてるだけなんだけど。

変更点2:プロパティとしてアクセスされる情報を、prototypeの方に定義するようにした

またもう一つは、このサンプルでも正常に動作しないサイトがいくつかあること。AJAXを多用しているサイトでも基本的には問題なく動くのですが、たとえばGMailにアクセスするとなぜか簡易版HTMLが表示されてしまいます。もしかすると上記の問題と関連しているのかも知れません。

これは推測だけれども、HTTP_USER_AGENT等が正常に送られなくなっていたせいではないかと思われる。

ちゃんと調べたわけではないんだけど、Webページ内のスクリプトのnavigator.userAgentの値や、HTTPの通信の際に送られるユーザエージェント名の文字列は、コントラクトIDが@mozilla.org/network/protocol;1?name=httpであるコンポーネントがnsIHttpProtocolHandlerインターフェースを通じて返す値を元に生成されているらしい。

然るに、thorikawaさんによるサンプルコードのコンポーネントにはnewChannel()などのメソッドは定義されているけれども、nsIHttpProtocolHandlerが持つはずのuserAgent等のプロパティは定義されていない。XPConnect経由でこのコンポーネントにアクセスすると、undefinedが文字列にキャストされて空文字として返ることになる。その結果、サーバに送られるUA文字列も空っぽになってしまう。つまりサーバから見たら未知のUAとなってしまう。GMailにアクセスするとなぜか簡易版HTMLが表示されてしまうのは、未知のUAに対しては簡易版HTMLを返すようにGMailが作られているからではないかと僕は推測している。

変更点1で書いた話を実施するにあたって、当初はこれらのプロパティもgetterとして定義してみてたんだけど、実際に動かしてみると、期待通りには動かなかった。具体的には、Webページ内のスクリプトからnavigator.userAgent等にアクセスしようとすると、セキュリティの制限により前述のgetter関数を実行できない、というエラーが出る。

なので、getter関数を使うことは諦めて、ProtocolHandlerProxyクラスのプロトタイプに、単純な文字列や数値の定数プロパティとしてそれらを設定しておくようにした。general.useragent.* 系の設定の変更を動的に反映しないといけないので(ここでgetterを使えないのが痛い)、とりあえず今のところは、それらの設定の変更を常時監視して、変更があればその都度ProtocolHandlerProxyクラスのプロトタイプの定数プロパティの値を更新するようにしている。

変更点3:https:なURIに対しても働かせるようにした

この実装では、ProtocolHandlerProxyをスーパークラスとして、HttpProtocolHandlerProxyHttpsProtocolHandlerProxyという2つのサブクラスを定義することにした。これらの違いは単にコントラクトIDだけ。(まあ、モジュールを登録する時にしか使わない情報なので、サブクラスにするまでもなかったんだろうとは思うけど……)

変更点4:このコンポーネントを使うかどうかをユーザが設定できるようにした

コントラクトIDが@mozilla.org/network/protocol;1?name=httpであるコンポーネントを入れ替える事を考えた時に、一番問題になるのはおそらく、同じ事をしようとするアドオンが2つ以上あった時のことだと思う。テストケース内でリクエストされるURIをリダイレクトするためだけに、いつもコンポーネントを入れ替えた状態にしておくというのは、無駄にリスクを高めるだけのような気がする。なので、必要な時にだけ機能を有効化できるようにしてみた。

当初は、NSGetModule()が返すモジュールのregisterSelf()メソッドの中でregisterFactoryLocation()している箇所を、設定値を見て必要に応じスキップするようにすればいいだろうか、くらいに考えていた。でも2つの理由でこれはうまくいかなかった。

  1. コンポーネントを登録する処理が走る段階では、プロファイルが初期化されていないので、ユーザが設定を有効にしているのか無効にしているのかが分からない。
  2. コンポーネントの登録はUxUをインストールまたはアップデートした後に1回行われるのみなので、ユーザが設定を変更しても即座には反映されない。

1の問題をもう少し具体的に書くと、例えばこのコンポーネントの有効無効をextensions.uxu.protocolHandlerProxy.enabledといった名前の設定で切り替えるようにしようと思っても、NSGetModule()が返すモジュールのregisterSelf()メソッドが実行される段階ではまだユーザプロファイル内に保存された設定値が読み込まれていないため、その段階でextensions.uxu.protocolHandlerProxy.enabledの値を取得しても常にデフォルト値(=false)となってしまう、ということ。

これを回避するためにUxUでは、設定値ではなくファイルを使うことにした。プロファイル内に「.uxu-protocol-handler-proxy-enabled」という名前のファイルがあればコンポーネントを有効に、ファイルがなければコンポーネントを無効に、という風に、ファイルの有無を真偽値型の設定代わりに使うようにしている。泥臭いというか非効率的というかすごく馬鹿っぽいというかそんな気がするけど、これ以上にいい方法を僕は思いつけなかった。

2の問題は、1を解決できたとしても発生する。Firefoxは利用可能なXPCOMコンポーネントのデータベースをプロファイル内にcompreg.datとxpti.datとして保存しているようで、これらのファイルを生成する時にNSGetModule()が呼ばれるんだけれども、毎回起動時にこのデータベースを作り直していたら無駄が大きすぎる。ということで、アドオンが追加・削除された時などの「コンポーネントの一覧に変更があった可能性がある」場合にだけ、有効なコンポーネントの一覧のデータベースが作り直されるらしい。UxU的には、プロトコルハンドラの乗っ取りのON/OFFを切り替えた後に必ずこのデータベース再作成の処理を走らせないといけない、でもそれができてない、ということだ。

で、これも似たような方法でアッサリ解決した。Firefoxはユーザプロファイル内に「.autoreg」という名前のファイルがあると(ファイルの有無しか見ないので、内容は何でもいい。空ファイルでよい。)、必ずcompreg.datとxpti.datを再作成する。なので、ユーザが設定を変更したらそのタイミングで「.autoreg」を作成してやり、次に起動した時には設定の変更が確実に適用される(コンポーネントが有効かあるいは無効化される)ようにした。

んで

これだけごちゃごちゃと妙なことをやっておいて、できることといったら単にURIをリダイレクトするだけなんだよね。ショボっ!

高橋メソッドドリブン開発 - May 28, 2009

Mozilla Partyの発表用資料を準備してる最中、自分で作っておきながら画像の埋め込みの仕様がクソ過ぎることにブチギレて、高橋メソッド in XULを久しぶりに改良してた。

  • 画像を全部確実にプレロードするようにした。
  • 画像の埋め込み指定に幅と高さの指定が無い時は、プレロードした画像それ自体の大きさで表示するようにした。
    • 幅か高さのどちらかが省略されていた場合、指定されている方の大きさに合わせてアスペクト比を保って拡大・縮小表示するようにした。
    • 幅や高さにパーセント値を指定した場合は、ウィンドウサイズに対する割合として計算するようにした。
  • ウィンドウの大きさが変わった時にちゃんと再描画するようにした。

公開ページはまだ更新してない。改良されたバージョンはリポジトリに置いてある

ローカルプロキシっぽいことをローカルプロキシを立てずにやろうとして挫折したことのまとめ - May 28, 2009

FireMobileSimulatorでのローカルプロキシ実装の試みを見て、UxUデバッグ用ローカルプロキシみたいな事をできるようにしてみたい、と思った。

ただ、HTTPのことはこれっぽっちも分からない。ソケット通信も、一応独自プロトコルっぽいものを使って別プロファイルで動作中のFirefoxからテスト結果を受け取るということはできるようになったけど、それ以上の事は分かってないまま。なので、まじめにローカルプロキシを立てる以外の方法で、「特定のURIにアクセスしようとした時だけ、あらかじめ定義しておいたルールに従って別のリソースを返す」ということをできるようにしてみようと考えた。

僕が現在把握している方法としては、以下の物がある。

  1. ローカルプロキシを実装して、その中でリダイレクトするやり方。
  2. http-on-modify-requestイベントのタイミングでリダイレクトするやり方。
  3. nsIContentPoilcyのshouldLoad()の中でリダイレクトするやり方。

1はthorikawaさんが頑張っておいでなので、それに期待している(ソースがGPL互換ならUxUにそのまま入れられるので)。他人のフンドシ。他力本願。ここでは後の2つについての挫折の経緯だけ書き留めておく。

結論を先に言っておくと、2も3も実装上の制限により全滅っぽい。やはり、ローカルプロキシをちゃんと実装するしか完全な解決策はないようだ。thorikawaさん期待age。

http-on-modify-requestイベントを使うやり方

これは、現在FireMobileSimulator等ですでに使われている。nsHttpChannelはリクエストを送信する前にnsIObserverServiceを通じてhttp-on-modify-requestイベントを、レスポンスが返ってきた後にhttp-on-examine-responseとかhttp-on-examine-merged-responseとかhttp-on-examine-cached-responseとかのイベントを通知する(SubjectはnsHttpChannel自身)ので、このタイミングでリダイレクトをしようという話。

リクエストし直す場合

FireMobileSimulatorの場合、nsHttpChannelの現在の通信をキャンセルした上で、nsHttpChannelからnsIWebNavigationを引っ張ってきてloadURI()で新しく通信を始めてる。

var redirected = 'http://www.example.com/';
var observer = {
  observe : function(aSubject, aTopic, aData) {
    if (aTopic == 'http-on-modify-request') {
    var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel)
                              .QueryInterface(Ci.nsIRequest);
    // ここでキャンセル
    httpChannel.cancel(Components.results.NS_ERROR_FAILURE);
    // リクエストし直し
    httpChannel.notificationCallbacks
      .getInterface(Ci.nsIWebNavigation)
      .loadURI(redirected, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
  }
};
Cc['@mozilla.org/observer-service;1']
  .getService(Ci.nsIObserverService)
  .addObserver(observer, 'http-on-modify-request', false);

通常のbrowserやiframeの場合はこれでいいんだけど、img要素やXMLHttpRequestからの通信では失敗する。具体的にはhttpChannel.notificationCallbacks.getInterface(Ci.nsIWebNavigation)の時点でエラーになる。例えばXMLHttpRequestによる通信なら、httpChannel.notificationCallbacks.getInterface(Ci.nsIXMLHttpRequest)とすれば元のリクエストを取得できるので、こんな感じで場合に応じて再リクエストの方法を振り分けてやる必要がある。(XMLHttpRequestの場合については、どうやれば再リクエストできるのかまではたどり着けてない。もしかしたら無理なのかも。)

nsIURIのspecを書き換える場合

nsIChannelのURIプロパティはnsIURIインターフェースの読み取り専用プロパティなので、通信先のURIを書き換えることはできない……ように見える。でも実はnsIURIのspecプロパティの方は書き換え可能なので、ここに新しいURIを代入することででもリダイレクトできてしまう。

observe : function(aSubject, aTopic, aData) {
  if (aTopic == 'http-on-modify-request') {
  var httpChannel = aSubject.QueryInterface(Ci.nsIHttpChannel);
  httpChannel.URI.spec = redirected;
}

とはいえこの方法は全くお勧めできない。動く場合もあるけど、動かない場合もある、という感じで実に不安定だ。nsHttpChannelの実装を見れば分かるけど、http-on-modify-requestが通知された段階ですでにその時のリクエスト先URIに基づいた初期化処理がいくつか終わってしまっているので、それと矛盾するURIを設定する(例えば、HTTPのリクエストだった物をFile URLにリダイレクトするとか)と、この後の内部処理でエラーが起こるっぽい。

レスポンスヘッダを書き換える

http-on-examine-responseの方ではnsIHttpChannelのsetResponseHeader()を利用できるので、LocationヘッダにURIを設定してみたんだけど、ダメだった。残念ながらヘッダを解釈してくれなかった。

nsHttpChannelの実装を見たら、HTTPのステータスコードが301とか302とかの時だけLocationヘッダを見るようになってた。ステータスコードを保持しているプロパティは読み取り専用なので、強制的にLocationヘッダを読ませるということはできそうにない。

nsIContentPoilcyのshouldLoad()を使うやり方

これはURNサポートなどで実際に使っている。詳しい方法は2007年当時のエントリに書いてあるけど、要約するとこういうことだ。

  1. nsIContentPolicyインターフェースを備えたXPCOMコンポーネントを作成して、
  2. カテゴリマネージャに登録しておき、
  3. 処理したいURIが渡ってきたら、元の通信の読み込みを中止させて、代わりに別のURIで通信を始める。

やってみると、一見上手く動いてくれてるように見えるんだけど、XPConnect特権がある実行コンテキストで作成したImageやnsIXMLHttpRequestのインスタンスからの通信が捕捉されなくて、UxUの用途では使えない感じだった。まあ仮に捕捉できたところで、元の通信を止めて別のURIで通信するようにさせる方法は分かってない(もしかしたら無いかもしれない)んだけど。

あと、shouldLoad()の第2引数はnsIURIインターフェースなのでこれのspecプロパティを書き換えたらどうだろう?と思ってやってみたけど、これもうまくいかなかった。browser要素等での読み込みの場合だと、nsDocShellのInternalLoad()でエラーが発生する。実装を見た感じでは、nsIContentPolicyに処理が渡ってくるより前に元のURIに基づいてセキュリティ関係の機能が初期化されてるので、その後でURIを書き換えたのがいけなかったんじゃないかと思う。

まとめ

とにかく、nsHttpChannelのインスタンスが作成された後からどうこうしようと思うのがそもそも手遅れくさい。それより前のステップでアクセス先のURIを書き換えようと思ったら、ローカルプロキシを立てる以外に手は無いようだ。

いいかげん諦めてMozilla Partyの発表資料作ることにします……

Trunkでデフォルトのテーマから削除されたThrobberの画像の代替 - May 25, 2009

くでんさんが報じておられる通り、Bug 418003 - Firefox is packaging unneeded images and iconsによって、過去に使われていたもののもう使われていない画像がデフォルトのテーマからまとめて削除された。Trunkでは既にそのようになってるけど、Firefox 3.5もそうなるんだろうか。だとしたら例によって「何でこんなギリギリになってからやるわけ?」と愚痴りたくなるわけですが。

削除されたファイルの一覧はMercurialのコミットログを見ると分かる。行の頭に「-」が付いている物が、削除されたファイルという事になる。

自分がアドオンの中で流用していて影響を受けるのは、「読み込み中」の状況を示すためのアイコンとして使っていたThrobber-small.gifだ。Throbber.png、Throbber.gif、Throbber-small.pngも一緒に削除されたので、これらを流用してる人は代わりのアイコンを参照するようにしないといけない。

これらのThrobber画像は、半透明のアニメーション画像に差し替える関係でFirefox 3.0の時点から既に使われなくなっていた。また、これらの新しいThrobber画像は16×16サイズのものしか用意されていないので、大きいサイズの画像が必要な場合は自作するか古いバージョンからぶっこ抜くしかない。

16×16については、以下のChrome URLで参照できる。

  • chrome://global/skin/icons/notloading_16.png(アニメーション無し)
  • chrome://global/skin/icons/loading_16.png(アニメーションあり)

少なくとも今の時点では、Windows用、Linux用、Mac OS X用のいずれも同じパスで参照できるようだ。

ユーザが別のテーマを選択している場合もこの画像を参照したければ、以下のようにjarファイルの中のパスを直接指定する必要がある。

  • jar:resource:///chrome/classic.jar!/skin/classic/global/icons/notloading_16.png
  • jar:resource:///chrome/classic.jar!/skin/classic/global/icons/loading_16.png

FUEL使ったんだ…… - May 16, 2009

FUEL の Window/Tab 周りのハンドラは Firefox 3 では使うな - 8時40分が超えられない - subtech

書籍の中で詳しく解説しといてナンですが、アレをほんとに使う人がいたのか!ということにむしろ驚いてますよってなもんです。

まあ言う人に言わせれば、NSPRやらNeckoやらのよくわからない物が何層も積み重なった上にあるFirefoxを使うということ自体が「しんじらんねー」な感じなんでしょうけども。

他のアドオンと連携しやすくするためのライブラリを作ってみた - May 13, 2009

マルチプルタブハンドラについてRockridge氏ほかから「メニューをカスタマイズできるようにしてくれ」という要望が挙がっていたのだけれども、Menu Editorという素晴らしいアドオンがあるのに自前で同じような機能を再実装するのは徒労感しか無いなあと思ったので、開き直って、マルチプルタブハンドラの設定ダイアログを以下のようにしてみた。

  • Menu Editorがインストールされていなければ「Menu Editorをダウンロードする」リンクを表示する。
  • Menu Editorがインストールされていれば「Menu Editorの設定を開く」ボタンを表示する。

Menu EditorのAPIをよく分かってないので(ていうかそもそも公開APIなのかどうかも知らない)、タブ選択時のメニューをカスタマイズできるようにするためにちょっと強引な方法を使ってる。

で、同じようなこと(他のアドオンの有無を調べた上で設定を開く)を何度も書きたくなかったので、設定ダイアログに加えた変更の要点を他のアドオンと連携しやすくするためのライブラリとして分離してみた。

if (window['piro.sakura.ne.jp'].extensions.isInstalled('my.extension.id@example.com') &&
    window['piro.sakura.ne.jp'].extensions.isEnabled('my.extension.id@example.com'))
    window['piro.sakura.ne.jp'].extensions.goToOptions('my.extension.id@example.com');

アドオンがインストールされているかどうか・有効か無効かを調べるだけならFUELで事足りるので、あまり使い出がないといえば使い出がない。まあThunderbird 2あたりでだったらニーズがあるかもだけど。

ちなみにFUELで書く場合、アドオンがインストールされているかどうかはApplication.extensions.has('my.extension.id@example.com')、有効か無効かはApplication.extensions.get('my.extension.id@example.com').enabledで分かる。設定ダイアログのChrome URLを調べるAPIはなくて、それでこのライブラリを作ることにした次第です。

Split Browser 0.6.2009050101(Firefox 2以下切り捨て) - May 01, 2009

分割ブラウザ更新した。Firefox 2捨てで、Firefox 3以降専用にした。おかげでJavaScriptコードモジュールとかの便利技術をためらいなく使えるようになった。最近になってアドオンを作り始めた人達がほんと羨ましい。あんな糞みたいな苦労をしなくても遙かに簡単に同じ事ができてより素晴らしい物を作れるんだから。羨ましいを通り越して妬ましくすらある。

更新履歴的には「ShiretokoとMinefieldで動くようにしました」で済ませてるけど、Firefox 2→Firefox 3で色々変わってた部分にかなり見落としがあったのを相当数直してる。ペイン内でのズームとかドラッグ&ドロップとか。

機能的な変更は以下の点くらい。

  • 分割の状態をpreferencesではなくセッションの方に保存するようにした。ウィンドウ単位で状態を保存するので、アドオンインストール後の再起動時やクラッシュからの復帰時はもちろんの事、Firefox 3.5以降の「最近閉じたウィンドウ」でも分割の状態までちゃんと復元されるようになった。
  • 閉じたペインを開き直す機能を加えた。デフォルトでは最大10回まで。開き直される位置は元通りではないので注意。
  • 分割後の領域のツールバー部分へのドラッグ&ドロップを、タブバーへのドロップと同等に扱うようにした。タブやリンクをツールバー部分にドロップするとタブで開く。

あと、地味にFirefox 3.5以降のプライベートブラウジングに対応してみた。プライバシーついでに、プライバシー情報の消去関係の機能とも統合を図ってる。この辺のノウハウはまたどっかで文書化したい(それか、勉強会のネタにするとか)。

swapBrowsersAndCloseOtherの変更への追従と、メソッドの動的な書き換えのメリット・デメリット - Apr 30, 2009

以前書いたtabbrowser要素のswapBrowsersAndCloseOther()メソッドの上書きの話で書いてたサンプルが使えなくなっていたので直した。というか自作アドオンの機能を使おうとして正しく動かなかったのでコードを改めて見てみて、メソッドの書き換えの対象になってる箇所に変更があった事に気付いた(4月16日に変更が行われていたようだ)。

以下は、Shiretoko 3.5b5preに合わせて修正した後のバージョンのサンプルコード。

if ('swapBrowsersAndCloseOther' in aTabBrowser) {
  eval('aTabBrowser.swapBrowsersAndCloseOther = '+
    aTabBrowser.swapBrowsersAndCloseOther.toSource().replace(
      '{',
      '$& MyAddonService.destroyTab(aOurTab);'
    ).replace(
      'if (aOurTab == this.selectedTab) {this.updateCurrentBrowser(',
      'MyAddonService.initTab(aOurTab); $&'
    )
  );
}

if (aOurTab == this.selectedTab)を目印にして「タブを入れ換えた後の再初期化処理」を入れてたんだけど、同じif文がこれより前の位置に増えたせいで、コードの挿入位置がずれてしまい、本来期待していた順番とは異なる順番で処理が行われてしまっていた。if文の中まで目印に含めるようにして一応回避したけど、これはこれで、if文の中を書き換えるアドオンがあると破綻してしまう可能性があるわけで……何かいい方法はないものだろうか。

今回の場合に限らず、「メソッドの入れ替えではなく動的な書き換えを行う」やり方には、こういう変更に逐一追従しなくてはならないという弱点がある。メソッドのインターフェース(引数の数や返り値)はそうそう変わらなくても、中身は結構頻繁に変わる。なので、気をつけておかないといけない。

もう1つの弱点として、書き換え対象のメソッドの中で参照されている変数がそのメソッドが定義された変数スコープからしか参照できないものであった場合に、それへの対処も必要となる。その変数の値がグローバルな名前空間からアクセス可能なものであれば、メソッドの書き換え時にその変数を同時に宣言し直してやればいいんだけど……

(function() {
  var attrName = 'foo';
  window.ExampleAddonService = {
    method : function(aNode) {
      aNode.setAttribute(attrName, true);
    }
  };
})();

// メソッドの書き換えには成功するが、attrNameという変数が
// 見つからなくなってしまうので、実行時にエラーになる
eval(
  'window.ExampleAddonService.method = '+
  window.ExampleAddonService.method.toSource().replace(
    '{',
    '{ AnotherAddonService.preProcess(aNode);'
  )
);

この例のような場合になると、もうお手上げ。

それでも動的なメソッドの書き換えの方を僕が積極的に使う最大の理由は、同じやり方でメソッドを書き換える他のアドオンとの互換性を維持するためだ。別名で元のメソッドを保持しておくやり方の場合、他のアドオンが元のメソッドの名前で関数オブジェクトを取得しても、その関数の内容は元のメソッドとは全然違うから、書き換えられなくて動かなくなる、という事態が起こり得る。それを避けるためには、上の例のようなお手上げな事例を除き、可能な限り元のメソッドを動的に書き換えた方が良いということになる。

予定もしくは野望もしくは妄想 - Apr 21, 2009

今の所、XUL/Migemoの辞書ファイルは全部普通のテキストファイルなんだけど、これをSQLiteデータベースに置き換えてみようかなと思ってる。メリットがあるのかどうかは分からないんだが。

現在の辞書周りは基本的にはplus7さんが作られた物を踏襲していて、起動時に全ての内容を読み込み、JavaScriptの普通の文字列としてオンメモリで保持しておくようになってる。でもこれだとFennecで動かすのって多分きついだろうなあ、とは思ってた。

どうなんだろう、SQLiteって関係ないデータはメモリに読み込まずにいてくれたりするんだろうか? Fennecのデフォルト設定を見た限りでは、履歴の保持日数はFirefoxと同じ90~180日となってるから、これを全部メモリ上に読み込んだらエラい事になるよねぇ。と考えると、必要最小限だけメモリに読み込むようになってる事を期待してると考えていいんだろうか? それとも、これは単にFennec開発陣がSQLiteを過信あるいはモバイルプラットフォームを甘く見てただけで、実は全部オンメモリになっちゃいますとか?

まあとりあえず実装するだけしてみて、それから考えてみよう……

……とりあえずやってみたけど、なんか、めさめさ重い……ちゃんと作ってないから単にどっかで無限ループしてるのかもだけど。しかしそれを抜きにしても、辞書が約4MBだったのがSQLiteにしたら9MBに膨れ上がってしまった。モバイルで使うなんてのはますます非現実的な領域だ。工夫しないと全然お話にならんね……

追記。もうちょっと進めてみたけど、余計泥沼に嵌ってる気がしてきた。バックエンドのSQLite化は無しだな、というのが現時点での結論ですわ。

ツリー型タブとVimperatorが衝突する - Apr 14, 2009

という報告を受けて調べてみて、解決策が見えなくて頭を抱えてる。

原因はFirefox 3以降でタブの拡張性が深刻なまでに低下したことに起因している。現在、Firefox 3でタブの中に独自の要素(ツリー型タブの「ツリーの開閉状態を示すアイコン」「折り畳まれたタブの数を表示するラベル」など)を追加しようとすると、バインディングを上書きしてやらないといけない。でも複数のアドオンでバインディングを上書き上書きとやってると、最後にバインディングを適用した誰かの物だけが生き残って、他のアドオンが追加したバインディングは全滅してしまう。今回の問題も、ツリー型タブがFirefox自身の提供するバインディングを上書きした後から、Vimperatorがさらにバインディングを上書きしているために、タブの要素構造がツリー型タブが想定する構造から変化して、初期化に失敗するようになってしまっている。

ツリー型タブマルチプルタブハンドラ情報化タブの3つを開発するにあたっては、この問題に対処するために、以下のような方針をとることにした。

  • それぞれが別個にバインディングを適用したら、お互いに主導権の奪い合いになる。
    • だから、適用するバインディングは「タブの中に要素を追加できる状態」を整えるためだけの物にする(三者でそれを共有する)。
      • その時の「タブの中に要素を追加できる状態」は、Firefox 2以前のバインディングと互換性がある物にする。
  • タブの中への要素の追加は、タブが追加された時のTabOpenイベントを拾って、スクリプトで動的に行う。

これを実現するために作ったのがこちらのライブラリ。3つのファイルで構成されてる。

3つを同じ所に置いてXULファイルだけをオーバーレイで読み込ませると、バインディングを上書きする。他のアドオンで同じライブラリが読み込まれてる時は、最新の物が使われる。

これをVimperatorの作者の人にも使ってもらえればいいんだけど……誰にコンタクトすればいいか分からないし、そもそも向こうにはわざわざこれを使う動機がない。Vimperatorユーザは基本的にVimperatorのカスタマイズですべてを解決する傾向にあるようだから、他のアドオンとの互換性が低くても問題にならないだろうし。

ていうか僕自身Vimperator使ってないから、わざわざ上記のような交渉をしようというモチベーションがない。というわけで、上に名前が出てる3つのアドオンのどれかとVimperatorとを共存させたいVimperatorユーザの人は、自分で作者にコンタクト取ってください。(丸投げ)

Page 12/239: « 8 9 10 11 12 13 14 15 16 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき

オススメ

Mozilla Firefox ブラウザ無料ダウンロード