【テクニカル・上級編】 HTMLパースの仕組みとトークン化 – Webブラウザの仕組み実践ガイド

ブラウザの深淵:HTMLパーサーという名の「終わらない生存戦略」

フロントエンドエンジニアの多くは、DOMを「JavaScriptから操作するデータ構造」だと捉えています。しかし、ブラウザのエンジン(BlinkやWebKit)にとって、HTMLパースは単なるデータ変換ではありません。それは、ネットワーク経由で断片的に届く「予測不能な文字列」を、一瞬の隙も許さずに実行可能なツリー構造へ叩き込む、極めて泥臭い生存戦略なのです。

今日は、その最前線である「トークン化」と、そこで発生するパフォーマンスのボトルネックについて、アーキテクトの視点から掘り下げていきましょう。

—

1. 「完了しない」パースの現実:トークン化の狂気

HTMLのパースは、ソースコードのコンパイルとは根本的に違います。C++やRustなら、ファイルを読み込んでから解析しますよね? しかし、Webの世界では「ネットワークのパケットが到着したその瞬間に、先頭から順次パースを始める」必要があります。

このとき活躍するのが「トークン化(Tokenization)」です。ブラウザは有限オートマトン(FSM)を用いて、文字を一つずつ読み込み、「これはタグの開始か? 属性か? それともただのテキストか?」を判定します。

// ブラウザ内部のアルゴリズムを簡易化した概念コード
function tokenize(htmlString) {
let state = ‘DATA’; // 初期状態
let tokens = [];

for (let char of htmlString) {
switch (state) {
case ‘DATA’:
if (char === ‘<') state = 'TAG_OPEN'; // ここでDOMノードの生成準備に入る break; case 'TAG_OPEN': if (char === '/') state = 'END_TAG'; else if (/[a-zA-Z]/.test(char)) state = 'TAG_NAME'; break; // ... 実際には数百の複雑な状態遷移が存在する } } return tokens; } このトークン化プロセスにおいて、最も恐ろしいのは「不完全なHTML」への対応です。閉じタグの欠落や不正なネストがあっても、ブラウザは止まりません。仕様書(HTML5 Spec)には、こうしたエラーをどう「修復(Error Handling)」するかが100ページ以上にわたって記されています。この修復コストこそが、実はレンダリング初期の最大の隠れた負荷なのです。

—

2. メモリ効率とパーサーの「先行読み込み」

ブラウザのパーサーは、メインスレッドを占有しないよう、「プリロードスキャナ(Preload Scanner)」という別のスレッドを持っています。

メインスレッドがDOMを構築している間に、プリロードスキャナはHTMLを先読みし、`


---

3. レンダリングの「重大なバグ」と回避策

エンジニアが遭遇する「レイアウトシフト」や「表示のチラつき」の多くは、CSSOMの構築待ちに起因します。

HTMLをパースしてDOMを作っても、CSSのパースが終わらなければ画面は描画されません(Render Treeが完成しないため)。特に、``内で外部CSSを読み込む際、そのCSSのダウンロードが遅延すると、ブラウザは「真っ白な画面」のままフリーズします。これを「白画面問題(Flash of Unstyled Content: FOUCの極致)」と呼びます。

対処法:クリティカルパスの最適化

以下のアーキテクチャを意識してください。

1. クリティカルCSSのインライン化: 画面上部に必要なCSSだけを`