Home > Latest topics

Latest topics 近況報告

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

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

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

Page 23/248: « 19 20 21 22 23 24 25 26 27 »

ローカルプロキシっぽいことをローカルプロキシを立てずにやろうとして挫折したことのまとめ - 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の発表資料作ることにします……

「英語(辞書アシスト検索)」モードは、英和辞書で検索する機能ではない - May 28, 2009

ローマ字入力で日本語ページ内の検索を可能にする「XUL/Migemo」拡張 - SourceForge.JP Magazine

さて、もう一つの検索方法である「英語(辞書アシスト検索)」であるが、これは英単語を入力するとその対訳となる日本語の単語が検索されるというモードだ。なぜか筆者の環境ではうまく機能しなかったのだが、一応使い方を説明しておく。

そんな機能実装した覚えないんですけど……

英語モードは、僕が正式にXUL/Migemoを引き継いだ後に行った大改造において、将来的に日本語だけでなく中国語や韓国語などのIMが必要な他の言語に対応できるよう、言語依存の主要モジュールを差し替え可能にした時に、日本語特有の処理(ローマ字入力を平仮名に変換するなどの処理)を含まないプレーンなモジュールの実装例として用意した物だ。

Firefox自身の通常の検索機能と比較して、このモードで具体的に起こる変化としては、「ext」まで入力した時点で「extension」「extend」等の英単語が強調表示されるようになる、という感じなんだけれども……これってぶっちゃけ無意味なんだよね。

  • 日本語では、入力した文字列(ローマ字)とページ内のヒット箇所(仮名漢字交じり)の文字列とが一致しない。「kanj」と入力した段階で登録済み単語の「漢字」にヒットしてくれれば、総キータイプ数が少なくなる。→メリットがある!
  • 英語では、入力した文字列とページ内のヒット箇所とが原則として一致する。なので、補完が無くても元々キータイプ数は最小で済む。→メリットが無い!

あとは、発音記号付きのアルファベット等を区別せずに検索できるようになるけど、それとて日本語用のエンジンに既に包含されている機能だし。

強いて言えば、日本語とかローマ字とか全然興味がないし必要も無いという英語圏のユーザの人が、Safari風の強調表示だけ利用する時に、ほとんど何も余計なことをしないから邪魔にならない……ということくらいだろうか。英語モードのメリットは。とにかく、「これこれこういう凄いことができるようになる」といった類の意義は皆無だ。そのモードを作った本人が言うんだから、間違いないよ。

Firefox 3.5の新アイコン、第7案はなかなか良いと思う - May 25, 2009

ノベルティやらパートナー企業の作るチラシやらが全部作り直しになるやんけ、という点を除けば、新しいドラフトはイイ感じに仕上がってきてると思う。

正直、初期のドラフトは端的に言って「え、これにリニューアル? ははは、またまたご冗談を」という印象しかなかった。「変えなきゃ、とにかく新しくしなきゃ」という意図が強く働いていなかったのか、何もかもを台無しにぶち壊していたように思う。それに比べたら最新のドラフトは、旧アイコンの解像度を上げた物として正当な進歩を遂げた形に落ち着いてると思った。カトキハジメの言うところの「マッハの戦い」とは、こういうことなんじゃないかと思う。

ここからまた変な方向に行ってしまわないかが心配だ。

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

parseTemplate() の引数で渡すオブジェクトのプロパティをテンプレート内のコード片で普通の変数として参照したい - May 20, 2009

前書き

PHPとかerbのようなテンプレートをJavaScriptで。という話に書いたやつの続き。

大切な事なので3回言います。
<% for (var i = 0; i < 3; i++) { %>
  今日は<%= today %>です。
<% } %>
オーケー?

こういう文字列をテンプレートとして解釈したい場面は多分よくあると思う。この例だと、todayという変数をどっか外部から与えて値を埋め込むことになる。で、この変数をどうやって渡したらええねん、と。

解1:グローバル変数を使う

グローバル変数をがんがんに使って構わないのであれば、先にグローバル変数としてtodayを定義しておけばいい。eval()で実行されるコードの中からも普通に参照できる。

しかしアドオンのコードのように、個々のグローバル関数やグローバル変数の寿命が長い事が予想されるスクリプトでは、この手は使えない。

解2:テンプレート内に書くコード片にthis.を付ける

parseTemplate()の仕様としてはあくまで「書かれたコード片は第2引数で渡されたオブジェクトをthisとして実行されます」という風にしておいて、テンプレート風に書かれている方の文字列内のJavaScriptコード片では必ずthis.todayという風に書くようにする、という方法もある。

しかしいちいちthis.を付けるのは面倒だし、そもそも僕がこのような記法を知ったきっかけであるERBでは、self.なんて書く必要がなかった。同じような物は同じように使えた方がいい。なのでこの方法も使いたくない。

解3:varで宣言し直す

先のエントリに当初書いていたコードでは、parseTemplate()の第2引数で渡したオブジェクト(ハッシュ)のプロパティを走査してそれをvarで変数として宣言し直す、ということをしてた。外の名前空間をなるべく汚さないための苦肉の策だ。

  if (aContext && typeof aContext == 'object') {
    for (var prop in aContext) {
      if (!aContext.hasOwnProperty(prop)) continue;
      __parseTemplate__codes.unshift('var '+prop+' = aContext.'+prop+';');
    }
  }

