the various client programs that initiate a request. 訳 「user agent」という用語は、リクエストを開始するさまざまなクライアントプログラムを指す RFC 9110 (HTTP Semantics) §3.5 User Agents rfc-editor.org/rfc/rfc9110.html#name-user-agents A user agent is any program that interprets a document written in the document language and applies associated style sheets … 訳 user agent とは、文書言語で書かれた文書を解釈し、対応するスタイルシートを適用するあらゆるプログ ラムである CSS 2.1 §3.1 Definitions w3.org/TR/CSS21/conform.html#user-agent HTTP は「取ってくる人」、CSS は「解釈して描く人」として、同じ言葉を定義している。 14
CSS 2.1「文書を解釈し、スタイルシートを適用する」 操作を受け付ける — HTML Standard の適合クラスの名前そのもの Web browsers and other interactive user agents 訳 Web ブラウザ、および対話的に操作できるその他の user agent HTML Standard §2.1.8 Conformance classes html.spec.whatwg.org/multipage/infrastructure.html#conformance-classes 15
agents Non-interactive presentation user agents Visual user agents that support the suggested default rendering User agents with no scripting support Conformance checkers Data mining tools Authoring tools and markup generators どういうものか 描いて、人間の操作を受け付ける 描くが操作させない。仕様の例はプリンタと電光掲示板 既定の見た目(UA スタイルシート)を実装する スクリプトを実行しない。JS を切ったブラウザもここ 適合性を検査する。いわゆる HTML validator 描画も検査もせず、中身だけ使う。クローラ HTML を書き出す側。エディタや SSG HTML Standard §2.1.8 Conformance classes html.spec.whatwg.org/multipage/infrastructure.html#conformance-classes 16
section describes an algorithm for converting a URI reference that might be relative to a given base URI into the parsed components of the reference's target. 訳 本節では、ある基準 URI に対して相対的でありうる URI 参照を、その参照先の構成要素へと変換するアル ゴリズムを述べる RFC 3986 (URI: Generic Syntax) §5.2 Relative Resolution rfc-editor.org/rfc/rfc3986.html#section-5.2 仕様の側が「アルゴリズム」と言い切っている。 場合分けの手順まで決まっている。 23
1 ⽂字ずつ読んで、タグ境界でトークンを放出 StartTag / EndTag / Characters / EndOfFile 2 ツリー構築 構⽂解析 「⽂書のどこにいるか」で挿⼊先を決める DOM ツリー この 2 段は、実装の都合ではなく仕様の指定。 … passed through a tokenization stage followed by a tree construction stage. HTML Standard §13.2.1 html.spec.whatwg.org/multipage/parsing.html#overview-of-the-parsing-model 29
following state machine to tokenize HTML. The state machine must start in the data state. 訳 実装は、HTML をトークン化するのに以下の状態機械を使ったかのように振る舞わなければならない。状態 機械は data state から開始しなければならない。 HTML Standard §13.2.5 Tokenization html.spec.whatwg.org/multipage/parsing.html#tokenization 13.2.5.1 13.2.5.6 13.2.5.32 13.2.5.36 13.2.5.53 ⋮ 13.2.5.84 Data state Tag open state Before attribute name state Attribute value (double-quoted) state DOCTYPE state Numeric character reference end state 状態は 84 個。ひとつずつ名前が付いていて、どの文字でどこへ移るかまで決まっている。 30
> なら、属性なしでそのまま放出 英字 < Data state > Tag open state 空⽩ Before attribute Tag name state name state 英字 StartTag を放出 値の終わり 値の始まり " " = After attribute value Attribute value Before attribute (quoted) state (double-quoted) state value state Attribute name state 31
= self._consume() if character is None: self._emit_eof() elif character in WHITESPACE: self.state = TokenizerState.BEFORE_ATTRIBUTE_NAME elif character == "/": self.state = TokenizerState.SELF_CLOSING_START_TAG elif character == ">": self._emit_tag() # ← タグが確定して放出 else: self._tag_name += character.lower() 仕様の「Tag name state」1 節の分岐が、そのままこの 12 行になる。 33
element in button scope, then close a p element. Insert an HTML element for the token. 訳 開いている要素のスタックに p があれば、その p を閉じる。そのうえで、このトークンの要素を挿入する。 HTML Standard §13.2.6.4.7 The "in body" insertion mode html.spec.whatwg.org/multipage/parsing.html#parsing-main-inbody 壊れた HTML をどう直すかは、実装者の判断ではない。だから自作でも Chrome と同じ木になる。 39
字句解析 1 ⽂字ずつ読んで、識別⼦や記号のトークンを放出 IdentToken / HashToken / OpenBraceToken / … 2 ルール構築 構⽂解析 セレクタと宣⾔をまとめて 1 ルールにする スタイルルール 同じ一文が、CSS の仕様書にも記載がある。 … passed through a tokenization stage followed by a tree construction stage. HTML Standard §13.2.1 / CSS Syntax Module Level 3 §3.1 w3.org/TR/css-syntax-3/#parsing-overview 43
None], initial: T, inherited: T, is_inherited: bool) -> T: default = inherited if is_inherited else initial for text in self._values.get(name, ()): match text.strip().lower(): case "inherit": return inherited case "initial": return initial case "unset" | "revert": return default parsed = parse(text) if parsed is not None: return parsed return default TypeVar の宣言も Generic[T] の継承も要らない。 [T] と書くだけ。 PEP 695 Type Parameter Syntax(Python 3.12〜) peps.python.org/pep-0695 48
union 親の下端 畳み込み(fold) はみ出した子 箱 1 つが BoxFragment、その矩形が Rect この下端が、ページの⾼さ document_height これをコードにすると、自分の枠から始めて、子の結果も入る大きさに広げていくだけ。 def deep_overflow(fragment: Fragment) -> Rect: rect = fragment.rect # ← 自分の枠から始めて if isinstance(fragment, BoxFragment): # ← 子を持つのは箱だけ if fragment.style.overflow_y.clips and fragment.style.overflow_x.clips: return rect # ← overflow: hidden なら打ち切る for child in fragment.children: rect = rect.union(deep_overflow(child)) # ← 子孫を 1 つの枠に畳む return rect 56
for child in self.children: if isinstance(child, Element): yield child yield from child.descendant_elements() # ← 子の分は子に任せる for element in document.descendant_elements(): # 木が、ただの for になる ... elements = sum(1 for _ in document.descendant_elements()) 再帰を書くのはこの 5 行だけ。使う側は、ただの for で回せる。 57
-> None: match item: case SolidFill(): self._draw_solid(item) case GradientFill(): self._draw_gradient(item) case ImageFill(): self._draw_image(item) case BorderPaint(): self._draw_border(item) case BoxShadowPaint(): self._draw_box_shadow(item) case TextPaint(): self._draw_text(item) case PushClip(): self._push_clip(item) case PushOpacity(): self._push_opacity(item) case PopState(): self._pop_state() 描画命令( DisplayItem )は 12 種類の frozen dataclass の Union クラスパターンで分岐すると、case の中では mypy が型を絞ってくれる 59