[インデックス 13598] ファイルの概要
このコミットは、Go言語の実験的なHTMLパーサーパッケージ exp/html
における変更です。具体的には、src/pkg/exp/html/parse.go
ファイルと、関連するテストログファイル src/pkg/exp/html/testlogs/tests7.dat.log
が変更されています。
コミット
commit fca45719a4365a0dc709202aa3efc2c58bbe473b
Author: Andrew Balholm <andybalholm@gmail.com>
Date: Wed Aug 8 10:00:57 2012 +1000
exp/html: foster-parent text correctly
If a table contained whitespace, text nodes would not get foster parented
correctly.
Pass 1 additional test.
R=nigeltao
CC=golang-dev
https://golang.org/cl/6459054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fca45719a4365a0dc709202aa3efc2c58bbe473b
元コミット内容
exp/html: foster-parent text correctly
テーブル内に空白が含まれている場合、テキストノードが正しくフォスターペアレントされませんでした。
追加で1つのテストがパスするようになりました。
変更の背景
このコミットの背景には、HTMLのパースにおける「フォスターペアレンティング (foster parenting)」という特殊なルールが関係しています。HTMLの仕様では、特定の状況下で要素が本来の親要素ではなく、別の場所(フォスターペアレント要素)に挿入されることがあります。
元の実装では、テーブル要素(<table>
, <tbody>
, <tfoot>
, <thead>
, <tr>
)の内部に空白文字(テキストノード)が存在する場合、これらのテキストノードがフォスターペアレンティングのルールに従って正しく処理されないという問題がありました。これにより、HTMLドキュメントの構造が意図しない形で構築され、レンダリング結果に影響を与える可能性がありました。
このコミットは、この問題を修正し、テーブル内の空白テキストノードも適切にフォスターペアレントされるようにすることで、HTMLパーサーの堅牢性と仕様への準拠を向上させることを目的としています。
前提知識の解説
HTMLパーシングとDOMツリー
HTMLパーシングとは、HTMLドキュメントの文字列を解析し、ブラウザが理解できる構造化されたデータ(DOMツリー)に変換するプロセスです。DOM(Document Object Model)ツリーは、HTML要素、属性、テキストなどをノードとして表現し、それらの親子関係をツリー構造で表します。
HTML5のパースアルゴリズムとフォスターペアレンティング
HTML5の仕様では、エラー耐性のあるパースアルゴリズムが詳細に定義されています。これは、不正なHTMLマークアップに対しても、ブラウザが予測可能な方法でDOMツリーを構築できるようにするためです。
「フォスターペアレンティング (foster parenting)」は、このHTML5パースアルゴリズムにおける重要な概念の一つです。これは、特定の状況下で、本来の親要素に属すべきノードが、DOMツリー内の別の場所(「フォスターペアレント要素」と呼ばれる)に挿入されるメカニズムを指します。
具体的には、HTMLのテーブル構造(<table>
, <tbody>
, <tfoot>
, <thead>
, <tr>
など)の内部に、テーブルコンテンツモデルに適合しない要素(例えば、<div>
や直接のテキストノードなど)が出現した場合に発生します。これらの「不適切な」ノードは、テーブル構造を破壊しないように、テーブルの直前の適切な親要素(フォスターペアレント要素)に「養子縁組」されるように挿入されます。
このメカニズムは、ブラウザが不正なHTMLをどのように解釈し、DOMツリーを構築するかを標準化するために非常に重要です。
Go言語の exp/html
パッケージ
exp/html
は、Go言語の標準ライブラリの一部として提供されているHTMLパーサーパッケージです。このパッケージは、HTML5のパースアルゴリズムに厳密に従ってHTMLドキュメントを解析し、DOMツリーを構築するための機能を提供します。実験的なパッケージとして開始されましたが、後にGoの標準ライブラリの一部として安定化されました。
技術的詳細
このコミットの技術的な核心は、HTMLパーサーがテキストノードを処理する際に、フォスターペアレンティングのルールを正しく適用するように修正した点にあります。
元のコードでは、addChild
メソッド内でフォスターペアレンティングの条件を直接チェックしていました。このチェックは、要素ノードの追加時には機能していましたが、テキストノードの追加時には考慮されていませんでした。特に、テーブル要素の内部に空白文字(テキストノード)が存在する場合、このテキストノードはテーブルのコンテンツモデルに違反するため、フォスターペアレンティングの対象となるべきでした。しかし、テキストノードの追加パスではこのロジックが欠けていたため、正しく処理されませんでした。
この修正では、フォスターペアレンティングの条件チェックを shouldFosterParent()
という新しいヘルパー関数に切り出しました。この関数は、現在のトップ要素がテーブル関連の要素であり、かつフォスターペアレンティングが有効な場合に true
を返します。
そして、この shouldFosterParent()
関数を addChild
メソッドだけでなく、addText
メソッド(テキストノードを追加するメソッド)からも呼び出すように変更しました。これにより、テーブル内部の空白テキストノードも shouldFosterParent()
の条件に合致すれば、fosterParent()
メソッドを通じて適切にフォスターペアレントされるようになりました。
この変更により、HTML5のパース仕様におけるフォスターペアレンティングのルールが、テキストノードに対しても一貫して適用されるようになり、パーサーの正確性が向上しました。
コアとなるコードの変更箇所
src/pkg/exp/html/parse.go
-
addChild
メソッドの変更:- フォスターペアレンティングの条件チェックロジックが、新しい
p.shouldFosterParent()
関数呼び出しに置き換えられました。 - 変更前:
fp := false if p.fosterParenting { switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: fp = true } } if fp { p.fosterParent(n) } else { p.top().Add(n) }
- 変更後:
if p.shouldFosterParent() { p.fosterParent(n) } else { p.top().Add(n) }
- フォスターペアレンティングの条件チェックロジックが、新しい
-
shouldFosterParent
ヘルパー関数の追加:- フォスターペアレンティングの条件を判断するための新しい関数が追加されました。
-
// shouldFosterParent returns whether the next node to be added should be // foster parented. func (p *parser) shouldFosterParent() bool { if p.fosterParenting { switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: return true } } return false }
-
addText
メソッドの変更:- テキストノードを追加する際に、フォスターペアレンティングの条件をチェックし、必要に応じて
fosterParent
を呼び出すロジックが追加されました。 - 変更前:
if text == "" { return } // TODO: distinguish whitespace text from others. t := p.top() if i := len(t.Child); i > 0 && t.Child[i-1].Type == TextNode { t.Child[i-1].Data += text }
- 変更後:
if text == "" { return } if p.shouldFosterParent() { p.fosterParent(&Node{ Type: TextNode, Data: text, }) return } t := p.top() if i := len(t.Child); i > 0 && t.Child[i-1].Type == TextNode { t.Child[i-1].Data += text }
- テキストノードを追加する際に、フォスターペアレンティングの条件をチェックし、必要に応じて
src/pkg/exp/html/testlogs/tests7.dat.log
- テスト結果のログが更新され、以前
FAIL
だった特定のテストケースがPASS
に変更されました。- 変更前:
FAIL "A<table><tr> B</tr> </em>C</table>"
- 変更後:
PASS "A<table><tr> B</tr> </em>C</table>"
これは、修正によって問題が解決され、テストが成功したことを示しています。
- 変更前:
コアとなるコードの解説
このコミットのコアとなる変更は、フォスターペアレンティングのロジックを shouldFosterParent()
関数にカプセル化し、それを addChild()
と addText()
の両方から呼び出すようにした点です。
-
shouldFosterParent()
関数:- この関数は、現在のパーサーの状態に基づいて、次に挿入されるノードがフォスターペアレントされるべきかどうかを判断します。
p.fosterParenting
がtrue
(フォスターペアレンティングモードが有効)であり、かつ現在のスタックトップの要素が<table>
,<tbody>
,<tfoot>
,<thead>
, または<tr>
のいずれかである場合にtrue
を返します。- これにより、フォスターペアレンティングの条件が明確に定義され、再利用可能になりました。
-
addChild()
メソッドの変更:- 以前は
addChild()
内に直接記述されていたフォスターペアレンティングの条件チェックが、p.shouldFosterParent()
の呼び出しに置き換えられました。これにより、コードが簡潔になり、ロジックの重複がなくなりました。
- 以前は
-
addText()
メソッドの変更:- これが最も重要な変更点です。以前の
addText()
メソッドは、テキストノードがフォスターペアレントされるべきかどうかを考慮していませんでした。 - 新しいコードでは、テキストノードを追加する前に
p.shouldFosterParent()
を呼び出します。 - もし
shouldFosterParent()
がtrue
を返した場合、新しいテキストノードは通常のAdd()
メソッドではなく、p.fosterParent()
メソッドを通じてDOMツリーに挿入されます。これにより、テーブル内部の空白テキストノードがHTML5の仕様に従って正しく「養子縁組」されるようになります。 Node{Type: TextNode, Data: text}
のように、新しいテキストノードをその場で作成してfosterParent
に渡している点も注目に値します。
- これが最も重要な変更点です。以前の
この修正により、HTMLパーサーはテーブル内の空白文字のような、一見無害に見えるテキストノードに対しても、HTML5の複雑なフォスターペアレンティングルールを正確に適用できるようになり、より堅牢で仕様に準拠したDOMツリーを構築することが可能になりました。
関連リンク
参考にした情報源リンク
- HTML5 Parsing Algorithm: https://html.spec.whatwg.org/multipage/parsing.html#parsing
- HTML5 Foster Parenting: https://html.spec.whatwg.org/multipage/parsing.html#foster-parenting
- Go
exp/html
package documentation (当時の情報に基づく): https://pkg.go.dev/exp/html (現在のパッケージはgolang.org/x/net/html
に統合されていますが、当時の文脈ではexp/html
が使用されていました。)
[インデックス 13598] ファイルの概要
このコミットは、Go言語の実験的なHTMLパーサーパッケージ exp/html
における変更です。具体的には、src/pkg/exp/html/parse.go
ファイルと、関連するテストログファイル src/pkg/exp/html/testlogs/tests7.dat.log
が変更されています。
コミット
commit fca45719a4365a0dc709202aa3efc2c58bbe473b
Author: Andrew Balholm <andybalholm@gmail.com>
Date: Wed Aug 8 10:00:57 2012 +1000
exp/html: foster-parent text correctly
If a table contained whitespace, text nodes would not get foster parented
correctly.
Pass 1 additional test.
R=nigeltao
CC=golang-dev
https://golang.org/cl/6459054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fca45719a4365a0dc709202aa3efc2c58bbe473b
元コミット内容
exp/html: foster-parent text correctly
テーブル内に空白が含まれている場合、テキストノードが正しくフォスターペアレントされませんでした。
追加で1つのテストがパスするようになりました。
変更の背景
このコミットの背景には、HTMLのパースにおける「フォスターペアレンティング (foster parenting)」という特殊なルールが関係しています。HTMLの仕様では、特定の状況下で要素が本来の親要素ではなく、別の場所(フォスターペアレント要素)に挿入されることがあります。
元の実装では、テーブル要素(<table>
, <tbody>
, <tfoot>
, <thead>
, <tr>
)の内部に空白文字(テキストノード)が存在する場合、これらのテキストノードがフォスターペアレンティングのルールに従って正しく処理されないという問題がありました。これにより、HTMLドキュメントの構造が意図しない形で構築され、レンダリング結果に影響を与える可能性がありました。
このコミットは、この問題を修正し、テーブル内の空白テキストノードも適切にフォスターペアレントされるようにすることで、HTMLパーサーの堅牢性と仕様への準拠を向上させることを目的としています。
前提知識の解説
HTMLパーシングとDOMツリー
HTMLパーシングとは、HTMLドキュメントの文字列を解析し、ブラウザが理解できる構造化されたデータ(DOMツリー)に変換するプロセスです。DOM(Document Object Model)ツリーは、HTML要素、属性、テキストなどをノードとして表現し、それらの親子関係をツリー構造で表します。
HTML5のパースアルゴリズムとフォスターペアレンティング
HTML5の仕様では、エラー耐性のあるパースアルゴリズムが詳細に定義されています。これは、不正なHTMLマークアップに対しても、ブラウザが予測可能な方法でDOMツリーを構築できるようにするためです。
「フォスターペアレンティング (foster parenting)」は、このHTML5パースアルゴリズムにおける重要な概念の一つです。これは、特定の状況下で、本来の親要素に属すべきノードが、DOMツリー内の別の場所(「フォスターペアレント要素」と呼ばれる)に挿入されるメカニズムを指します。
具体的には、HTMLのテーブル構造(<table>
, <tbody>
, <tfoot>
, <thead>
, <tr>
など)の内部に、テーブルコンテンツモデルに適合しない要素(例えば、<div>
や直接のテキストノードなど)が出現した場合に発生します。これらの「不適切な」ノードは、テーブル構造を破壊しないように、テーブルの直前の適切な親要素(フォスターペアレント要素)に「養子縁組」されるように挿入されます。
このメカニズムは、ブラウザが不正なHTMLをどのように解釈し、DOMツリーを構築するかを標準化するために非常に重要です。
Go言語の exp/html
パッケージ
exp/html
は、Go言語の標準ライブラリの一部として提供されているHTMLパーサーパッケージです。このパッケージは、HTML5のパースアルゴリズムに厳密に従ってHTMLドキュメントを解析し、DOMツリーを構築するための機能を提供します。実験的なパッケージとして開始されましたが、後にGoの標準ライブラリの一部として安定化されました。
技術的詳細
このコミットの技術的な核心は、HTMLパーサーがテキストノードを処理する際に、フォスターペアレンティングのルールを正しく適用するように修正した点にあります。
元のコードでは、addChild
メソッド内でフォスターペアレンティングの条件を直接チェックしていました。このチェックは、要素ノードの追加時には機能していましたが、テキストノードの追加時には考慮されていませんでした。特に、テーブル要素の内部に空白文字(テキストノード)が存在する場合、このテキストノードはテーブルのコンテンツモデルに違反するため、フォスターペアレンティングの対象となるべきでした。しかし、テキストノードの追加パスではこのロジックが欠けていたため、正しく処理されませんでした。
この修正では、フォスターペアレンティングの条件チェックを shouldFosterParent()
という新しいヘルパー関数に切り出しました。この関数は、現在のトップ要素がテーブル関連の要素であり、かつフォスターペアレンティングが有効な場合に true
を返します。
そして、この shouldFosterParent()
関数を addChild
メソッドだけでなく、addText
メソッド(テキストノードを追加するメソッド)からも呼び出すように変更しました。これにより、テーブル内部の空白テキストノードも shouldFosterParent()
の条件に合致すれば、fosterParent()
メソッドを通じて適切にフォスターペアレントされるようになりました。
この変更により、HTML5のパース仕様におけるフォスターペアレンティングのルールが、テキストノードに対しても一貫して適用されるようになり、パーサーの正確性が向上しました。
コアとなるコードの変更箇所
src/pkg/exp/html/parse.go
-
addChild
メソッドの変更:- フォスターペアレンティングの条件チェックロジックが、新しい
p.shouldFosterParent()
関数呼び出しに置き換えられました。 - 変更前:
fp := false if p.fosterParenting { switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: fp = true } } if fp { p.fosterParent(n) } else { p.top().Add(n) }
- 変更後:
if p.shouldFosterParent() { p.fosterParent(n) } else { p.top().Add(n) }
- フォスターペアレンティングの条件チェックロジックが、新しい
-
shouldFosterParent
ヘルパー関数の追加:- フォスターペアレンティングの条件を判断するための新しい関数が追加されました。
-
// shouldFosterParent returns whether the next node to be added should be // foster parented. func (p *parser) shouldFosterParent() bool { if p.fosterParenting { switch p.top().DataAtom { case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: return true } } return false }
-
addText
メソッドの変更:- テキストノードを追加する際に、フォスターペアレンティングの条件をチェックし、必要に応じて
fosterParent
を呼び出すロジックが追加されました。 - 変更前:
if text == "" { return } // TODO: distinguish whitespace text from others. t := p.top() if i := len(t.Child); i > 0 && t.Child[i-1].Type == TextNode { t.Child[i-1].Data += text }
- 変更後:
if text == "" { return } if p.shouldFosterParent() { p.fosterParent(&Node{ Type: TextNode, Data: text, }) return } t := p.top() if i := len(t.Child); i > 0 && t.Child[i-1].Type == TextNode { t.Child[i-1].Data += text }
- テキストノードを追加する際に、フォスターペアレンティングの条件をチェックし、必要に応じて
src/pkg/exp/html/testlogs/tests7.dat.log
- テスト結果のログが更新され、以前
FAIL
だった特定のテストケースがPASS
に変更されました。- 変更前:
FAIL "A<table><tr> B</tr> </em>C</table>"
- 変更後:
PASS "A<table><tr> B</tr> </em>C</table>"
これは、修正によって問題が解決され、テストが成功したことを示しています。
- 変更前:
コアとなるコードの解説
このコミットのコアとなる変更は、フォスターペアレンティングのロジックを shouldFosterParent()
関数にカプセル化し、それを addChild()
と addText()
の両方から呼び出すようにした点です。
-
shouldFosterParent()
関数:- この関数は、現在のパーサーの状態に基づいて、次に挿入されるノードがフォスターペアレントされるべきかどうかを判断します。
p.fosterParenting
がtrue
(フォスターペアレンティングモードが有効)であり、かつ現在のスタックトップの要素が<table>
,<tbody>
,<tfoot>
,<thead>
, または<tr>
のいずれかである場合にtrue
を返します。- これにより、フォスターペアレンティングの条件が明確に定義され、再利用可能になりました。
-
addChild()
メソッドの変更:- 以前は
addChild()
内に直接記述されていたフォスターペアレンティングの条件チェックが、p.shouldFosterParent()
の呼び出しに置き換えられました。これにより、コードが簡潔になり、ロジックの重複がなくなりました。
- 以前は
-
addText()
メソッドの変更:- これが最も重要な変更点です。以前の
addText()
メソッドは、テキストノードがフォスターペアレントされるべきかどうかを考慮していませんでした。 - 新しいコードでは、テキストノードを追加する前に
p.shouldFosterParent()
を呼び出します。 - もし
shouldFosterParent()
がtrue
を返した場合、新しいテキストノードは通常のAdd()
メソッドではなく、p.fosterParent()
メソッドを通じてDOMツリーに挿入されます。これにより、テーブル内部の空白テキストノードがHTML5の仕様に従って正しく「養子縁組」されるようになります。 Node{Type: TextNode, Data: text}
のように、新しいテキストノードをその場で作成してfosterParent
に渡している点も注目に値します。
- これが最も重要な変更点です。以前の
この修正により、HTMLパーサーはテーブル内の空白文字のような、一見無害に見えるテキストノードに対しても、HTML5の複雑なフォスターペアレンティングルールを正確に適用できるようになり、より堅牢で仕様に準拠したDOMツリーを構築することが可能になりました。
関連リンク
参考にした情報源リンク
- HTML5 Parsing Algorithm: https://html.spec.whatwg.org/multipage/parsing.html#parsing
- HTML5 Foster Parenting: https://html.spec.whatwg.org/multipage/parsing.html#foster-parenting
- Go
exp/html
package documentation (当時の情報に基づく): https://pkg.go.dev/exp/html (現在のパッケージはgolang.org/x/net/html
に統合されていますが、当時の文脈ではexp/html
が使用されていました。)