Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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つのフェーズで構成されます。

  1. トークン化 (Tokenization): HTMLの生バイト列を、意味のある単位である「トークン」に分解します。例えば、<p>は開始タグトークン、Helloはテキストトークン、</p>は終了タグトークン、<!DOCTYPE html>DOCTYPEトークンになります。
  2. ツリー構築 (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仕様に従って適切に無視されるようになり、パーサーの堅牢性と仕様準拠性が向上します。

関連リンク

参考にした情報源リンク