こんな感じ。

でも、これだとエラーになりそうな場合がけっこう考えられる。例えばハッシュのキーが01234という文字列だと、実行されるJavaScriptのコードはvar 01234 = aContext.01234;となってしまい、eval()にかけた時点でSyntaxError: missing variable nameと怒られてしまう。

なので、こういうエラーの元になる名前のプロパティを除外して、安全な物だけをvarで宣言する、ということをやりたかった。言い換えると、そのプロパティの名前を構成している文字列がJavaScriptの識別子として妥当かどうかを判別したかった。

ということでやっと、このエントリの本題に入る。

続きを表示する ...

PHPとかerbのようなテンプレートをJavaScriptで。 - May 19, 2009

UxU 0.5.11に入れ損ねた。次版で標準のヘルパーメソッドに入れるけど、割といいかげんな実装で、たいした規模じゃないから、テストケースの中に直接書いて使ってもいいと思う。

function parseTemplate(aCode, aContext) {
  var __parseTemplate__codes = [];
  aCode.split('%>').forEach(function(aPart) {
    var strPart, codePart;
    [strPart, codePart] = aPart.split('<%');
    __parseTemplate__codes.push('__parseTemplate__results.push('+
                                strPart.toSource()+
                                ');');
    if (!codePart) return;
    if (codePart.charAt(0) == '=') {
      __parseTemplate__codes.push('__parseTemplate__results.push(('+
                                  codePart.substring(1)+
                                  ') || "");');
    }
    else {
      __parseTemplate__codes.push(codePart);
    }
  });
  var __parseTemplate__results = [];
  with(aContext|| {}) {
    eval('(function() { '+__parseTemplate__codes.join('\n')+' }).call(aContext|| {})');
  }
  return __parseTemplate__results.join('');
}

var source = <![CDATA[
      大切な事なので3回言います。
      <% for (var i = 0; i < 3; i++) { %>
        今日は<%= today %>です。
      <% } %>
      オーケー?
    ]]>.toString();
var params = {
      today : (new Date()).toString()
    };
var result = parseTemplate(source, params);

レガシーだけどクロスブラウザな書き方だったら、こうか。

function parseTemplate(aCode, aContext) {
  var __parseTemplate__codes = [];
  aCode = aCode.split('%>');
  var strPart, codePart;
  for (var i in aCode) {
    aCode[i] = aCode[i].split('<%');
    strPart = aCode[i][0];
    codePart = aCode[i].length == 1 ? null : aCode[i][1] ;
    __parseTemplate__codes.push('__parseTemplate__results.push(unescape("'+
                                escape(strPart)+
                                '"));');
    if (!codePart) continue;
    if (codePart.charAt(0) == '=') {
      __parseTemplate__codes.push('__parseTemplate__results.push(('+
                                  codePart.substring(1)+
                                  ') || "");');
    }
    else {
      __parseTemplate__codes.push(codePart);
    }
  }
  var __parseTemplate__results = [];
  with(aContext|| {}) {
    eval('(function() { '+__parseTemplate__codes.join('\n')+' }).call(aContext|| {})');
  }
  return __parseTemplate__results.join('');
}

var source = '大切な事なので3回言います。\n'+
             '<% for (var i = 0; i < 3; i++) { %>\n'+
             '  今日は<%= today %>です。\n'+
             '<% } %>\n'+
             'オーケー?';
var params = {
      today : (new Date()).toString()
    };
var result = parseTemplate(source, params);

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

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

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

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

ツリー型タブとマルチプルタブハンドラのAPIドキュメントの追加 - May 16, 2009

not enough memory - snj: 最近,Firefoxはもっと拡張間で連携プレーした方が良いんじゃないかって思ってきた....

リンク先に書かれてる話からは少しズレる。どちらかというとこの前書いた互換性の話の方だ。

アドオン同士で連携するには、機能が使いやすくまとまってるということが当然必要だけど、それだけでなく、外部から安心して使えるということも非常に重要だと思う。公開されていて、今後のアップデートでも後方互換性が保証されていないと、安心して使えない。「ソースを掘り返してこういう機能を見つけたから使ってみてるけど、これって知らんうちに削除されてしまったりしないか?」という不安があってはいけない。機能が「今のバージョンにあるかないか」ではなく、「今後もあり続けるのかどうか」が重要だと思う。

ツリー型タブは以前からいくつかAPIを公開してはいたけど、基盤になる処理をもっと公開APIとして表に出すことにした。公開した物は原則として後方互換性を維持していく方針でいる。

マルチプルタブハンドラの方も地味にAPIを整備している。

もう僕は疲れたんで、この辺使ってよろしくやってくれってことで……

about:configで項目の絞り込みに正規表現やワイルドカードを使えるようになっていた - May 14, 2009

スペース区切りで入力したらAND検索する、という風なことができないかなーと思ってソースを見てみたら、Firefox 3からは正規表現リテラル(「/foobar/i」という風に書けば正規表現としてそのまま解釈される)やワイルドカード(「*」で0個以上の任意の文字にマッチする)を利用できるようになっていたということに今更気付いた。

今まで目grepしてた苦労はなんだったんだ。

他のアドオンと連携しやすくするためのライブラリを作ってみた - 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はなくて、それでこのライブラリを作ることにした次第です。

Page 23/248: « 19 20 21 22 23 24 25 26 27 »

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のコメント

最近のつぶやき