Home > Latest topics

Latest topics > 独自の文書型宣言を含めつつエラーを出さない方法(XHTML 1.1でXMLとして他の名前空間の要素を組み合わせて使いたい人向けの話)

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

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

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

独自の文書型宣言を含めつつエラーを出さない方法(XHTML 1.1でXMLとして他の名前空間の要素を組み合わせて使いたい人向けの話) - Mar 17, 2010

僕はXHTMLのルビ(小さな字で読み仮名をふったりするアレ)を使いたくてXHTML 1.1を選択してるんですが、過去にXHTML2で検討されてたl要素(パラグラフより細かい単位の「行」を示すための物)やiframeやなんかをどうしても埋め込みたくなって、しかしそのせいでバリデータでエラーが出てしまうのも嫌だったので、見よう見まねで頑張って独自の文書型宣言を書いてみたんですよ。いつやったのか忘れたけど、結構前に。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
    <!ENTITY % XLINK.xmlns.attrib "xmlns:xhtml2 CDATA #FIXED 'http://www.w3.org/2002/06/xhtml2'">
    <!ENTITY % Inline.extra "| xhtml2:l | iframe">

    <!ELEMENT xhtml2:l (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
    <!ATTLIST xhtml2:l 
      id    ID    #IMPLIED
      class CDATA #IMPLIED
      style CDATA #IMPLIED
      title CDATA #IMPLIED>

    <!ELEMENT iframe (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
    <!ATTLIST iframe
      id    ID    #IMPLIED
      class CDATA #IMPLIED
      style CDATA #IMPLIED
      title CDATA #IMPLIED
      longdesc     CDATA               #IMPLIED
      src          CDATA               #IMPLIED
      frameborder  ( 1 | 0 )           '1'
      marginwidth  CDATA               #IMPLIED
      marginheight CDATA               #IMPLIED
      scrolling    ( yes | no | auto ) 'auto'
      height       CDATA               #IMPLIED
      width        CDATA               #IMPLIED
    >
]>

CGIを使ってIEにはこの文書型宣言を出さないようにしてますが、FirefoxとかChromeとかでトップページあたりを開いてソースを見れば、こういうのが頭にくっついてるのが分かると思います。

で、バリデータ的にはこれでvalidになったのでめでたしめでたしだったんですが、GeckoのDTDパーサ部分にバグがあるらしくて、この文書型宣言を正しく解釈してくれないんですよね。最後の]>ではなくその前の>の部分で文書型宣言が終わったものと見なされてしまうせいで、Firefoxでこのサイトのページを開くと、ページの頭に謎の]>という文字列がくっついてしまう状態になってたのです。表示が崩れるのが嫌だったので、この]>がテキストノードとしてページの頭に存在してる時は動的に削除するようなJavaScriptを書いて、長らくごまかしてました。

そしたら先日、W3CのHTML5のルビに関する議論の中で紹介されてたXHTMLルビサポートのページを見たという方(Leif Halvard Silliさん)がメールを下さいまして、以下のようなハックを使えばページの頭に謎の]>が出てくる事を防げるよ、と教えてくれたんです。

