[インデックス 12908] ファイルの概要
このコミットは、Go言語の実験的なHTMLパーサーライブラリ exp/html
における、HTML5仕様への準拠を目的とした修正です。具体的には、HTMLドキュメントのパース処理において、beforeHTMLIM
(before html insertion mode) ステートでDOCTYPE
トークンが検出された際の挙動を、仕様に合わせて「無視する」ように調整しています。
コミット
- コミットハッシュ:
b39bbf1e5b908070ee348c1ba6006da8ff1374c8
- Author: Andrew Balholm andybalholm@gmail.com
- Date: Wed Apr 18 13:26:35 2012 +1000
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b39bbf1e5b908070ee348c1ba6006da8ff1374c8
元コミット内容
exp/html: adjust beforeHTMLIM to match spec
Add case for doctype tokens (which are ignored).
This CL does not change the status of any tests.
R=golang-dev, nigeltao
CC=golang-dev
https://golang.org/cl/6061047
変更の背景
HTMLのパースは、非常に複雑なプロセスであり、Webブラウザが異なるHTMLドキュメントをどのように解釈し、表示するかを決定する上で極めて重要です。このプロセスは、W3CやWHATWGによって策定されたHTML仕様(特にHTML5)によって厳密に定義されています。
このコミットの背景にあるのは、exp/html
パッケージがHTML5のパース仕様に完全に準拠することです。HTML5の仕様では、パースツリー構築アルゴリズムの「挿入モード (insertion mode)」という概念が導入されており、ドキュメントの特定の段階で特定のトークンがどのように処理されるべきかが詳細に記述されています。
beforeHTMLIM
(before html insertion mode) は、HTML要素がまだ構築されていない、ドキュメントの初期段階を指します。このモード中にDOCTYPE
トークンが検出された場合、HTML5仕様ではそのトークンを「無視する」と規定されています。これは、DOCTYPE
宣言が通常、ドキュメントの冒頭に一度だけ出現し、その主な目的がブラウザに「標準モード」でレンダリングするよう指示することであるためです。このモードで再度DOCTYPE
が検出されることは、通常、不正なHTML構造を示しており、パースエラーとして扱われつつも、そのトークン自体はDOMツリーの構築には影響を与えないように無視されるべき、という仕様の意図があります。
このコミットは、exp/html
パッケージがこの特定の仕様要件を満たしていなかったため、その挙動を修正し、より堅牢で仕様準拠のHTMLパーサーを提供することを目的としています。
前提知識の解説
HTMLパースの基本
HTMLパースは、ブラウザがHTMLドキュメントを読み込み、それを表示可能なウェブページに変換するプロセスです。このプロセスは大きく分けて以下の2つのフェーズで構成されます。
- トークン化 (Tokenization): HTMLの生バイト列を、意味のある単位である「トークン」に分解します。例えば、
<p>
は開始タグトークン、Hello
はテキストトークン、</p>
は終了タグトークン、<!DOCTYPE html>
はDOCTYPE
トークンになります。 - ツリー構築 (Tree Construction): トークン化されたストリームを基に、DOM (Document Object Model) ツリーを構築します。DOMツリーは、HTMLドキュメントの論理的な構造を表すオブジェクトの階層です。
挿入モード (Insertion Modes)
HTML5のパースアルゴリズムの核心的な部分の一つが「挿入モード」です。これは、ツリー構築フェーズにおけるパーサーの状態を表します。パーサーは、現在の挿入モードと次に受け取ったトークンの種類に基づいて、次のアクション(DOMノードの作成、モードの変更、エラー処理など)を決定します。
主要な挿入モードには以下のようなものがあります。
initial
: ドキュメントの初期状態。DOCTYPE
トークンを期待します。before html
:<html>
タグの開始前。DOCTYPE
やコメント、空白文字などを処理します。before head
:<head>
タグの開始前。in head
:<head>
タグ内。in body
:<body>
タグ内。
DOCTYPEトークン
DOCTYPE
宣言(例: <!DOCTYPE html>
) は、HTMLドキュメントの最初の行に記述されることが一般的です。その主な目的は、ブラウザにドキュメントがどのHTMLバージョンに準拠しているかを伝え、それに応じて「標準モード (standards mode)」でレンダリングするよう指示することです。これにより、ブラウザ間のレンダリングの一貫性が保たれます。
HTML5では、<!DOCTYPE html>
という簡潔な形式が推奨されており、これは特定のDTD (Document Type Definition) を参照するものではありません。
exp/html
パッケージ
exp/html
は、Go言語でHTML5のパース仕様に準拠したパーサーを実装するための実験的なパッケージです。このパッケージは、Go標準ライブラリのhtml
パッケージの基盤となっています。HTMLドキュメントをトークン化し、DOMツリーを構築する機能を提供します。
技術的詳細
このコミットは、exp/html
パッケージ内のparse.go
ファイルにあるbeforeHTMLIM
関数に焦点を当てています。この関数は、パーサーがbefore html
挿入モードにあるときに呼び出され、次のトークンを処理します。
HTML5のパース仕様 (例えば、W3C HTML 5.2: 12.2.6.4.2 The "before html" insertion mode) では、before html
モードでDOCTYPE
トークンが受信された場合の処理が明確に定義されています。
A
DOCTYPE
token:Parse error. Ignore the token.
つまり、このモードでDOCTYPE
トークンが来たら、それはパースエラーではあるものの、そのトークン自体は無視して次の処理に進むべき、ということです。
コミット前のbeforeHTMLIM
関数は、DOCTYPE
トークンに対する明示的な処理ケースを持っていませんでした。そのため、このモードでDOCTYPE
トークンが来ると、おそらくデフォルトのケースや他のトークンタイプとして誤って処理される可能性がありました。
今回の修正では、switch p.tok.Type
文にDoctypeToken
のケースを追加し、その中で単にreturn true
とすることで、トークンを無視し、パーサーが次のトークンを読み進めるようにしています。これにより、exp/html
パーサーはHTML5仕様のこの特定の要件に完全に準拠するようになります。
この変更は、既存のテストのステータスを変更しないとコミットメッセージに記載されています。これは、この特定のケースが既存のテストスイートでカバーされていなかったか、あるいはこの変更が既存のテストの期待される出力を変更しないことを意味します。
コアとなるコードの変更箇所
--- a/src/pkg/exp/html/parse.go
+++ b/src/pkg/exp/html/parse.go
@@ -408,6 +408,9 @@ func initialIM(p *parser) bool {
// Section 12.2.5.4.2.
func beforeHTMLIM(p *parser) bool {
switch p.tok.Type {
+ case DoctypeToken:
+ // Ignore the token.
+ return true
case TextToken:
p.tok.Data = strings.TrimLeft(p.tok.Data, whitespace)
if len(p.tok.Data) == 0 {
コアとなるコードの解説
変更はsrc/pkg/exp/html/parse.go
ファイルのbeforeHTMLIM
関数内で行われています。
beforeHTMLIM
関数は、parser
構造体へのポインタp
を受け取ります。このp
は、現在のトークン (p.tok
) やパースの状態を保持しています。
変更前のコードでは、switch p.tok.Type
文の中にDoctypeToken
を処理するcase
がありませんでした。
追加されたコードは以下の通りです。
case DoctypeToken:
// Ignore the token.
return true
case DoctypeToken:
: 現在のトークンのタイプがDoctypeToken
である場合に、このブロックのコードが実行されます。// Ignore the token.
: コメントで、このトークンを無視するという意図が明確に示されています。return true
: この行が重要です。true
を返すことで、パーサーは現在のトークンの処理を完了し、次のトークンを読み込む準備ができたことを示します。これにより、DoctypeToken
はDOMツリーに影響を与えることなく、単にスキップされます。
この修正により、beforeHTMLIM
モードでDOCTYPE
トークンが検出された場合、HTML5仕様に従って適切に無視されるようになり、パーサーの堅牢性と仕様準拠性が向上します。
関連リンク
- Go CL 6061047: https://golang.org/cl/6061047
参考にした情報源リンク
- HTML Standard (WHATWG) - 13.2.6.4.2 The "before html" insertion mode: https://html.spec.whatwg.org/multipage/parsing.html#the-before-html-insertion-mode
- W3C HTML 5.2 - 12.2.6.4.2 The "before html" insertion mode: https://www.w3.org/TR/html52/syntax.html#the-before-html-insertion-mode
- HTML parsing process (MDN Web Docs): https://developer.mozilla.org/en-US/docs/Web/HTML/Parsing_HTML
- The DOCTYPE declaration (MDN Web Docs): https://developer.mozilla.org/en-US/docs/Glossary/Doctype
- HTML5 Parsing Algorithm: https://www.html5rocks.com/en/tutorials/internals/howbrowserswork/#The_parsing_algorithm
- htmlparser.info (HTML Parsing Visualizer): https://htmlparser.info/ (これは一般的な情報源であり、特定のコミットに直接関連するものではありませんが、HTMLパースの理解に役立ちます。)
- Python's
html.parser
documentation (for general understanding of parser methods): https://docs.python.org/3/library/html.parser.html (これも一般的な情報源であり、特定のコミットに直接関連するものではありませんが、パーサーの概念理解に役立ちます。)