Dev Study
← Rust で小さなブラウザを作る コース

3. 本物のブラウザではどこにあたるか — Chromium と Firefox の部品との対応表

このコースの各段には、本物のブラウザに同じ役の部品があります。名前は違っても、仕様の節名で探せば見つかります。ここでは (エンジン 、JavaScript エンジン 、通信の )と Firefox(、、通信の )の両方を、この実装のファイルと並べます。場所は 2026 年 10 月 14 日時点のソースで確かめたもので、Chromium は chromium.googlesource.com/chromium/src、Firefox は GitHub の mozilla-firefox/firefox、V8 は github.com/v8/v8 で読めます。

表の読み方は 3 列です。左がこの実装の関数やファイル、中が Chromium、右が Firefox。灰色の段はこの実装に無いもので、I 章の「作っていない機能は本物のどこで動いているか」で仕組みを説明します。各レッスンの末尾にも「本物のブラウザでは」の段落を置いたので、段ごとの細かい差はそこで読めます。

先に大きな差を 3 つ押さえておくと、表が読みやすくなります。1 つ目はプロセスの分け方で、本物は通信・描画・GPU を別プロセスにし、はを開けません。2 つ目は速さのための層で、ルールの棚分け、前回の結果の使い回し、と JIT、並列走査が各段に挟まっています。3 つ目は寿命の管理で、この実装の / に対して、Blink は というトレース GC、Firefox は参照カウントと、JS エンジンは を使います。

逆に、同じところも多いです。HTML のは 3 つとも仕様のをそのまま と分岐で書いていて、Firefox のそれは Java で書かれた のを機械翻訳した C++ です。CSS の字句解析は Firefox では Rust の()で、F 章で書いたコードに最も近い本物です。 の復元は Chromium のファイル先頭に「Mozilla 由来」と書かれていて、2 つのブラウザが同じ祖先のコードを使っています。

開発のどこに活かすか: 自分の小さな実装と本物の対応表を作る習慣は、ライブラリやフレームワークを読むときにそのまま使えます。『この関数は本物のどの部品か』『本物はなぜこの層を足しているのか』を一度書き出すと、本物のソースを開いたときにどのディレクトリから読めばよいかが分かり、設計の判断(なぜ別プロセスか、なぜキャッシュが 3 層か)を自分の仕事の構成に持ち帰れます。

点線の付いた語はタップすると説明が出ます。用語集を開く →

① 通信(B・C 章)