<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
    <!ENTITY % XLINK.xmlns.attrib "xmlns:xhtml2 CDATA #FIXED 'http://www.w3.org/2002/06/xhtml2'">
    <!ENTITY % Inline.extra "| xhtml2:l | iframe">

    <!ELEMENT xhtml2:l (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
    <!ATTLIST xhtml2:l 
      id    ID    #IMPLIED
      class CDATA #IMPLIED
      style CDATA #IMPLIED
      title CDATA #IMPLIED>

    <!ELEMENT iframe (#PCDATA|br|span|em|strong|dfn|code|samp|kbd|var|cite|abbr|acronym|q|sub|sup|bdo|a|img|map|object|input|select|textarea|label|button|ruby|ins|del|script|noscript|xhtml2:l)*>
    <!ATTLIST iframe
      id    ID    #IMPLIED
      class CDATA #IMPLIED
      style CDATA #IMPLIED
      title CDATA #IMPLIED
      longdesc     CDATA               #IMPLIED
      src          CDATA               #IMPLIED
      frameborder  ( 1 | 0 )           '1'
      marginwidth  CDATA               #IMPLIED
      marginheight CDATA               #IMPLIED
      scrolling    ( yes | no | auto ) 'auto'
      height       CDATA               #IMPLIED
      width        CDATA               #IMPLIED
    >
<?parser-hack ><!-- ?>
]>
<!--><?!-->

強調箇所がそのハック。処理命令(PHPのコード片を埋め込んだりするのに使うのと同じ奴)の記法でコメントとして解釈できる文字列を埋め込んで、問題の部分を無視させるという物のようです。バリデータに通しても、これでもvalidです。素晴らしいです。

同じような事をやってる酔狂な人がもしいたら役立てて欲しいと思ったので、氏に許可を得てエントリにさせて頂きました。Thanks a lot, Leif!

文法的な解釈

何故これがvalidなのかも考えてみよう。

<?parser-hack ><!-- ?>
  • この行は、これ自体で1つの処理命令となる。
    • 処理命令は <?ターゲット名 任意のUnicode文字?> という書式なので、これはあくまで「><!-- という内容」を含んだ処理命令として解釈される。
  • 文書型宣言の定義では、[から]までの間に登場しうる内容として処理命令も含まれている。そして、上記の箇所は一つの完結した処理命令である。よって、文書型宣言中に登場しても何ら問題ない。
<!--><?!-->
  • この行は、「><?! という内容」を含んだコメントと解釈される。

という具合で文法的には何も問題ないので、W3Cのバリデータはこれをvalidと判断する。きちんとXMLパーサを実装しているブラウザ上でも同様です。

Geckoの解釈

一方、Geckoの解釈はどうか。これは「ソースを表示」での色分けを見るとよく分かる。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [

Geckoはまず、この部分だけで完結した文書型宣言と認識する。「[」で終わりと判断したのではなくて、その次の <!ENTITY が来た時点で「あ、もう文書型宣言は終わってたのね」と見なしてるようだ。

<!ENTITY ~>
<!ELEMENT ~>
<!ATTLIST ~>

この辺はすべて、それぞれ別々の宣言として解釈されている。という事で、

]>

何もハックをしない場合はこの部分だけが取り残されてしまって、Gecko的にはこれが「XML的には不正だけどSGML的にはアリ」なテキストノードとして扱われて、ゴミとして表示されてしまうわけだ。

では、ハック有りの場合はどうなるか。

<?parser-hack ><!-- ?>
]>
<!--><?!-->

Geckoはこれを3つのノードとして解釈している。

<?parser-hack >

まずGeckoは、これを1つの処理命令と判断する。XMLの仕様では ?> が来るまで処理命令の終わりにはならないんだけど、Geckoのパーサは > で処理命令が終わったものと見なしてしまう。

<!-- ?>
]>
<!-->

これは当然1つのコメントになる。

<?!-->

最後、これも <? から > までで1つの処理命令と見なされる。

という事で、ハック有りの時はどの文字もテキストノードとして取り残されずに済むので、画面上は何もゴミが表示されずに済む。

こんなのよく考えるなー、と、読み解いてみて改めて感心しました。

分類:Web技術 > XML, , , 時刻:23:54 | Comments/Trackbacks (0) | Edit

Comments/Trackbacks

TrackBack ping me at


の末尾に2014年1月19日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。例えば「noda」なら、「2010-03-17_parser-hack.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。

Post a comment

writeback message: Ready to post a comment.

2014年1月19日時点の日本の首相のファミリーネーム(ひらがなで回答)

Powered by blosxom 2.0 + starter kit
Home

カテゴリ一覧

過去の記事

1999.2~2005.8

最近のつぶやき

オススメ

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