Webブラウザの仕組み

# はじめに
URL入力〜画面描画までのブラウザの仕組みについてまとめた。
誰が何をやるのかについて、少し曖昧なところがある。

# 全体像
今回記述するブラウザの仕組みは、以下の図の粒度で説明する。

```plantuml
@startuml
title ブラウザの仕組みシーケンス図
autonumber
actor ユーザ as user
participant 画面 as view
participant ユーザインタフェース as ui #lightblue

participant ブラウザエンジン as be #lightblue
participant レンダリングエンジン as re #lightblue
participant JavaScriptエンジン as je #lightblue
participant Webサーバー as ws #lightgreen

user -> ui : アドレスバーにURLを入力
ui -> be : URLを渡す
be -> be : URLを解析し、HTTPリクエスト作成
be -> ws : HTTPリクエストを送信
ws -> be : HTTPレスポンスを受信
be -> re : htmlを渡す
re -> re : htmlを字句解析しトークン配列を生成
re -> re : htmlを構文解析しDOMを生成
alt styleタグがある場合
re -> re : cssを字句解析しトークン配列を生成
re -> re : cssを構文解析しCSSOMを生成
else scriptタグがある場合
re -> je : JavaScriptを渡す
je -> je : JavaScriptを字句解析しトークン配列を生成
je -> je : JavaScriptを構文解析しノードを生成
je -> je : 実行する
alt JavaScriptにDOM操作が含まれる場合
je -> re : DOMを操作する
else JavaScriptにCSSOM操作が含まれる場合
je -> re : CSSOMを操作する
end
end
re -> be : DOMとCSSOMを渡す
be -> be : レイアウトツリー生成
be -> be : ラスタライズ
be -> view : ドットをWebページに表示する
view -> user : Webページが見れる
@enduml
```

# ブラウザの仕組み
## 1.　URLを入力〜HTTPレスポンス受信
まず、ユーザがブラウザのアドレスバーに閲覧したいWebページのURLを入力する。ユーザインタフェース機能が、認識したURLを構文解析する。
ブラウザエンジンが構文解析したURLからHTTPリクエストを生成し、WebサーバにHTTPリクエストを送信する。
WebサーバからHTTPレスポンスを受け取る。
ブラウザエンジンがレスポンスを分解し、HTMLをHTMLレンダリングエンジンに渡し処理を依頼する。

## 2.　HTMLの解析〜DOMの構築
### HTMLレンダリングエンジンがHTMLを字句解析する
トークン単位でHTMLを分解する。
トークンの種類としては、以下の６つがある。
* DOCTYPE
* 開始タグ
* 終了タグ
* コメント
* 文字列
* EOF

トークンは、トークンインタフェースを実装している。
トークンインタフェースには、メンバーとして、次のトークンへのポインタを持っている。そうすることで、htmlの順序をがわかる仕組みである。

以下の文字列を想定し、トークンの分割例を示す。
例:
`<body><h1>HelloWorld</h1></body>`


```plantuml
@startuml
Token--start_body
Token--start_h1
Token--text
Token--end_h1
Token--end_body
Token--eof

class Token {
TokenType type
char tag_name
char data
struct Token next
}

object start_body {
  type = 開始タグ
  tag_name = body
  data = null
  next = start_h1
}
object start_h1 {
  type = 開始タグ
  tag_name = body
  data = null
  next = text
}
object text {
  type = 文字列
  tag_name = null
  data = HelloWorld
  next = end_h1
}
object end_h1 {
  type = 終了タグ
  tag_name = h1
  data = null
  next = end_body
}
object end_body {
  type = 終了タグ
  tag_name = body
  data = null
  next = eof
}
object eof {
  type = EOF
  tag_name =null
  data = null
  next = null
}
@enduml
```

### HTMLレンダリングエンジンがHTMLを構文解析する
基本的に1つのタグが1つのノードになる。言い換えるとトークンをタグの単位でまとめる。
ノードの種類に応じて、Nodeインタフェース、Elementインタフェース、HTMLElementインタフェース、HTMLBodyインタフェースを各ノードに実装する。

例：
```plantuml
@startuml
Node--body
Node--h1
Node--text

class Node {
NodeType type
Element Type element_type
char tag_name
char data
struct Node parent
struct Node first_child
struct Node last_child
struct Node previous_sibiling
struct Node next_sibiling
}

object body {
type = Element
element_type = Body
tag_name = body
data = null
parent = null
first_child = h1
last_child = h1
previous_sibiling = null
next_sibiling = null
}
object h1 {
type = Element
element_type = H1
tag_name = h1
data = null
parent = body
first_child =text
last_child = text
previous_sibiling = null
next_sibiling = null
}
object text {
type = Text
element_type = null
tag_name = null
data = HelloWorld
parent = h1
first_child = null
last_child = null
previous_sibiling = null
next_sibiling = null
}

@enduml
```
## 3.　CSSの解析〜CSSOMの構築
HTMLの構文解析時に、styleタグがあった場合、この処理を行う。
HTMLレンダリングエンジンがCSSの字句解析と構文解析をする。CSSの字句解析、構文解析は、2. でHTMLに対しての手順と同じような処理である。
言語が異なっているが、やることは一緒である。

## 4.　JavaScriptの解析〜実行
HTMLの構文解析時に、scriptタグがあった場合、この処理を行う。
ここにsrc属性が付加されている場合、srcのリンクを以下のいずれかからバイトストリームをロードする。
* ネットワーク
* キャッシュ
* Service Worker

バイトストリームデコーダがバイトストリームをデコードし、バイトにする。バイトを字句解析してトークンごとに区切る。
JavaScriptエンジンのプリパーサーが構文エラーがないかチェックするし、パーサが構文解析し、ノードツリーを生成する。
これをプログラムとして実行しDOMやCSSOMを操作する。DOMやCSSOMが変化することで必要に応じて画面の描画が更新されることになる。

## 5.　レンダリングツリーの生成〜画面描画
DOMとCSSOMからレンダリングツリーを生成する。レンダリングツリーからレイアウトツリーを生成する。
レイアウトツリーをラスタライズし、画面に描画する。

#  参考文献
* 雑誌 WEB+DB vol.120  Webページが表示されるまで
* [ブラウザのしくみ](https://web.dev/howbrowserswork/)

# おわりに
ブラウザの仕組みについてまとめた。