2009年6月12日金曜日

ソースコードから公開識別子の扱いを調べる

「SGML Catalog」という投稿で「稿を改めて書いてみたい」と書いておきながら、一ヶ月が経ってしまった。言い訳をしたい気持ちをこらえて、一ヶ月遅れでも書いてしまうことにしよう。

もともとSGMLのDTDの話を書こうと思ったのは、HTMLでシステム識別子が省略可能かという話からであった(4月18日の投稿)。そのときにも書いたのだが、HTML/XHTMLには、SGML/XMLのインスタンスという側面と、Webに特化した(SGMLでもXMLでもない)データ記述フォーマットとしての側面とがあり、単純にSGML/XMLのルールをあてはめればいい、とは言い切れない。後者の側面を規定するのは、実際のアプリケーションの振舞いということになるだろう。非常に現実に沿った話である。

というわけで、実際にWebブラウザのソースコードを眺めて振舞いを調べたほうがいいだろうと思ったわけである。現状でブラウザのソースコードとして入手可能なのは、Mozilla(Firefox, Caminoなど)とWebkit(Safari, Google Chromeなど)である。この2つのソースコードをざっと眺めてみた。とはいえ、どちらのソースコードも巨大で、一朝一夕に全貌を理解できるような代物では到底ない。眺めたのはパーサ周りのごく一部に過ぎない。

まず、Mozilla, Webkitともに、HTML/XHTML専用パーサを使用している(Firefox, Safari 4 public beta)。汎用のSGMLパーサは内包されておらず、汎用XMLパーサもHTML/XHTMLのパースの時には用いられていないようだ。また、汎用SGMLパーサではないから、SGML Catalogのような仕組みも利用していない。ブラウザという目的を特化したソフトウェアだから、こういう構成なのは予想していたところである。

そして、Mozilla, Webkitともに、公開識別子に関するパーサの振舞いを規定するデータ構造PubIDInfoを内包していた。構造体の宣言も同じで、記述されている公開識別子も似たような感じになっている。いずれもオープンソースのプロジェクトだから、この辺は相互にソースコードを流用しているのかもしれない。

PubIDInfoにおけるパーサの振舞いには、FullStandards, AlmostStandards, Quirks, Quirks3の4種類がある。公開識別子と照らし合わせてみると、QuirksはHTML 4互換モード、Quirks3はHTML 3互換モードとされているようである。

PubIDInfoにはもう一つ面白い点がある。定義を一部転記してみよう。
eMode mode_if_no_sysid;
eMode mode_if_sysid;
システム識別子がある場合とない場合とで、パーサのモードが変えられるようになっているのである。

では、どのような場合にどのモードでパーサが実行されるのか。Mozillaの場合はnsParser.cpp、Webkitの場合はHTMLDocument.cppの中にコードが含まれている。コードの外見はかなり違うが、どうやら、モード決定のロジックはどちらも同じようだ。

簡単なのは MIME Type が application/xhtml+xmlなど、なんらかのXMLデータだと解釈される場合で、このときは FullStandards モードになる。

MIME Type が text/html の場合は、いろいろなパターンに分かれる。
  • 文書型宣言があり、公開識別子がない場合:FullStandards モード、文書型は HTML Strict。HTML 5の <!doctype html>などがこれに該当すると思われる。
  • 文書型宣言があり、内部サブセットがある場合:FullStandards モード、文書型は HTML Strict。
  • 公開識別子が上記 PubIDInfo のテーブルにない場合:FullStandards モード、文書型は HTML Strict。HTML 4.01 Strict や XHTML 1.1 などがこれに該当すると思われる。
  • HTML 4.01 Transitional, HTML 4.01 Framesetでシステム識別子がある場合:AlmostStandards モード、文書型は HTML Strict。
  • XHTML 1.0 Strict, XHTML 1.0 Frameset, XHTML 1.0 Transitional:システム識別子の有無にかかわらず AlmostStandards モード、文書型は HTML Strict。
これ以外は Quirks3 もしくは Quirks モードになる。例えば、文書型宣言がなければ Quirks モード、HTML 4.01 Transitional でシステム識別子がなければ Quirks3 モードになる。

もちろん、上に書いたのはパーサのモードについてのみ解析した結果であり、パーサの出力であるDOMや、レンダリング結果については、モードが異なれば結果が異なる可能性がある、としか言えない。それにしても、HTML 4.0以前とHTML 4.01とでモードが異なる、StrictとTransitional/Framesetでモードが異なる、MIME Typeでモードが異なる、などの結果は、私にとってはかなり新鮮であった。

ところで、こうやってソースを眺めてみると、当初の目的以外にいろいろなことに気づくことがある。まず、Webkitのほうが断然ソースコードが読みやすい。というか、Mozillaのコードはめちゃ汚い…AppleやGoogleがWebkitを採用したのも分かる気がする。

それから、謎の公開識別子がいくつも目につく。例えばこんなの:
-/w3c/dtd html 4.0 transitional/en
-//microsoft//dtd internet explorer 3.0 html//en
-//w3c//dtd html experimental 19960712//en
こういうのに対するパーサのモードが全部定義してあるのである。Webデザイナがブラウザのバグに悩まされているのと同じように、ブラウザのプログラマもHTML文書のバグに悩まされてるんだなあ、ということがよく分かる。