Nu Markup CheckerでもHTML 4とXHTML 1.0のチェック

先日、Nu Markup Checkerhttp://validator.nu/)でHTMLファイルをチェックするBrackets用の拡張機能を作りました(Nu Markup Checker for Brackets)。拡張機能自体はありきたりで、特に何というほどのものでもないのですが、この記事ではNu Markup Checker単独でHTML 4やXHTML 1.0のチェックを行う方法について述べたいと思います。

Nu Markup Checkerは主にHTML5のチェックに使われていますが、HTML 4とXHTML 1.0のチェックを行うこともできます。その際、HTML 4とXHTML 1.0に対しても単なるDTDのチェック(に相当するチェック*1)だけではなく、表の列数が行によって異なっていないかどうかやusemap属性値が妥当かどうかなどのチェックも行うことができます。ただし、そのためにはパラメーターをいくつか設定する必要があります。

tl;dr

(この記事執筆時点で)Nu Markup Checker単独でHTML 4とXHTML 1.0をチェックするためのパラメーターの組み合わせは次の通りです。

文書型 sniffdoctype parser preset
HTML 4.01 Strict yes 指定しない 指定しない
HTML 4.01 Transitional yes 指定しない 指定しない
XHTML 1.0 Strict 指定しない xmldtd http://s.validator.nu/xhtml10/xhtml-strict.rnc http://s.validator.nu/html4/assertions.sch http://c.validator.nu/all-html4/
XHTML 1.0 Transitional 指定しない xmldtd http://s.validator.nu/xhtml10/xhtml-transitional.rnc http://s.validator.nu/html4/assertions.sch http://c.validator.nu/all-html4/

DOCTYPE(sniffdoctype)

Nu Markup CheckerはRelaxNG、Schematron、Javaで書かれたチェッカーを組み合わせてファイルをチェックします(syntax)。この組み合わせは変更可能で、Nu Markup Checkerには最初からいくつかのプリセットが用意されています。デフォルトではHTML5用のプリセットでファイルをチェックします。ですが、Nu Markup Checkerには文書のDOCTYPEを見てチェック用プリセットを変更する機能があります。

Nu Markup Checkerへ渡すクエリにsniffdoctype=yesを付与すると、DOCTYPEがXHTML 1.0、HTML 4.01、HTML 4.0のTransitionalとStrictの場合、適切なプリセットが設定されます。sniffdoctypeなしではHTML5としてチェックされるHTML 4、XHTML 1.0も、sniffdoctype=yesを付けるとHTML 4やXHTML 1.0としてチェックされるようになります。例えば、HTML4やXHTML 1.0でa要素の中にブロック要素を記述していても、通常はスルーされますが、sniffdoctype=yesを付けるとエラーが報告されます。

ただし、この方法にもいくつかの欠点があります。

  • sniffdoctype=yesをつけてHTML5をチェックしようとするとシステムエラーが発生する
  • XHTMLもHTMLパーサーで処理される

HTML5をチェックしようとするとシステムエラーが発生する件は、DOCTYPEをみてパラメーターの付与を調整することで外から対応できますが、XHTMLをHTMLパーサーで処理する件はNu Markup Checkerで使われているパーサーの話なのでsniffdoctypeパラメーターでは設定できません。実際にsniffdoctype=yesを付けてXHTML 1.0 Strictをチェックしようとすると、次のような注意が表示されます。

Info: XHTML 1.0 Strict doctype seen. Appendix C is not supported. Proceeding anyway for your convenience. The parser is still an HTML parser, so namespace processing is not performed and xml:* attributes are not supported. Using the schema for HTML 4.01 or XHTML 1.0, Strict.

XHTMLがHTMLパーサーで処理されると次のような問題が起こります(Appendix CはXHTML 1.0のAppendix C)。

  • 名前空間をサポートしていないため、xml:lang属性などを認識できない(許可されていない属性であるというエラーが報告される)
  • HTMLとしてパースしているため、終了タグ忘れ(<meta>)などがエラーとして報告されない

xml:lang属性がエラーとして報告されるのは無視すれば良いのですが、報告されてほしいものが報告されないのは困ります。

パーサー(parser)とプリセット(preset)

