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

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

このコミットは、Go言語の実験的なHTMLパーサー(exp/htmlパッケージ)における<frameset>タグの処理に関するバグ修正と改善を目的としています。具体的には、HTMLドキュメントの<body>要素内に空白文字のみが存在する場合に、<frameset>タグが誤って無視されてしまう問題を修正し、より標準的なHTMLの解釈に近づけています。

コミット

commit 4d2251967847cf9b710da09525c676a7e733113d
Author: Andrew Balholm <andybalholm@gmail.com>
Date:   Wed Jul 25 12:09:58 2012 +1000

    exp/html: allow frameset if body contains whitespace
    
    If the body of an HTML document contains text, the <frameset> tag is
    ignored. But not if the text is only whitespace.
    
    Pass 4 additional tests.
    
    R=nigeltao
    CC=golang-dev
    https://golang.org/cl/6442043

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

https://github.com/golang/go/commit/4d2251967847cf9b710da09525c676a7e733113d

元コミット内容

exp/html: allow frameset if body contains whitespace

If the body of an HTML document contains text, the <frameset> tag is
ignored. But not if the text is only whitespace.

Pass 4 additional tests.

R=nigeltao
CC=golang-dev
https://golang.org/cl/6442043

変更の背景

HTMLの仕様では、<frameset>タグは<body>タグと同時に存在することはできません。通常、HTMLドキュメントがフレームセットベースの構造を持つ場合、<html>タグの直下に<frameset>タグが配置され、<body>タグは存在しません。しかし、もし<body>タグが存在し、その中に何らかのコンテンツ(テキストなど)が含まれている場合、ブラウザは<frameset>タグを無視し、<body>タグの内容をレンダリングします。

このコミット以前のGoのexp/htmlパーサーでは、<body>タグ内に空白文字のみが含まれている場合でも、<frameset>タグを誤って無視していました。これは、HTMLの標準的な解釈とは異なり、特定の有効なHTMLドキュメントが正しくパースされない原因となっていました。

この変更の背景には、HTMLパーサーがより堅牢で、かつHTML仕様に準拠した動作をするように改善するという目的があります。特に、ウェブコンテンツの多様性を考慮し、空白文字の扱いを正確にすることで、より多くのHTMLドキュメントを適切に処理できるようになります。

前提知識の解説

HTMLの構造と<frameset>タグ

HTMLドキュメントは通常、<html><head><body>という基本的な構造を持っています。

  • <html>: HTMLドキュメントのルート要素。
  • <head>: ドキュメントのメタデータ(タイトル、スタイルシート、スクリプトなど)を含む。
  • <body>: ウェブページに表示されるすべてのコンテンツ(テキスト、画像、リンクなど)を含む。

一方、<frameset>タグは、ウェブページを複数のフレームに分割するために使用されるHTML要素でした。各フレームは独立したHTMLドキュメントを表示でき、ウェブページ内に複数の異なるコンテンツを同時に表示するのに利用されました。しかし、<frameset>はHTML5で非推奨となり、現代のウェブ開発では<iframe>やCSS、JavaScriptを用いたレイアウト技術が主流となっています。

重要な点は、<frameset>タグが使用される場合、<body>タグはドキュメント内に存在してはならないというHTMLのルールです。もし両方が存在する場合、ブラウザは通常、<body>の内容を優先し、<frameset>を無視します。

HTMLパーシングとトークナイザー

HTMLパーシングとは、HTMLドキュメントの文字列を解析し、ブラウザが理解できるDOM(Document Object Model)ツリー構造に変換するプロセスです。このプロセスは通常、以下の2つの主要な段階に分けられます。

  1. トークナイゼーション(Lexical Analysis): HTMLの文字列を、意味のある最小単位である「トークン」(タグ、属性、テキスト、コメントなど)に分解します。
  2. ツリー構築(Tree Construction): トークンストリームを基に、DOMツリーを構築します。この段階で、HTMLの文法規則に従って要素がネストされ、エラーがあれば修正(エラーリカバリ)が行われます。

Go言語のexp/htmlパッケージは、このようなHTMLパーシングの機能を提供します。このパッケージは、HTML5のパーシングアルゴリズムに準拠することを目指しており、ブラウザがHTMLをどのように解釈するかを模倣しています。

framesetOKフラグ

HTMLパーサー内部では、特定の状態を管理するためのフラグが使用されることがあります。このコミットで言及されているframesetOKフラグは、パーサーが現在<frameset>タグを有効なものとして受け入れることができる状態にあるかどうかを示すものと推測されます。

  • 初期状態ではframesetOKtrueであり、<frameset>タグがパースされることを許可します。
  • しかし、<body>タグ内に非空白文字のコンテンツが追加された場合、このフラグはfalseに設定され、それ以降に現れる<frameset>タグは無視されるようになります。

このフラグの管理が、<frameset>タグの正しい解釈に不可欠です。

技術的詳細

このコミットの技術的な核心は、HTMLパーサーが<body>要素内のテキストコンテンツを処理する際のロジックの変更にあります。

変更前のパーサーは、inBodyIM("in body insertion mode")という状態において、テキストノード(d)が追加されるたびに無条件にp.framesetOK = falseを設定していました。これは、たとえ追加されたテキストが単なる空白文字(スペース、タブ、改行など)であっても、<frameset>の許可フラグを無効にしてしまうことを意味します。

