たまに18歳未満の人や心臓の弱い人にはお勧めできない情報が含まれることもあるかもしれない、甘くなくて酸っぱくてしょっぱいチラシの裏。RSSによる簡単な更新情報を利用したりすると、ハッピーになるかも知れませんしそうでないかも知れません。
の動向はもえじら組ブログで。
宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。 以下の特設サイトにて、単行本まんがでわかるLinux シス管系女子の試し読みが可能!
オープンソースなライセンスとかコピーレフトなライセンスとかたくさんありすぎて違いがよく分かってなかった(自分で使ってるのに……)。特に、それぞれどう組み合わせることができてどういう組み合わせはアウトになるのか、どういう使い方は許されててどういう使い方は許されないのか、というあたりがボンヤリとしか分かってなかった(詳しい人にツッコまれたらその時対処しよう……という考え)。なので、可知さんの書かれた記事とかを読んで改めて調べてみた。
とりあえず大前提として、以下の説明はあくまでソフトウェアを作る側が開発から頒布までの過程でコードを使う、著作権法上の「利用」にあたる範囲の話で、そのソフトウェアで商売したり作品を作ったりという、著作権法上の「使用」の範囲には言及していません。つまり完全に開発者向けの文章。以下、混乱を招きそうなので「利用」の文脈の時は全て「利用」と書くことにします。
あと、この理解が間違ってる場合はブクマコメントとかでこっそり書くんじゃなくてこのエントリのコメントで指摘しておいてもらえると、このエントリをウッカリ見てしまった人がこのエントリの内容を信用しないで済むので、そうしてもらえるとうれしいです。
各地既報ですが、セキュリティ上の仕様変更によってmozIJSSubScriptLoaderで読み込めるスクリプトの置き場所がChrome内に限定されるようになった(File URLなどの読み込みは拒否されるようになった)そうで。まあ自分が作ってる拡張機能には外部のスクリプトを動的に読み込んでどうこうするものがあんまり無いのでそれほど影響は無いんですが、せっかくなので手持ちの情報を晒しときます。
実は僕、割と最近まで、mozIJSSuScriptLoaderで実行コンテキストを指定できるってことに気付いてなかったんですよね。だから、外部スクリプトは実行できてもその実行結果を取り出すとかはできないと思い込んでまして。Firefox 1.5以前から作ってたやつでデフォルト設定をdefault.jsとしてcontent内に置いてた物で、default.jsの内容を読み込ませるために、わざわざmozIJSSubScriptLoaderと同じ働きをするコードを書いてたんです。ああ車輪の再発明。そんなわけで以下の情報がもしかしたら参考になるかもしれません。
……あ。今気付いたけど(ぉぃ)、Chrome URL限定ってことはdata: URLもダメになったってことか。んじゃUXUもやっぱ影響受けるなぁ。さてどうしたものか。
とりあえずスクリプトを実行するだけだったら上記の方法で読み込んだファイルの内容をeval()
するだけでいいので、その点では話は簡単なんですけどね。
20日追記。最終的に、格好悪いやっつけの方法ではあるんだけど、こういう風な所に落ち着いた。UxU 0.2.6で採用した方法は以下の通り。
eval()
するだけのスクリプトをパッケージに含めておき、mozIJSSubScriptLoaderで読み込んで実行。実際のコードはこんな感じ。
var loader = Components
.classes['@mozilla.org/moz/jssubscript-loader;1']
.getService(Components.interfaces.mozIJSSubScriptLoader);
this.include = function(aSource, aEnvironment, aEncoding) {
var encoding = aEncoding || this.getPref('extensions.uxu.defaultEncoding')
var script = this.readFrom(aSource, encoding) || '';
var env = aEnvironment || this.environment;
env._subScript = script;
loader.loadSubScript(
'chrome://uxu/content/test/helper/subScriptRunner.js?includeSource='+
encodeURIComponent(aSource)+
';encoding='+encoding,
env
);
};
subScriptRunner.jsというファイルの内容は、たったこれだけ。
if (_subScript) eval(_subScript);
これで、mozIJSSubScriptLoaderで普通にスクリプトを読み込ませるのと同じような結果になる。
さらに追記。仕様が変更されて、file:とresource:は使えるようになったそうだ。でもdata:は相変わらずダメなので、今までdata:でやってたものはここに書いたような何らかの方法で代替するしかないと思う。
さらにさらに追記。evalInSandboxを使う方法もあるそうだ。
さらにさらにさらに追記。evalの機能についてFirefox 3.1でまた変更があったようだ。
ツリー型タブでタブバーの表示・非表示を切り替える時の画面のちらつきがUZEEEEEEEEEE!!というのは前々から把握してたんだけど、一時的に画面描画を止めるとかそういうのはJavaScriptのレイヤからは手が出せないっぽいので放置してた。AutoHideではC++あたりでXPCOMコンポーネントを作ってどうにかしてるようだけど、そんなん僕には作れないし。
でもよく考えたらHTML Canvas使って解決できるんじゃね? と思って、そういう物を作ってライブラリ化してみた。fullScreenCanvas.show()
を呼ぶと、今のウィンドウの表示内容のスクリーンショットを貼り付けたような状態のCanvasがウィンドウ内の要素の最前面に表示されます。画面がチラつくような処理をその下でやって、終わったらfullScreenCanvas.hide()
でCanvasを消す、という風にすると、ユーザにイヤンな思いをさせないでいろんな事ができるかも知れない。ブラウズ領域のスクロールバーの部分が描画されないのはどうにもならなかった。
以下、工夫した所。
XULとCSSのポジショニングの組み合わせは、Gecko 1.9でもバッドノウハウの塊ですね。
Firefox 3のタブにFirefox 2と同じプレースホルダーを復元するライブラリと併せて、MITライセンスでの公開とします。
追記。canvasついでに(意味不明)、タブバーの後ろにcanvasを置いてうそっこ透過タブバーを実現してみた(描画内容が微妙にズレることがあるのは勘弁してください)。これで、ページの閲覧を邪魔されずにタブバーを使えるようになるだろうか?
マルチプルタブハンドラによる複数タブのドラッグ&ドロップに対応するためのAPIの説明を書いた。拡張機能作者の人は、なんかおもしろい使い方を考えてください。
なお、このAPIはバージョン0.2.2008022701以降で利用可能です。
まーこんな記事誰も読みたがらんだろうとは思いますが。ここ数日取り組んでたマルチプルタブハンドラでの複数タブの同時ドラッグ&ドロップ処理について一段落付いたので、やったことのまとめを文章で残しておこうかなと。
さて。タブのドラッグ&ドロップの処理になんかの機能や処理を加えようと思ったら、まず思いつくのは、gBrowser(tabbrowser要素)をnsDragAndDropのオブザーバとして利用するために定義されているonDragStart、onDragOver、onDragExit、canDrop、onDrop、getSupportedFlavoursあたりのメソッドを上書きするというやり方。実際、ツリー型タブではそうしてる。Tab Mix Plusの場合はもいっこ上のレベル、ondraggestureなどのイベントハンドラを書き換えてnsDragAndDropに渡すオブザーバそのものを入れ換えるという事をやってるけど、まあ、これも要するに同じことですね。
ただ、マルチプルタブハンドラの場合はその方法は使いたくなかった、というか、使えなかった。自分の使い方ではツリー型タブとの組み合わせが必須なんだけど、そうなると、もしマルチプルタブハンドラの実装を同様の方法でやってしまったら、組み合わせて使うと二つの拡張機能が同じ場所(nsDragAndDrop用のメソッド)を互いに上書きしようとするわけで、これはもう衝突すること間違いなしなワケですよ。だから何としても別の方法を考えなきゃならんかった。
Firefox 3では更新情報の通知で具体的な説明文を配信できるということで、実験というか実践というか練習というかともかくそういう目的もあって、自前で公開してる方でもこの機能を使うようにしてみた。
問題は更新情報をどこに置くかということ。Firefox 3の新機能は、指定されたURLのXHTML文書をXSLTでサニタイズしてから表示するという物なので、既存のHTMLをそのまま参照させることは残念ながらできない。そこで、幸いというかなんというか、Rewind/Fastforward Buttons用に勝手SITEINFOを提供しようと思ってBlosxomで簡単なWikiっぽいものを作ったばかりだったので、これを流用して解決することにした。ここに更新情報を置いてflavourでapplication/xhtml+xmlで出力するようにしたあと、RewriteRuleで日本語版は日本語版・それ以外の言語は全部英語版に飛ばしている。早速戻る/進むサムネイルをアップデートしてみたところ、ちゃんと情報が表示されることが確認できた。
ところで、今までは更新情報のHTMLからupdate.rdfを生成するのに秀丸マクロを使ってたんだけど、このWikiっぽいものに更新情報を載せるというステップが加わるとさすがに秀丸マクロじゃ辛い。ということで、思い切ってMozilla用のJavaScriptに移植してみた。自作の偽DOM風サブルーチンじゃなくて本物のDOM、それもXPathとかバリバリ使いまくれるので、非常に楽だった。こんなだったらもっと早くにやっときゃ良かった。
McCoy用のアドオンにすればMcCoyだけで一連の作業を半自動で行えるようになるけど、今のところはまだそこまでたどり着けてない。そこは今後の課題ということで……
nsIMsgDBHdrとnsIMsgFolderからメールの本文を文字列として取得する方法をあれこれ試してみてこんな感じのところに辿り着きましたとさ。
mozIStorageConnectionのcreateFunctionメソッドを使うとユーザ定義関数を作れるそうなんだけど、Firefox 2だとユーザ定義関数から値を返せない(返り値が常にNULLになる)。値を返せるようになるのはFirefox 3からだそうな。
const DirectoryService = Components
.classes['@mozilla.org/file/directory_service;1']
.getService(Components.interfaces.nsIProperties);
var file = DirectoryService.get('ProfD', Components.interfaces.nsIFile);
file.append('mydatabase.sqlite');
var storageService = Components
.classes['@mozilla.org/storage/service;1']
.getService(Components.interfaces.mozIStorageService);
var dbcon = storageService.openDatabase(file);
// mozIStorageFunctionインターフェースを備えたオブジェクト
var func = {
onFunctionCall : function(aArgs) {
// 渡ってくるのはmozIStorageValueArrayのオブジェクト
var data = aArgs.getString(0);
var pattern = aArgs.getString(1);
return data.match(new RegExp(pattern)) ? true : false ;
}
};
// 引数の個数を「-1」にすると可変長引数の関数になる
dbcon.createFunction('match', -1, func);
var extracted = [];
var statement = dbcon.createStatement(
'SELECT * FROM mytable WHERE match(data_column, ?1)'
);
statement.bindStringParameter(0, '^foobar');
try {
while (statement.executeStep())
{
extracted.push(statement.getString(0));
}
}
catch(e) {
}
statement.reset();
alert(extracted.length); // 0
こんなの何に使えばいいんだ?
まあどうせPlacesはFirefox 3からだし、正規表現でのマッチングをやる必要が出てくるのはそれからだと思うんだけど。でもこのコードのようにJavaScript製ユーザ定義関数でマッチングするんだったら、結局は全ての行に対して処理が行われるわけで、それだったら先に全ての行を取り出してからループ回してマッチングするのと、処理速度的には全然変わらないよね……ほんと無意味。標準の関数で正規表現が使えればいいのになあ。
検索を開始したタイミングでヘッダ情報を一気に収集するという現状の仕様だと、フォルダ内に数千件とかメールが溜まってる人はまず間違いなく死亡するので、フォルダの内容を表示したタイミングでバックグラウンドでちまちまとヘッダ情報を収集するようにして、ついでに、その収集結果をMozStorageで保存するようにしてみてるんだけど。
今の所、一つのフォルダにつき一つのレコードとして、「Subjectだけ集めたもの」「Toだけ集めたもの」のような文字列をそれぞれフィールドに保存してるんだけど、これもあんまりでっかくなりすぎるとまずいですよねえ多分。仮にSubjectが10文字のメールが1000件あったとすると、20~40KBとかそれくらいになるのかな? 3000件もあれば100KBはいってもおかしくない。これって大丈夫なんだろうか。
えーと。SQLiteの制限事項によると、SQLiteデフォルトでの文字列の最大長は1ビリオン(1000000000)バイト……えーと、だいたい1GBくらい? Firefoxのソースツリーを検索してもSQLITE_MAX_LENGTHという文字列は登場しないようなので(というのは全然根拠にならんのだけど)、このデフォルト値のまんまなんだとしたら、パフォーマンスはさておきとりあえずこのままでも動くことは動くのかな?
というか別にSQLite使う必要は必ずしもないんですけどね。なんとなく最近無駄に多用する癖が……
XUL/Migemoでメールの検索をできるようにしてみたいな、と思って試してみた。
メールの検索はOR検索はできるようなので、キャラクタクラスやグルーピングを展開して単語のリストを生成してOR検索したらいけるかな?と思ってやってみたんだけど、ローマ字入力から単語のリストを作る所までで数百ミリ秒くらいの遅延というのはまあ許したとしても、そこから先の実際に検索にぶち込む所で重すぎてThunderbirdが落ちた。そりゃ9000ワードをOR検索とか、いくらC++で書かれた処理が速いといっても限度があるわ。
というわけで正攻法は無理なので別のやり方を考えないといかんですね。やっぱアレかな、検索対象のメールすべてのヘッダを一旦文字列に取り出してそこに検索をかけてから、ヒット箇所の単語を実際の検索に投げるという、いかにもXUL/Migemoらしい(plus7さんよくこんなやり方を思いついたもんだ)やり方で行くしかないのかな。
どーでもいいけどこの正規表現を単語のリストに変換する処理、自分で書いておきながら、どういう理屈で動いているのかさっぱり分からんです。
追記。前述したヘッダを一旦文字列に取り出して云々のやり方を実装してみたら、数十件程度ならわりとストレスなく動くようになった。1フォルダに数百件、数千件とかのメールが溜まってる環境だとやばいかもだけど。