左: この実装 / 中: Chromium(Blink・V8・//net) / 右: Firefox(Gecko・SpiderMonkey・Necko)
url.rs parse()分割と正規化を 1 関数で
url/ の parser + canonicalizer → GURLBlink は KURL(platform/weborigin)
netwerk/base/nsStandardURL.cpp
名前解決と TCP
http.rs fetch()ToSocketAddrs + TcpStream
HostResolverImpl / TransportClientSocketPool別プロセスの Network Service
nsHostResolver / nsSocketTransportsocket プロセス
要求を送り、応答を分解
build_request() / HttpResponse::parse()
HttpStreamParser / HttpResponseHeaders
nsHttpTransaction / nsHttpResponseHead
chunked の復元
decode_chunked()
net/http/http_chunked_decoder.cc先頭に Mozilla 由来と明記
nsHttpChunkedDecoder.cpp
(無し) TLS
net/socket/ssl_client_socket_impl.cc
NSS(security/manager/ssl)
(無し) HTTP キャッシュ
HttpCache::Transaction + net/disk_cache
netwerk/cache2
  • ・Chromium の流れは net/docs/life-of-a-url-request.md、Firefox は netwerk/docs に説明がある
  • ・灰色の段はこの実装に無いもの。I 章「作っていない機能は本物のどこで動いているか」で扱う

ソケットを開く場所が違います。この実装は 1 プロセスの中で開きますが、本物は描画プロセスから別プロセスへ頼みます。

② HTML → DOM(D・E 章)

左: この実装 / 中: Chromium(Blink・V8・//net) / 右: Firefox(Gecko・SpiderMonkey・Necko)
html/tokenizer.rs(18 状態)enum State + match
html_tokenizer.cc(kDataState …)仕様の節名の enum
nsHtml5Tokenizer.cppJava(validator.nu)から機械翻訳
トークン → 木
html/parser.rs(挿入モード 9)
HTMLTreeBuilder + HTMLElementStack + HTMLConstructionSite
nsHtml5TreeBuilder.cppTreeBuilder.java から生成
木の持ち方と寿命
html/dom.rs NodeRc / Weak / RefCell
core/dom/node.h(兄弟を連結リスト)Oilpan(トレース GC)
dom/base/nsINode.h参照カウント + サイクルコレクタ
id で探す
get_element_by_id(全走査)
TreeScope の TreeOrderedMap(id → 要素)
dom/base/Document.cpp
(無し) 文字参照 &
html_entity_parser.cc + html_entity_table.h
make named_characters の生成表
(無し) 先読み
HTMLPreloadScanner / BackgroundHTMLScannerscript 待ちの間に img / link を先に取る
nsHtml5StreamParser + nsHtml5SpeculativeLoad
  • ・両方とも WPT の html/syntax/parsing を共通のテストにしている
  • ・Firefox の HTML パーサーは Java から生成した C++。parser/html/java/README.txt の make translate

状態機械と挿入モードは 3 つとも同じ設計です。差は寿命管理(GC か手動か)と、止まっている間に先を読む仕組みの有無です。

③ CSS → 計算値(F 章)

左: この実装 / 中: Chromium(Blink・V8・//net) / 右: Firefox(Gecko・SpiderMonkey・Necko)
css/tokenizer.rsnext_token() で 1 つずつ
CSSTokenizer(css_tokenizer.h)TokenizeSingle() で 1 つずつ
rust-cssparser(third_party/rust/cssparser)Rust のクレート
ルールを持ち、要素と照合
css/cssom.rs StyleSheet / Selector::matches
RuleSet(右端のセレクタで棚分け)+ SelectorChecker
Stylist(stylist.rs)+ selectors クレート
カスケードと継承
renderer/style.rs compute_style(詳細度, 出現順)で安定ソート
StyleResolver → StyleCascade → ComputedStyle起源・重要度・詳細度・順序
Stylo の cascade → ComputedStyle(style struct)rayon で木を並列走査
  • ・Blink の手順は core/css/style-calculation.md に書かれている
  • ・Firefox 57 以降のスタイルシステムは Servo 由来の Rust(servo/components/style)

F 章で書いた Rust のコードに最も近い本物は Firefox です。Stylo は Rust で書かれ、cssparser・selectors・rayon を使っています。

④ レイアウトと描画(G 章)

左: この実装 / 中: Chromium(Blink・V8・//net) / 右: Firefox(Gecko・SpiderMonkey・Necko)
renderer/layout.rs LayoutBox / layout_blockCursor を渡して書き換える
LayoutObject の木 + LayoutNGBlockNode + ConstraintSpace → 不変の PhysicalFragment
nsIFrame の frame tree + reflowReflowInput → ReflowOutput、dirty ビット
文字の幅
CHAR_WIDTH_RATIO(文字数 × 比率)
HarfBuzzShaper(platform/fonts/shaping)
gfx/harfbuzz + gfxFont
表示リスト → 画面
renderer/paint.rs DisplayItem → render_ascii表示リストとラスタライズを 1 段で
display item → PaintChunk → cc::Layer → cc → Vizcc はタイルで並列ラスタライズ、Viz は GPU プロセスで合成
nsDisplayList → WebRenderRust の WebRender が GPU プロセスで描く
  • ・Blink: core/layout/layout_ng.md、core/paint/README.md、docs/how_cc_works.md、components/viz/README.md
  • ・Firefox: firefox-source-docs の Layout Overview と Rendering Overview

本物は『前回の結果を使い回す』ための仕組み(不変の fragment、display item のキャッシュ、タイル)が層になっています。この実装は毎回全部を作り直します。

⑤ JavaScript(H 章)

左: この実装 / 中: Chromium(Blink・V8・//net) / 右: Firefox(Gecko・SpiderMonkey・Necko)
js/lexer.rsPeekable で 1 文字先読み
src/parsing/scanner.cc先読み最大 4 文字、予約語は完全ハッシュ
js/src/frontend/TokenStream.h
構文解析
js/parser.rs → js/ast.rs
parser.cc → ast.h → BytecodeGeneratorpreparser.cc で遅延解析
Parser.cpp → BytecodeEmitter.cpplazy parsing、Stencil
実行
js/runtime.rs(AST を歩く)
Ignition → Sparkplug → Maglev → TurboFanfeedback で段階的に機械語へ
Interpreter → Baseline → WarpMonkeyインラインキャッシュで加速
変数の置き場
Environment の連鎖
Context / ScopeInfo(src/objects)
EnvironmentObject / Scope(js/src/vm)
メモリ
Rc(GC 無し)
Orinoco(世代別・並列・増分・並行)
js/src/gc(Nursery、増分)
ブラウザ API の橋
ConsoleLog / GetElementById(手書き 2 本)
Web IDL → bindings/ で生成(ラッパー)
dom/webidl → dom/bindings/Codegen.py
  • ・V8: v8.dev/blog/scanner、/blog/maglev、/blog/trash-talk
  • ・SpiderMonkey: firefox-source-docs の js/ の概要

字句と構文は同じ形です。差は『AST を歩く』か『バイトコードにしてから段階的に機械語へ』か、そして GC があるかです。

⑥ つなぎ目とプロセス(browser.rs / main.rs)

左: この実装 / 中: Chromium(Blink・V8・//net) / 右: Firefox(Gecko・SpiderMonkey・Necko)
browser.rs Page::load(1 回通す)parse → style → script → layout → paint
DocumentLifecycle + メインスレッドのタスクランナーTaskType 付きのタスク、入力が最優先
PresShell + TaskController(xpcom/threads)イベントループで必要な段をやり直す
プロセスの分け方
main.rs(1 プロセス)
browser / renderer(サイトごと)/ Network Service / GPU(Viz)docs/process_model_and_site_isolation.md
parent / content(webIsolated=サイト)/ socket / GPU / RDDfirefox-source-docs の Process Model(Fission)
  • ・『サイト』= scheme + eTLD+1。描画プロセスは 1 サイトに固定され、ブラウザプロセスが IPC を検査する(Chromium)

この実装の main.rs は 1 プロセスで全部をやります。本物は信頼の度合いで分け、描画プロセスは通信も GPU も直接触れません。

図やコードに出てくる用語(タップで説明)

公式ドキュメントで詳しく ↗