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

[インデックス 10287] ファイルの概要

このコミットは、Go言語のhtmlパッケージにおけるHTMLパーサーの挙動を修正し、非標準の<image>タグを標準の<img>タグとして扱うように変更するものです。これにより、一部のHTMLテストケースが正しくパースされるようになります。

コミット

commit ce4eec2e0acf9ec36c34bd42e81bbb2e32f18b81
Author: Andrew Balholm <andybalholm@gmail.com>
Date:   Wed Nov 9 09:43:55 2011 +1100

    html: treat <image> as <img>
    
    Pass tests1.dat, test 90:
    <p><image></p>
    
    | <html>
    |   <head>
    |   <body>
    |     <p>
    |       <img>
    
    Also pass test 91:
    <a><table><a></table><p><a><div><a>
    
    R=nigeltao
    CC=golang-dev
    https://golang.org/cl/5339052

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/ce4eec2e0acf9ec36c34bd42e81bbb2e32f18b81

元コミット内容

このコミットの目的は、HTMLパーサーが<image>タグを<img>タグとして認識し、処理するようにすることです。これにより、tests1.datのテスト90(<p><image></p><p><img>としてパースされるべきケース)がパスするようになります。また、テスト91(<a><table><a></table><p><a><div><a>という複雑なネスト構造のケース)もパスするようになったと述べられています。

変更の背景

HTMLの仕様において、画像を埋め込むための標準的なタグは<img>です。しかし、一部の古いブラウザや非標準的なHTML生成ツールでは、<image>というタグが使用されることがありました。これはSVG(Scalable Vector Graphics)の<image>要素とは異なり、HTMLの文脈で<img>の代替として使われるケースです。

Go言語のhtmlパッケージは、HTML5のパースアルゴリズムに準拠することを目指しています。HTML5の仕様では、未知の要素や非標準の要素がどのように扱われるかについても定義されています。このコミットは、このような非標準的な<image>タグが入力として与えられた場合に、それを一般的な<img>タグとして解釈することで、より堅牢で互換性の高いHTMLパースを実現することを目的としています。これにより、ウェブコンテンツの多様性に対応し、予期せぬ入力に対しても適切なDOMツリーを構築できるようになります。

前提知識の解説

HTMLパーシング

HTMLパーシングとは、HTMLドキュメントの文字列を読み込み、それをブラウザが理解できる構造化されたデータ(通常はDOMツリー)に変換するプロセスです。このプロセスは、HTMLの仕様に厳密に従って行われますが、ウェブ上には仕様に準拠しない「壊れた」HTMLも多数存在するため、パーサーはエラー回復メカニズムを備えている必要があります。

HTML5パーシングアルゴリズム

HTML5の仕様には、HTMLドキュメントをパースするための詳細なアルゴリズムが定義されています。これは、従来のSGMLベースのパーシングとは異なり、より堅牢で、エラー耐性があり、既存のウェブコンテンツとの互換性を重視しています。このアルゴリズムは「トークナイゼーション」と「ツリー構築」の2つの主要なフェーズに分かれます。

  1. トークナイゼーション (Tokenization): 入力されたHTML文字列を、タグ、属性、テキストなどの「トークン」に分解します。例えば、<p class="foo">Hello</p>は、開始タグトークン(pclass="foo")、テキストトークン(Hello)、終了タグトークン(p)に分解されます。
  2. ツリー構築 (Tree Construction): トークナイザーから受け取ったトークンを基に、DOMツリーを構築します。このフェーズでは、要素のネスト規則、スコープ、挿入モード(insertion mode)といった複雑なルールが適用されます。

挿入モード (Insertion Mode)

HTML5のツリー構築アルゴリズムの重要な概念の一つに「挿入モード」があります。これは、現在のパーサーの状態に応じて、次に受け取るトークンをどのように処理するかを決定するものです。例えば、inBodyIM("in body insertion mode")は、パーサーが<body>要素の内部にいるときに適用されるモードであり、このモードでは様々なHTML要素がどのようにDOMツリーに挿入されるかが定義されています。

<image>タグと<img>タグ

  • <img>タグ: HTMLの標準的な画像埋め込みタグです。src属性で画像ファイルのURLを指定し、alt属性で代替テキストを提供します。
  • <image>タグ: HTMLの標準仕様には存在しない非標準のタグです。歴史的に一部のブラウザやツールが<img>の代わりにこれを使用することがありました。SVG(Scalable Vector Graphics)には<image>要素が存在しますが、これはHTMLの文脈とは異なります。HTMLパーサーは、このような非標準タグをどのように扱うか決定する必要があります。多くの場合、未知のタグはインライン要素として扱われるか、あるいは特定の既知のタグにマッピングされることがあります。このコミットでは、後者のアプローチ、つまり<image><img>にマッピングする選択をしています。

技術的詳細

このコミットの技術的な核心は、HTMLパーサーのツリー構築フェーズにおけるトークン処理の変更です。具体的には、src/pkg/html/parse.goファイルのinBodyIM関数内で、トークンが<image>である場合に、そのトークンのデータ(タグ名)を"img"に書き換える処理が追加されています。

HTML5のパーシングアルゴリズムでは、トークナイザーが生成したトークンは、現在の挿入モードに基づいてツリー構築器に渡されます。inBodyIMは、パーサーがHTMLドキュメントの<body>セクションを処理している際の主要な挿入モードです。このモードでは、様々なHTML要素の開始タグがどのように処理されるかが定義されています。

