042刊行 2026-10-20 ・ 読了約6分
ブラウザは、ひとりで作れるのか。
Can One Person Build a Web Browser?
Python 1,859行で、ウィキペディアの記事まで描けた。ところが自作の20問で本物と全問一致していたのに、ブラウザ各社の共通試験では1.2%しか解けなかった。比べていたのは body の中だけだった。
調べ方Python で自作した描画エンジンと、web-platform-tests の木構築1,739問
- 規模
- Python 1,859行・試験1,739問
- 触れる
- 段階を選ぶと、同じページの途中経過が出る
- 結論
- 描ける。試験は42%。
要旨── 描けた。でも、採点は別の話だった
Chrome も Safari も、何百人もの技術者が何十年もかけて作ってきた。ブラウザは、ひとりで作れるのか。
仕組みを知るために、Python で描画エンジンを自作した。URL を渡すと、文字列を読み、木を組み、CSS を当て、箱を並べ、絵にする。途中の7段階を全部ファイルに書き出す作りにした。コードは7つのファイルで1,859行、借りたのは画像を書き出す Pillow だけである。
描くところまでは、作れた。手元の見本ページは1,265文字から289の塗る命令を経て1枚の絵になり、日本語版ウィキペディアの記事も6,405個のノードと1,011本の CSS 規則を読んで描けた。
おもしろかったのは、その先である。壊れた HTML の直し方を自分で20通り作り、Chrome と Safari のエンジンに並べて比べると、20通りすべてで同じ木になった。ところが、ブラウザ各社が使う共通の試験問題1,739問を解かせると、正解は21問、1.2%だった。
自分で作った検査は、自分の思い込みしか検査しない。
方法── 7段階を、全部ファイルに書き出す
- 作り
- 取得・分解・木・スタイル・レイアウト・描画命令・画素の7段階を、1つずつ別のファイルに分けて書いた。それぞれの段が、次の段に渡すものを人が読める形で書き出す。本物のブラウザの内側で起きていることを、番号順に開いて追えるようにするためである。
- 見本
- 閉じ忘れの p や li、CSS の @media や :hover をわざと混ぜた試験ページ(1,265文字)と、example.com、日本語版ウィキペディアの「ウェブブラウザ」の記事。
- 答え合わせ1
- 壊れた HTML を自分で20通り作り、自作・Chrome(エンジンは Blink)・Safari と同じ WebKit の3つに読ませて、組み上がった木を並べた。最初の10通りは直しながら見た問題、あとの10通りは直したあとに足した、まだ見ていない問題である。
- 答え合わせ2
- ブラウザ各社の共通試験 web-platform-tests にある、HTML の木の組み立ての問題。全1,959問のうち、入口の違う部分解析(206問)と JavaScript が前提の問題(14問)を除いた1,739問を採点した。木を決まった書式で書き出し、1文字でも違えば不正解とした。
文字列は、7回姿を変えて絵になる── 1,265文字が、289の命令へ
見本ページを幅640で描かせると、7つの段はそれぞれ次のものを書き出した。
いちばん手間がかかるのは、文字を行に折る段だった。英語は空白で折れるが、日本語は1文字ずつ折れる。そのうえ「。」や「」」は行の頭に来てはいけないので、前の字にくっつけて離さない(禁則)。見本ページ1枚の各段の所要時間は、レイアウトが10.7ミリ秒、画素にする段が67.5ミリ秒で、ほかの段は2ミリ秒に届かなかった。
「見出しが大きい」「リンクが青い」も、HTML そのものの性質ではなかった。ブラウザは最初に、自前の既定の CSS を当てている。自作では94本の規則を書き、その上にページの規則を重ねた。
h1 が大きいのは、ブラウザが先にそう書いておいたからだった。
自作の20問は、全問一致した── 壊れた HTML の直し方は、仕様で決まっている
閉じ忘れや入れ子の交差のような壊れた HTML を読ませると、Chrome と WebKit は、最初の10通りすべてで同じ木を組んだ。別の会社のエンジンでも、直し方が仕様で1手ずつ決まっているからである。
最初の自作は、そのうち9通りで本物と違った。合っていたのは、li の閉じ忘れだけだった。そこで、HTML の仕様にある木の組み立ての手順を写していった。
- 範囲
- 閉じタグは、表やセルの「壁」の外にある要素を閉じない。
- 書式の作り直し
- 開いた b や i を覚えておき、途中で閉じられても、次の文字の前に作り直す。
- 養子縁組
<b>太<p>段落</b>のように入れ子が交差したら、書式の要素を複製して付け直す。仕様での正式な名前も adoption agency(養子縁組の手続き)である。- 表からの追い出し
- 表の骨組みに置けない文字は、表の直前へ移す。
<table>の中に迷い込んだ「迷子」という文字は、表の前に出てくる。
直したあとは、最初の10通りも、新しく足したまだ見ていない10通りも、20通りすべてで3者が一致した。まだ見ていない問題でも合ったので、仕様を写せたと考えた。
公式の試験では、1.2%だった── 比べていたのは、body の中だけ
次に、ブラウザ各社が実際に使っている共通の試験問題を解かせた。結果は1,739問中21問だった。
原因は、問題の難しさではなかった。自作の比べ方は、body の中身しか見ていなかった。公式の問題は木の全体を見る。<head> を書かない HTML で空の head を補っていなかったのが、ほぼ全問に効いていた。そこを1か所直すだけで、正解は21問から358問に増えた。
続けて、注釈 <!-- --> と doctype を木に入れた。注釈の行き先は、読んだ位置で変わる。<html> の前なら文書の直下、</body> の後なら html の最後の子、それ以外は開いている要素の中である。
途中で一度、40.8%に下がった。「body が始まる前の閉じタグは捨てる」という規則を広く当てすぎ、解けていた問題を壊したためである。当てる範囲を <html> の前だけに絞り、42.2%になった。
20問の全問一致は、20問ぶんの自信でしかなかった。
作らなかったもの── 0%のまま残っている山
試験の問題を中身で分けると、解けている所と、まったく手が付いていない所がはっきり分かれた(1問が複数の分類に入る)。
- よく解ける
- 文字参照(& など)は137問中120問で87.6%。下のどの特別な要素も含まない問題は188問中136問で72.3%。
- 半分に届かない
- 表は273問中92問で33.7%、select は92問中29問で31.5%、注釈を含む問題は430問中118問で27.4%。
- 0%
- HTML の中の SVG と数式(221問)、template(125問)、frameset(110問)、NUL と CR の文字(40問)。どれも、普通の HTML とは別の規則で木を組む。
描画の側にも、わざと作っていないものがある。JavaScript は動かない。position で浮かせる箱は捨て、flex と grid は縦に積むだけにした。画像は中身を描かず「[画像: 説明]」と文字で出す。上下の余白を相殺する規則も無いので、見出しと箇条書きのあいだが本物より広い。
どれも、本物のブラウザの仕様の大半を占める部分である。ひとりで作れたのは、ページを「読んで、描く」ところまでだった。
段階を切り替える── 同じページが、7回姿を変える
見本ページ1枚について、7つの段が書き出したものを切り替えて見られる。抜粋は、自作のブラウザが実際に書き出したファイルからそのまま取った。
幅640で描かせた結果。絵は2倍の細かさで書き出したものを縮めて載せている。抜粋は各段の先頭部分だけで、長い行は枠の中で横に送って読める。
結論── 描くところまでは、ひとりで作れる
ブラウザは、ページを読んで描くところまでなら、ひとりで作れた。1,859行で、日本語版ウィキペディアの記事が描ける。ただ、本物と同じように読めているかを公式の試験で測ると、42.2%だった。
いちばんの発見は、作ったものより測り方のほうにあった。自分で作った20問は、自分が気にしていた所しか見ていなかった。head を補っていないことは、自分の問題では一度も見つからず、他人が作った1,739問では、ほぼ全問で見つかった。全問一致は、問題を作った人と答えた人が同じだったからだった。
本物のブラウザが巨大なのは、描く仕組みが難しいからだけではない。どんなに壊れたページでも、どの会社のエンジンでも同じ木になるように、直し方を1手ずつ決めた仕様と、それを確かめる何千もの共通問題を積み上げてきたからである。
次の問い:残りの58%のうち、どこまでが「ひとり」で届くのか。
ネタもと── 仕様・試験・教科書
- AWHATWG「HTML Living Standard」13.2.6 Tree construction
壊れた HTML の直し方を1手ずつ決めた手順。範囲、書式の作り直し、養子縁組、表からの追い出しはここから写した。 - Bweb-platform-tests「html/syntax/parsing」
ブラウザ各社の共通試験のうち、HTML の木の組み立ての問題(resources/*.dat、全1,959問)。 - Chtml5lib-tests
B の問題のもとになった試験集。 - DPanchekha, P. & Harrelson, C.「Web Browser Engineering」
同じ道を Python で最後まで行く教科書。段の分け方を参考にした。 - EWikipedia「ウェブブラウザ」
描かせた実際のページ(ノード6,405個、ページの CSS 規則1,011本、幅800で高さ11,818px)。