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

18. CSSOM — ルールは prelude と宣言ブロック、読めないものは捨てて先へ進む

CSS のの出力は 、「 → ルール → と」の木です。CSS Syntax Level 3 §5 の言葉では、 は「component value のリストからなる と、宣言のリスト」を持ち、declaration は「名前(文字列)と、component value のリストからなる値」を持ちます。prelude とは "{" より前の部分で、スタイルルールではこれがセレクタです。

参照実装の CssParser は、を で 1 つしながら進みます。parse_stylesheet はルールを繰り返し読み、先頭が AtKeyword なら at-rule を丸ごと読み飛ばし(@media や @charset はこの実装では解釈しない)、そうでなければ consume_qualified_rule。consume_qualified_rule は "{" までを prelude として集め、parse_selector_list でセレクタにし、consume_declarations で "}" までの宣言を読みます。宣言は Ident、":"、";" か "}" までの値、の形です。

芯はエラー回復です。仕様は、読めないものに出会ったら「通常の解析に戻れる最小限だけを捨てる」と定めています。":" の無い宣言(color red;)は次の ";" までを捨ててルール自体は生かす。"{" の前に入力が終わったルールは捨てる。at-rule は ";" か対応する "}" まで飛ばす。知らないは CSSOM には入れますが、次の段()で無視されます。だから 1 か所のタイプミスでスタイルシート全体が死ぬことはありません。

セレクタは単純セレクタ 1 つだけを読みます。Ident なら型(p)、Hash なら ID(#main)、Delim('*') なら全称、Delim('.') + Ident ならクラス(.note)。それ以外(div p、p.note、a:hover)は Unsupported として残し、何にも一致させません。h1, h2 { } のようなコンマ区切りのリストは、それぞれ別のセレクタとして同じ宣言を持たせます。

本物のブラウザでは: はスタイルシートを StyleSheetContents / CSSStyleSheet に持ち、照合のために へ入れ直します。RuleSet は各ルールの一番右のセレクタで棚分けし、たとえば p.cname は ClassRules の "cname" の棚に入ります(core/css/style-calculation.md の FindBestRuleSetAndAdd)。に class が無ければその棚は見ないので、全ルールを総当たりしません。Firefox の Stylist(servo/components/style/stylist.rs)も同じ役です。この回の <QualifiedRule> を先頭から全部見る形は、棚分けを省いた版です。

開発のどこに活かすか: 「壊れた入力の一部だけを捨てて続ける」設計は、ログの取り込み、CSV のバッチ処理、設定ファイルの読み込みで、全停止より望ましいことがよくあります。その際に「どこまで捨てるか」の境界(ここでは ; と })を仕様として決めておくことが、挙動の予測可能性を保ちます。また、知らない項目を保持して後段に判断を委ねる(未知のプロパティを CSSOM に残す)のは、前方互換のための定番です。

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

▶ サンプルコードを実行してみよう(自由に書き換えてOK)

Rust

手元で動かす(配布物)

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

サンプルコードで身につける(3本) →

学んだ概念を、素朴な例から実務寄りの例まで3つの具体例で確認します。

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