変更前は、<image>のような非標準タグがinBodyIMで検出された場合、おそらくdefaultケースにフォールバックし、未知の要素としてDOMツリーに追加されていたと考えられます。しかし、このコミットにより、<image>タグが検出されると、明示的にそのタグ名が"img"に変換されます。これにより、パーサーは後続の処理でこの要素を標準の<img>タグとして扱い、適切なDOMノリーフ(<img>要素)を生成します。

このアプローチは、HTML5の「quirks mode」や「standards mode」における互換性処理、あるいは「error handling」の一部として見ることができます。非標準の入力を標準的な出力に正規化することで、パーサーの堅牢性と互換性を向上させています。

parse_test.goの変更は、この新しい挙動を検証するためのテストケースの追加と、既存のテストスイートの更新を示しています。tests1.datのテストケース数が89から92に増えたことは、テスト90と91が新たにカバーされるようになったことを意味します。

ただし、renderTestBlacklist<a><table><a></table><p><a><div><a>が追加されている点には注意が必要です。コミットメッセージでは「Also pass test 91」とありますが、これは「パースは通るようになったが、レンダリングテストではまだ問題があるためブラックリストに登録する」という意味合いである可能性があります。HTMLの複雑なネスト構造、特にインライン要素とブロック要素、テーブル要素の組み合わせは、ブラウザのレンダリングエンジンにとって非常にトリッキーなケースであり、パースが成功してもレンダリングが意図通りにならないことは珍しくありません。このブラックリストへの追加は、パースの正確性を確保しつつも、レンダリングに関する既知の問題を一時的に回避するための措置と考えられます。

コアとなるコードの変更箇所

src/pkg/html/parse.go

--- a/src/pkg/html/parse.go
+++ b/src/pkg/html/parse.go
@@ -651,6 +651,9 @@ func inBodyIM(p *parser) (insertionMode, bool) {
 		case "base", "basefont", "bgsound", "command", "link", "meta", "noframes", "script", "style", "title":
 			return useTheRulesFor(p, inBodyIM, inHeadIM)
+		case "image":
+			p.tok.Data = "img"
+			return inBodyIM, false
 		default:
 			// TODO.
 			p.addElement(p.tok.Data, p.tok.Attr)

src/pkg/html/parse_test.go

--- a/src/pkg/html/parse_test.go
+++ b/src/pkg/html/parse_test.go
@@ -133,7 +133,7 @@ func TestParser(t *testing.T) {
 		tn int
 	}{
 		// TODO(nigeltao): Process all the test cases from all the .dat files.
-		{"tests1.dat", 89},
+		{"tests1.dat", 92},
 		{"tests2.dat", 0},
 		{"tests3.dat", 0},
 	}
@@ -210,6 +210,7 @@ var renderTestBlacklist = map[string]bool{
 	// The second <a> will be reparented to the first <table>'s parent. This
 	// results in an <a> whose parent is an <a>, which is not 'well-formed'.
 	`<a><table><td><a><table></table><a></tr><a></table><b>X</b>C<a>Y`: true,
-	// The second <a> will be reparented, similar to the case above.
+	// More cases of <a> being reparented:
 	`<a href="blah">aba<table><a href="foo">br<tr><td></td></tr>x</table>aoe`: true,
+	`<a><table><a></table><p><a><div><a>`:                                     true,
 }

コアとなるコードの解説

src/pkg/html/parse.goの変更

inBodyIM関数は、HTMLパーサーが<body>要素の内部でトークンを処理する際のロジックを定義しています。追加されたcase "image":ブロックがこのコミットの核心です。

  • case "image":: これは、トークナイザーが<image>という開始タグトークンを生成した場合に実行されるコードパスです。
  • p.tok.Data = "img": ここで、現在のトークン(p.tok)のデータ(タグ名)が"image"から"img"に書き換えられます。これにより、パーサーは後続の処理でこの要素を標準の<img>タグとして扱います。
  • return inBodyIM, false: この行は、現在の挿入モード(inBodyIM)を維持し、次のトークンを処理するためにツリー構築アルゴリズムの次のステップに進むことを示します(falseは、現在のトークンが再処理されるべきではないことを意味します)。

この変更により、非標準の<image>タグが入力として与えられても、GoのHTMLパーサーはそれを標準の<img>タグとして解釈し、DOMツリーに組み込むことができるようになります。

src/pkg/html/parse_test.goの変更

  • {"tests1.dat", 89}から{"tests1.dat", 92}への変更: TestParser関数内のtestsスライスは、テストデータファイルと、そのファイルから実行するテストケースの数を指定しています。この変更は、tests1.datファイル内のテストケースのうち、以前は89番目までしか実行していなかったものを、92番目まで実行するように拡張したことを意味します。これにより、コミットメッセージで言及されているテスト90とテスト91が、このパーサーの変更によって正しく処理されることを検証できるようになります。
  • renderTestBlacklistへの追加: renderTestBlacklistは、パースは成功するものの、レンダリング結果が期待通りにならない、あるいは特定のレンダリングテストで問題を引き起こすことが既知のHTMLスニペットをリストアップしています。<a><table><a></table><p><a><div><a>という複雑なHTML構造がここに追加されました。これは、この特定のケースがパースはできるようになったものの、レンダリングの側面でまだ課題があるか、あるいはレンダリングテストのフレームワークがこの構造を正しく扱えないため、一時的にテストから除外していることを示唆しています。

関連リンク

参考にした情報源リンク