HTMLの仕様では、<body>要素内に非空白文字のコンテンツが存在する場合にのみ、<frameset>タグが無視されるべきです。空白文字のみであれば、<frameset>は依然として有効な選択肢として扱われるべきです。

このコミットでは、この挙動を修正するために、p.framesetOK = falseの実行に条件を追加しました。新しい条件は以下の通りです。

if p.framesetOK && strings.TrimLeft(d, whitespace) != "" {
    // There were non-whitespace characters inserted.
    p.framesetOK = false
}

この変更により、framesetOKフラグがtrueである(まだ<frameset>が許可されている)場合に加えて、追加されるテキストノードdから先頭の空白文字をトリムした結果が空文字列ではない(つまり、非空白文字が含まれている)場合にのみ、framesetOKフラグがfalseに設定されるようになりました。

strings.TrimLeft(d, whitespace)は、文字列dの左側からwhitespace(空白文字の集合)に含まれる文字をすべて削除します。もしdが空白文字のみで構成されている場合、この関数は空文字列""を返します。したがって、strings.TrimLeft(d, whitespace) != ""という条件は、「dに非空白文字が含まれている」ことを正確に判定します。

この修正により、パーサーは以下のように動作します。

  • <body>内に非空白文字が挿入された場合:framesetOKfalseになり、以降の<frameset>は無視されます。
  • <body>内に空白文字のみが挿入された場合:framesetOKtrueのまま維持され、以降に<frameset>が現れても有効なものとして扱われる可能性があります。

この変更は、HTMLパーシングの正確性を向上させ、特に古いHTMLドキュメントや、厳密なHTMLバリデーションを通過しないがブラウザでは許容されるようなドキュメントの互換性を高める上で重要です。

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

変更は主にsrc/pkg/exp/html/parse.goファイル内のinBodyIM関数にあります。

--- a/src/pkg/exp/exp/html/parse.go
+++ b/src/pkg/exp/exp/html/parse.go
@@ -725,7 +725,10 @@ func inBodyIM(p *parser) bool {
 		}
 		p.reconstructActiveFormattingElements()
 		p.addText(d)
-		p.framesetOK = false
+		if p.framesetOK && strings.TrimLeft(d, whitespace) != "" {
+			// There were non-whitespace characters inserted.
+			p.framesetOK = false
+		}
 	case StartTagToken:
 		switch p.tok.DataAtom {
 		case a.Html:

また、この変更によって、以下のテストログファイルが更新され、以前はFAILとなっていたテストケースがPASSに変わっています。

  • src/pkg/exp/html/testlogs/plain-text-unsafe.dat.log
  • src/pkg/exp/html/testlogs/tests19.dat.log

コアとなるコードの解説

inBodyIM関数は、HTMLパーサーが<body>要素の内部を処理している際の挿入モード(insertion mode)を管理する部分です。この関数は、次のトークンがテキストトークン(TextToken)である場合に、そのテキストコンテンツdを処理します。

変更前のコードでは、テキストトークンが検出されると、無条件にp.framesetOK = falseが実行されていました。これは、<body>内に何らかのテキスト(空白文字を含む)が追加された時点で、<frameset>タグが許可されない状態になることを意味します。

変更後のコードでは、以下の条件が追加されました。

if p.framesetOK && strings.TrimLeft(d, whitespace) != "" {
    // There were non-whitespace characters inserted.
    p.framesetOK = false
}
  • p.framesetOK: これは、現在のパーサーの状態がまだ<frameset>を許可しているかどうかを示すフラグです。この条件がfalseであれば、すでに<frameset>は許可されていないため、これ以上処理する必要はありません。
  • strings.TrimLeft(d, whitespace) != "": これは、追加されたテキストdが、空白文字以外の文字を含んでいるかどうかをチェックします。
    • strings.TrimLeft(d, whitespace): 文字列dの左側から、whitespace(スペース、タブ、改行などの空白文字の集合)に含まれる文字をすべて削除します。
    • もしd" ""\n\t"のような空白文字のみで構成されている場合、strings.TrimLeftは空文字列""を返します。
    • もしd" hello""world\n"のように非空白文字を含んでいる場合、strings.TrimLeftは非空文字列を返します。
    • したがって、!= ""という条件は、「dが空白文字のみではない(非空白文字を含んでいる)」ことを意味します。

このif文全体は、「もし現在<frameset>が許可されており、かつ追加されたテキストが空白文字のみではない場合、<frameset>を許可しない状態にする」というロジックを実装しています。これにより、<body>内に空白文字のみが存在する場合は<frameset>が引き続き有効であるという、HTMLのより正確な解釈が実現されました。

テストログの変更は、この新しいロジックが期待通りに動作し、以前は失敗していた特定のテストケース(<body>内に空白文字のみがある状態で<frameset>を含むケース)が成功するようになったことを示しています。

関連リンク

  • Go言語のexp/htmlパッケージのドキュメント(当時のもの、または現在のgolang.org/x/net/htmlパッケージのドキュメント)
  • HTML5仕様(特にパーシングアルゴリズムとframesetbodyの相互作用に関するセクション)

参考にした情報源リンク