追悼 E4X (仮) 発表資料 ― 2013年05月27日 21時49分
Firefox 21 で E4X のサポートが削除されたのを受け、「追悼 E4X (仮)」というイベントが開催されました。東京での開催だったのですが、私自身仕様の邦訳を手がけるなど E4X には並々ならぬ想いがあり、京都から駆けつけた次第です。
来たからにはと私も「E4X と autovivification」という題で LT をしてきました。Perl でいうところの autovivification という機能が E4X にも備わっているという話です。ほかに「私と E4X」という発表 (むしろ自分語り) もしたのですが、こちらはその場限りのオフレコです。
追悼というだけあって皆さん E4X に対する熱い思いを語っていましたが、特に感心したのが Vimperator の対応の話です。それまで E4X を使っていた部分を、ECMAScript 6 での採用が検討されているテンプレートリテラルに置き換えたものの、それ自体現在の SpiderMonkey では実装されていません。そこで、chrome://liberator/....js
と JavaScript ファイルを読み込んでいたのを、liberator://....js
と独自プロトコルを介した読み込みに変更し、そのプロトコルハンドラの中でソースコード変換を行っているとのことでした。
私も製品のコード中で E4X を使っており、結果として後進に負の遺産を残すこととなってしまったのですが、一方で E4X があったから今の私があるというのもひとつの真実であり、E4X 仕様及びそれを実装した Mozilla に深く感謝します。
chrome URL からファイル一覧を取得する ― 2011年01月12日 01時41分
Firefox にて、ディレクトリを指す chrome URL から、そのディレクトリ以下の全ファイルの URL を返すサンプルコードを書きました。
ChromeFiles.get("chrome://browser/content/");
/* => [ "chrome://browser/content/NetworkPanel.xhtml",
* "chrome://browser/content/aboutDialog.css",
* ...,
* "chrome://browser/content/browser.css",
* "chrome://browser/content/browser.js",
* "chrome://browser/content/browser.xul",
* ... ]
*/
特徴として、実際のファイルが (.jar
または .xpi
に) パッケージ化されているかどうかに関わらず、ファイル一覧を取得できることが挙げられます。ソースコード全体は上記 Gist へのリンクを参照してもらうとして、以下は各関数の解説です。
var ChromeFiles = {
get: function CF_get(spec) {
const ios = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
let uri = ios.newURI(spec, null, null);
return this.getByURI(uri);
},
...
};
単に、文字列として受け取った URL を nsIURI
のインスタンスにして、getByURI()
へ処理を委譲しているだけです。
getByURI: function CF_getByURI(uri) {
// 1. ディレクトリを指す URL にする
let baseURI = uri.clone().QueryInterface(Ci.nsIURL);
baseURI.path = baseURI.directory;
// 2. chrome URL からローカルファイルシステムでの URL へ変換
const registry = Cc['@mozilla.org/chrome/chrome-registry;1'].
getService(Ci.nsIChromeRegistry);
let localURI = registry.convertChromeURL(baseURI);
// 3. ディレクトリ中のファイル名を取得
let leafNames = null;
if (localURI instanceof Ci.nsIFileURL) {
leafNames = this.getLeafNamesByDirectory(localURI.file);
} else if (localURI instanceof Ci.nsIJARURI) {
leafNames = this.getLeafNamesByJARURI(localURI);
} else {
throw new Error('Unknown URI: ' + localURI.spec);
}
// 4. ディレクトリのパスとファイル名を結合
let baseSpec = baseURI.spec;
return leafNames.sort().map(function (leafName) baseSpec + leafName);
},
-
chrome URL において、
chrome://{package}/content/
とchrome://{package}/content/{package}.xul
は同じリソースを表し、nsIIOService#newURI()
の引数に前者を渡しても返ってくる URI オブジェクトのspec
は後者になります。確実にディレクトリを指す URI オブジェクトを得るためには、自分で URI オブジェクトのプロパティを変更しなければなりません。ところが、
newURI()
で作られたオブジェクトは可変でない (immutable な) ことがあり、このときプロパティに値を設定しようとすると例外が発生します。chrome URL の場合clone()
で生成した URI オブジェクトは可変になるので、まずは URI オブジェクトを複製します。パスからディレクトリ部分だけを抜き出すのは、
nsIURL
インターフェースのdirectory
プロパティを使うのが簡単です。nsIURL
インターフェースを経由すれば、ディレクトリ以外にもファイル名や拡張子などをすぐに取得できます。 -
chrome URL からローカルファイルシステム上でのファイル位置をあらわす URL への変換は、
nsIChromeRegistry#convertChromeURL()
で一発です。これにより得られる URL は大抵の場合 file URL か jar URL かのいずれかです。 -
ディレクトリ中のファイル名一覧を配列として取得します。処理本体は file URL の場合と jar URL の場合で別になります。
なお、
QueryInterface()
を使わなくとも、instanceof
演算子でnsIFileURL
インターフェースを実装していることを確認できたなら、それ以降はnsIFileURL
のfile
プロパティからnsIFile
オブジェクトを取得できます。 -
得られたファイル名一覧の順序はわからないので、辞書順で並べ替えます。その後にディレクトリ部分を表す chrome URL と結合すれば、ディレクトリ直下のファイルを指す chrome URL の一覧が得られます。
getLeafNamesByDirectory: function CF_getLeafNamesByDirectory(dir) {
let files = dir.directoryEntries;
let leafNames = [];
while (files.hasMoreElements()) {
let file = files.getNext().QueryInterface(Ci.nsIFile);
if (file.isFile())
leafNames.push(file.leafName);
}
return leafNames;
},
ディレクトリを表す nsIFile
オブジェクトから、その子ファイルの名前一覧を取得します。単に子ファイルを列挙していき、それがディレクトリなどでないときにファイル名を取得するだけです。
getLeafNamesByJARURI: function CF_getLeafNamesByJARURI(jarURI) {
// 1. ZipReader を作成
let zip = this.openZipReader(jarURI.JARFile);
try {
// 2. ディレクトリ直下のファイルのパスを取得
let baseEntry = jarURI.JAREntry;
let pattern = baseEntry + '?*~' + baseEntry + '?*/*';
let entries = zip.findEntries(pattern);
// 3. ファイル名部分だけを抜き出し、返す
let leafNames = [];
while (entries.hasMore())
leafNames.push(entries.getNext().substring(baseEntry.length));
return leafNames;
} finally {
zip.close();
}
},
jar URI は jar:file://path/to/file.jar!/path/to/entry/
のような形で表されます。nsIJARURI
インターフェースは、file://path/to/file.jar
の部分を示す JARFile
プロパティ (返ってくるのは nsIURI
オブジェクト) と、/path/to/entry/
の部分を指す JAREntry
プロパティ (返ってくるのは文字列) を持っています。
-
JAR ファイル (または XPI ファイル) は ZIP 書庫なので、内部のファイル情報を読み取るためには
nsIZipReader
のインスタンスを作成し書庫を開く必要があります。 -
書庫内部のファイル名一覧を取得するには
findEntries()
を使います。ここで指定するファイル名のパターンにおいて、"?"
は任意の1文字を、"*"
は任意の文字列を、"pattern1~pattern2"
はpattern1
にマッチするがpattern2
にはマッチしないものを表します。"/path/to/entry/*"
というパターンでは/path/to/entry/
自信も含まれてしまうので、ディレクトリではないファイルだけを抽出するために"/path/to/entry/?*"
と指定します。また、それだけだと子孫ディレクトリ中のファイルも含まれるので、"/path/to/entry/?*/*"
を除外してやります。 -
findEntries()
で得られた値にはディレクトリ部分も含まれるので、その部分は切り取ってファイル名だけにします。
openZipReader: function CF_openZipReader(uri) {
let zip = Cc['@mozilla.org/libjar/zip-reader;1'].
createInstance(Ci.nsIZipReader);
if (uri instanceof Ci.nsIFileURL) {
// 1. file URL なら単にそのファイルを開く
zip.open(uri.file);
} else if (uri instanceof Ci.nsIJARURI) {
// 2. jar URL なら JAR ファイル内部のファイルを開く
let innerZip = this.openZipReader(uri.JARFile);
zip.openInner(innerZip, uri.JAREntry);
} else {
throw new Error('Unknown URI: ' + uri.spec);
}
return zip;
},
-
JAR ファイルがローカルファイルシステム上に直接存在するなら、単に
open()
メソッドにファイルオブジェクトを渡して開くだけです。 -
開こうとする JAR ファイルが別の書庫内に存在することもあります。その場合は
openInner()
に、JAR ファイルが含まれる書庫と、その書庫内での JAR ファイルのパスを指定してやります。nsIZipReader#openInner()
は Firfox 4 で追加されたものですが、Firefox 4 より前では jar URL がネストすることはないといっていいので、ここで使っても問題ないでしょう。
たとえば Firefox 4 Beta でツリー型タブを使うと、chrome://treestyletab/content/
の実体は jar:jar:file://{profile}/extensions/[email protected]!/chrome/treestyletab.jar!/content/treestyletab/
といった URL になりますが、上記のようにすればその内部のファイル構成を知ることができます。
また、resource://gre/modules/XPCOMUtils.jsm
といった resource URL に関しても、nsIResProtocolHandler#resolveURI()
を使えばローカルファイルシステム上の URL へ変換でき、上と同様にファイル一覧の取得などが可能になります。
Firefox Developers Conference 2010 ― 2010年12月28日 00時18分
Firefox Developers Conference 2010 に行ってきた。全体のまとめとしては以下が詳しい。
- Firefox Developers Conference 2010 アウトラインメモ | Web scratch
- nakamura001 @ ウィキ - Firefox Developers Conference 2010
はてなブックマークで fxdevcon タグがつけられたエントリーを見てまわるのもいい。Firefox 4 ベータ版機能概要では新しいタブインターフェース "Panorama" の紹介動画を見られる。
内容に関しては上述のページを参考にしてもらうとして、個人的に感じたことををいくつか挙げる。
Beyond Firefox 4 (Jay Sullivan)
Mozilla はユーザーの選択肢を広めることを重視する、逆に言えば単一のプラットフォームを目指すわけではないということのようだ。
John Resig feat. Shibuya.js
10+1 Things you should know about JavaScript testing (t_wada)
私の場合、Web ブラウザ向け JavaScript のテストを書いていないのはさくっとテストできる環境を知らないからというのが大きい。Firefox 拡張に関しては UxU という素晴しい環境があったから部分的とはいえテストを書けた。
カスタムイベントで処理をつなぐというのは結構やる。イベントモデルの実装も何回かやったけど、jQuery が使えるなら jQuery の実装 (bind、trigger) を使うのが楽だ。カスタムイベント名には Progress Events 仕様で提案されているものを流用したほうがいいかと思ったが、trigger 時に独自引数を渡すならかえって紛らわしいかもしれない。
// 独自のオブジェクトに jQuery のイベントモデル実装を組み込む例
function Loader() {
this.$ = $({});
}
$.extend(Loader.prototype, {
method: function () {
this.$.trigger('load', args);
}
});
var loader = new Loader();
loader.$.bind('load', function (args) {});
Node.js にまつわる 7 つの誤解 (meso)
現在、Node.js の開発は個人の手を離れ Joyent 社主導で行われているそうだ。会社によってはライブラリ/フレームワークの採用基準に、それが一定規模の団体によって保守されているかどうかが含まれるだろうから、Node.js の普及を促進する上ではよいことだと思う。
トークセッション: HTML5 時代の技術で Web プラットフォームはどう変わるのか (矢倉眞隆、村岡正和、浅井智也)
W3C CSS working group ではレイアウト関係が要注目とのこと。Flexible Box Layout Module は Mozilla の XUL ボックスモデルに由来するもので、(Mozilla から Apple へ移籍した Dave Hyatt の手により) 類似のモデルが WebKit でも実装されている。ただし、現在の草案は display プロパティの値に "box" ではなく "flex" を採用するなど、両実装と異なる点が多い。Grid Alignment Module は Microsoft が中心となって策定を進めている (編者の一人 Alex Mogilevsky は Flexible Box Layout Module の編者も兼任)。両モジュールともアプリケーション UI の整形を念頭に置いているが、手軽さでは Flexible Box、柔軟性では Grid Alignment が勝っているように感じる。
まとめとして、矢倉氏は描画には canvas だけでなく SVG、通信には WebSocket だけでなく Server-Sent Events など、あることを実現するのにさまざまな手段があるのを知ってほしいと、村岡氏は積極的に HTML5 を書き、関連 API を使って開発し、そして仕様策定者側にフィードバック (提案) しようと述べていた。
大ライトニングトーク
HTML 2.0 (TAKESAKO)
"2.0" というのはバズワード。HTML パーサのエラー処理方法の違いを利用してブラウザを判別する試みである。HTML5 では HTML 構文の解析方法がエラー処理も含めて規定されるので、今後ブラウザが HTML5 HTML 構文に対応していく中でこのような試みは困難になっていくと思われる。
placeholder 実装マニアックス
テキスト入力欄に何らかのメッセージを表示し、入力欄にフォーカスするとそれが消えるという UI は以前から見られた。HTML5 では placeholder 属性でこのメッセージを指定できるが、未対応ブラウザでこの挙動を再現しようとすればスクリプトを使う必要がある。
まず考えられるのは入力欄の value プロパティにメッセージの文面を設定し、フォーカス移動に際してこれを消去することである。しかしこの場合、フォーム送信時のメッセージ消去や、ブラウザがフォームコントロールの入力値を記憶することへの対処が必要となる。
別の方法として、CSS を使いメッセージと入力欄を重ね合わせることも可能だ。メッセージ部分を絶対配置の要素とし、文書木上では入力欄の直前に挿入する。left 及び top プロパティを指定しなければ絶対配置の要素は「その要素が絶対配置でなかったときの位置」に置かれる。この絶対配置要素の初期位置は意外と便利で、top、left、right、bottom プロパティのいずれかひとつのみ指定すれば、水平方向または垂直方向にだけずらすこともできる。
なお、placeholder に指定するのは入力欄に関する補助的な情報 (なくても問題ないもの) であり、入力欄に対するラベルではない。ラベルは label 要素で指定する。中には入力中に参照したい情報もあるだろうし、状況によっては placeholder の内容が提示されないこともあるようなので、そもそも placeholder を使うべき場面なのか検討したほうがいい。
懇親会
食事があっという間になくなってしまったのが残念だった。
ブラウザの進化速度は速い。できることはどんどん多くなるが、それらをどう組み立てていくかというパターンはまだ不安定に思える。その部分を探っていきたい。
Mozilla 勉強会 @ 大阪 ― 2010年03月03日 04時53分
2 月 20 日に行われた Mozilla 勉強会 @ 大阪に行ってきました。「js-ctypes で音声読み上げ」という題でライトニングトークをしたので、そのスライドとサンプルコードを公開します。
- js-ctypes で音声読み上げ
- read-selection.uc.js (userChrome.js 用スクリプト)
js-ctypes は C/C++ 用のライブラリ関数を JavaScript から呼び出すための機能で、Firefox の次期バージョンに搭載される予定です。ここでは「AquesTalkとkakasiを組み合わせて再生。 - 世界はアルゴリズムでできている。」を参考に音声読み上げライブラリを呼び出してみました。
上記コードでは標準 C ライブラリ関数を使って無理やりポインタを扱っていますが、Overview of js-ctypes や MozillaWiki の js-ctypes のページを見るに、将来的には標準でポインタや構造体、コールバック関数を使えるようにするみたいです。
IME の実装の概要について (中野雅之さん)
現在 Mozilla で IME 周りのコードを書いているのは中野さんだけだそうです (レビュワは Windows と Linux に一人ずつ)。「TSF を使う (1) - Windows Input Method の歴史 - NyaRuRuの日記」によると Mozilla は IME 関係の描画を自前で行っているそうで、それを一人でやり続けているというのには頭が下がります。
研究プラットフォームとしての Firefox の可能性 (山本岳洋さん)
研究の一環として Rerank.jp を作っている方です。当初は C# でアプリケーションを作成していたが、いちいち起動するのが面倒で自分でさえも使わなくなってしまったとのこと。そういう点では、常に起動しているアプリケーション実行環境で、おまけにクロスプラットフォームというのはとっつきやすいのかもしれませんね。
ちなみに私が知る限りでは、ほかにも次のような研究が Firefox (Mozilla) を使っています。
- Web ブラウザと融合するインスタントメッセンジャー「むすび」
- 大画面ディスプレイ上でのウェブブラウジングにおける効率的タスク切り替え支援手法 (PDF)
- ブラウザの履歴を二次元的に記録・表示する「Web Map」
Jetpack Reboot (あかつかだいすけさん)
今 Jetpack は基礎から作り直している最中だそうです。Jetpack のランタイムを各フィーチャーが含んで、個別の「拡張機能」として扱えるようになり、またそれに伴い addons.mozilla.org (AMO) での公開手順も整備中とのこと。ある程度広まって問題点も明らかになり、それでいて 1.0 も出ていないこの時期は、まさに「再起動」にぴったりだと思います。
modest と研究と勉強会
Mozilla/Firefox の開発情報を集約する場として、Mozilla Developer Street (modest) を積極的に使ってほしいという話がありました。Firefox を研究に使うのと同時に、modest を大学で使っていくのもありだそうです。
また、このような勉強会を今後も定期的に開いていくそうですが、大学での開催も可能とのことです。Firefox を研究に使いたいけれど開発方法がわからないという方は、modest で声を上げてみてはいかがでしょうか。
Firefox 3.6 での XPath による要素取得 ― 2010年01月24日 22時44分
Firefox 3.6 にしたら、動的に生成した文書からの XPath による要素取得ができなくなったという報告が挙がっています。
- Firefox3.6でdirect_bookmark.jsとdirect_hb.jsのはてブのタグを取得できていない問題(未解決→解決) - ヴィンペラートル・オクタウィアヌス - vimperatorグループ
- subscldr.jsが動かなくなったのを直してみた - mountain_dewの日記
この原因は、Firefox 3.6 で HTML 要素の名前空間の扱いが変わったことにあります。Firefox 3.6 (Gecko 1.9.2) では、HTML5 に従い、HTML 要素が (XML 文書中でなくても) XHTML の名前空間 (HTML5 でいうところの「HTML の名前空間」) http://www.w3.org/1999/xhtml
に属するようになりました。
これに伴って、これまでは XML 文書中でも有効だった、XPath 式の評価における HTML 要素の特別扱い (要素名の大文字小文字を区別しない、非修飾名を HTML の名前空間に属するものとみなす) が、HTML 文書中でのみ有効となったようです。なお、この特別扱いは、XPath 1.0 に違反しますが、HTML5 で認められています。
document.implementation.createDocument(null, 'html', null)
で作成されるのは XML 文書なので、接頭辞なしの XPath 式では HTML 要素を取得できなくなってしまいました。このことは、「HTMLDocument の動的な生成」でも、(こちらは Firefox 3.7a ですが) createDocument
メソッドでの名前空間なし XPath の結果が×になっていることで確認可能です。
解決策としては、HTML 文書を作成するようにすればいい話です。これには XSLT の HTML 出力を使ってもいいのですが、なぜか今現在の Firefox nightly (3.7a1pre 20100123) ではこの方法がうまくいかないので、createDocument
メソッドに文書型宣言と名前空間を指定するほうをおすすめします。
ちなみに AutoPagerize では document.cloneNode
メソッドを使っていますが、これは読み込む文書が基本的に同じ Web サイト内のものであり、現在の文書が HTML 文書なら次も HTML 文書、XML 文書なら次も XML 文書と推定できるからです。XML 文書から HTML 文書を読み込む場合など、汎用性を考えるならやはり createDocument
メソッドを使ったほうがいいでしょう。
最近のコメント