ですが、Nu Markup Checkerはクエリにparserパラメータを指定することで、使用するパーサーを変更することができます。例えば、parser=htmlを指定するとHTML5パーサー、parser=xmldtdを指定するとXMLパーサーを使用します(通常はHTML5パーサー)。ただし、この方法で指定できるXMLパーサーにもいくつか注意すべき点があります。

  • XMLとしてエラーがあるとその時点で処理が終了する
  • XMLパーサーにはsniffdoctype機能はないため、チェックのプリセットを手動で設定する必要がある

XMLとしてエラーが見つかった時点で処理が終了するのはXMLパーサーとして一般的な挙動です。例えば、XMLパーサー使用時のNu Markup Checkerは、次のようなXHTML*2を渡されるとhead要素の終了タグが登場した時点で処理を終了します(meta要素の終了タグを見つける前にhead要素の終了タグが来たためです)。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Virtual Library</title>
</head>
<body>
<a href="http://example.org/"><p>Moved to example.org.</p></a>
</body>
</html>

この場合、body要素の子供としてa要素が記述されていることなどは報告されません。チェックツールとしてはいささか不親切ですが、ESLintも似たようなものなので、あきらめましょう(何)。XMLとしてwell-formedになればエラーが報告されるようになります。

もう1つのチェックのプリセットはNu Markup Checkerに含まれるテキストファイル(presets.txt)に記述されているため、同一の内容を外から指定することができます。テキストファイルは通常のプリセットとW3Chttp://validator.w3.org/nu/)用のプリセットがあります。

どの文書型にどのプリセットを使うべきかはファイルを見れば想像できますが、VerifierServletTransaction.javaに定数が定義されています(「XHTML1TRANSITIONAL_SCHEMA = 1」など)。(この記事執筆時点で)プリセットの番号と文書型の対応関係は次の通りです。

番号 文書型
1 HTML 4/XHTML 1.0 Transitional
2 HTML 4/XHTML 1.0 Strict
3 HTML5
7 XHTML 5

これをもとにW3C版のpresets.txtを見ると、HTML 4/XHTML 1.0 Transitionalであれば1の内容

http://s.validator.nu/xhtml10/xhtml-transitional.rnc http://s.validator.nu/html4/assertions.sch http://c.validator.nu/all-html4/

を指定すれば良く、HTML 4/XHTML 1.0 Strictであれば2の内容

http://s.validator.nu/xhtml10/xhtml-strict.rnc http://s.validator.nu/html4/assertions.sch http://c.validator.nu/all-html4/

を指定すれば良いことがわかります。これとparserの指定を組み合わせれば、XHTML 1.0をXHTML 1.0としてチェックさせることができます。ParserMode.javaにモードの列挙子が、VerifierServletTransaction.javaにparserパラメータの処理が記述されています。

VerifierServletTransaction.javaを見るとXHTML 1.0にはparser=xmldtdを指定すれば良いことがわかります*3。同じく、HTML 4にはparserパラメータを指定する必要がないことがわかりますし、sniffdoctype=yesで自動的にプリセットが割り当てらることがわかります。

まとめ

このように文書型に応じてパラメーターを調整すれば、Nu Markup Checker単独でもHTML 4とXHTML 1.0もチェックを行うことができます。W3C版のプリセットを使った場合のパラメーターの組み合わせは次のようになります。

文書型 sniffdoctype parser preset
HTML 4.01 Strict yes 指定しない 指定しない
HTML 4.01 Transitional yes 指定しない 指定しない
XHTML 1.0 Strict 指定しない xmldtd http://s.validator.nu/xhtml10/xhtml-strict.rnc http://s.validator.nu/html4/assertions.sch http://c.validator.nu/all-html4/
XHTML 1.0 Transitional 指定しない xmldtd http://s.validator.nu/xhtml10/xhtml-transitional.rnc http://s.validator.nu/html4/assertions.sch http://c.validator.nu/all-html4/

*1:Nu Markup CheckerはチェックにDTDを用いない

*2:XHTML 1.0の仕様にある例を改変

*3:paraser=xmlを指定すると、XMLパーサーが実体参照を解決しないため、XHTMLで定義されている文字実体参照(&copy;など)がエラーとして報告されます。