実際のところ「ブラウザを立ち上げてページが表示されるまで」には何が起きるのか

実際のところ「ブラウザを立ち上げてページが表示されるまで」には何が起きるのか


実際のところ「ブラウザを立ち上げてページが表示されるまで」には何が起きるのか


問題のツイート

面接の質問で「ブラウザを立ち上げてページが表示されるまでの仕組みを全て知ってる限り説明してください」ってのをやると結構Web系の知識どれだけあるか分かると思ってる

— (@tan_go238)










解釈

今回は「ChromeのURL欄に入力してからページが表示されるまで」をやります。ブラウザの起動云々はWeb系の話じゃないと信じてます。


1. HTTPリクエストが飛ぶ

HTTP2のヘッダ圧縮技術に全て書いてありました。


実際のリクエストヘッダ

:authority: www.google.co.jp

method
GET
path
/
scheme
https

accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
accept-encoding: gzip, deflate, br
accept-language: ja,en-US;q=0.9,en;q=0.8
cache-control: max-age=0
cookie: 🤫
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.81 Safari/537.36
x-client-data: CJW2yQEIpbbJAQjBtskBCKmdygEI2J3KAQjancoBCKijygEIh6fKAQ==

今回はリクエストメソッドがGETなのでヘッダのみですが、POSTやPUTの場合はデータがリクエストボディで送られます。詳しくはHTTPメソッド(CRUD)についてまとめたなどを参照してください。

余談ですが、何年か前に【パズル1】ほとんどのエンジニアには解けるが、下位10%のダメなエンジニアにだけ解けないパズル?で遊んだのを思い出しました。楽しくHTTPメソッドについて学べるパズルなので解いていない人はぜひ解いてみてください。


実際のレスポンスヘッダ

HTTP/1.1 200
status: 200
date: Tue, 11 Sep 2018 09:36:30 GMT
expires: -1
cache-control: private, max-age=0
content-type: text/html; charset=UTF-8
strict-transport-security: max-age=3600
content-encoding: br
server: gws
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
set-cookie: 1P_JAR=2018-09-11-09; expires=Thu, 11-Oct-2018 09:36:30 GMT; path=/; domain=.google.co.jp
alt-svc: quic=":443"; ma=2592000; v="44,43,39,35"

レスポンスヘッダは上みたいな感じで、このヘッダと一緒に届くレスポンスボディが<!doctype html>〜〜というhtmlドキュメントです。

もっと低レイヤーについてはOSI参照モデルまとめが分かりやすいです。

DNS?何ですかそれは。

DNSプロトコルってよく分かりませんしHTTPSで名前解決できたら楽でいいですよね。


2. レンダリングエンジンが描画を行う

レスポンスボディで返ってきたhtmlドキュメントを元にブラウザに搭載されているレンダリングエンジンが画面を描画します。

Chromeの場合はBlinkというエンジンが搭載されています。

レイヤー図

(image from How Blink works by Kentaro Hara)

で、このBlinkで行われているレンダリングプロセスが下図です。

レンダーパイプライン

(image from How Blink works by Kentaro Hara)

htmlドキュメントを解釈してDOMを構築し、styleを当てはめてレイアウトしてから描画です。あとはLife of a Pixel 2018を見てください。マジで分かりやすいのでオススメです。これを見た後じゃあ自分で書き直そうという気にはなれませんね。

でも「どうしてもお前が書いた解説が読みたいんだ!!!」という人の為に書きます。正直これ以降の部分は質が微妙です。プロのあなたの助けを求めています。コメント・編集リクエストお待ちしています。


DOMとは

Document Object Modelの略です。chromiumのDOMのページに実装などの情報がまとまっています。

「いやDocument Object Modelって何やねん」となると思うのですが、端的にいうと木です。tree。

DOMはChromeで「⌘+⌥+i」を押すと出現する検証モードの「element」の項目に並んでいるアイツです。キーボードに⌘キーや⌥キーが無い場合はこちらへどうぞ。


(例)google.co.jpのDOM

DOCUMENT (always the root)
└─HTML
├─HEAD
│ └─...
└─BODY
├─DIV
│ └─...
├─SCRIPT
└─SCRIPT

それぞれのnodeの実装

container node graph

(image from core/dom/README.md by きらきら☆はやとたん)

親はfirstChildとlastChildの情報しか持ってないそうです。連結リストってヤツですね。

レンダリングエンジンはHTML文章を解析し、上記のようなDOMツリーを構築します。


Style

レンダリングエンジンはCSSを解析してCSS Object Modelツリーを構築します。


元のCSS

body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }

⬇️CSSOM

CSSOM

(image from オブジェクト モデルの構築 by Ilya Grigorik)

DOMとCSSOMを組み合わせるとRender Treeが完成します。

render tree

(image from レンダリング ツリーの構築、レイアウト、ペイント by Ilya Grigorik)

ちなみにこのCSSOMはJavascriptにexpose(日本語訳求む)されているのでwindow.getComputedStyle() で要素のスタイルを取得する事ができます。


Layout

レンダリングツリーを元に、個々の要素がウィンドウ内のどこに配置されるかを決めていきます。

css box model

(image from CSS Box Model Module Level 3)

上の画像みたいなCSS Box Modelを使って、親から子へと再帰的に、つまり子は親のcontent内にレイアウトされていきます。CSSでは親要素との相対的な位置関係で記述されていますが、レイアウト処理の出力段階では画面上の絶対的な位置になります。

Life of a Pixel 2018 (1).png

(image from Life of a Pixel 2018)

Life of a Pixel 2018.png

(image from Life of a Pixel 2018)

中のコンテンツがスペースをオーバーしてもOKで、その時の選択肢は全て表示する「visible」、オーバーした部分を表示しない「hidden」、そしてスクロールできるようにする「scroll」の3つです。


  • SCROLLING IN BLINK | スクロールの仕組みについて。position: stickyって初めて知ったけどめっちゃ便利じゃないですか?もっと早く知りたかった……

後LayoutNGなる新しいレイアウトシステムが開発中らしい。


Paint

表示されるべき要素とそのスタイルが計算できたので、最後にそれぞれの要素を実際のピクセルに落とし込んでいきます。

Life of a Pixel 2018 (2).png

(image from Life of a Pixel 2018)

DOMツリーとは関係なく、描画リストの順番で描画されていきます。CSSのz-indexを設定する事で順番を明示的に指定できます。

Life of a Pixel 2018 (3).png

(image from Life of a Pixel 2018)

また、backgroundやfloatなどの要素によってどれが上に来るのかが変わるので、背後の要素が部分的に前に出て来ることもあるらしいです。

この描画工程が終わると画面にページが表示されます。


Compositor thread

ブラウザのレンダリング処理は全てmainスレッドという一つのスレッドで行われます。スレッドが一つしか無いというのは、javascriptなどで何か重たい処理を実行してしまいスレッドを埋めてしまった場合にスクロールなど基本的な操作すらおぼつかなくなってしまう危険があるということです。

そこで、スクロールやアニメーション、ズームなど「レイヤーの移動・スケールの変更」で対処できるものに関しては、レンダリングが終わった後に別スレッドへと移動させ、スムーズな操作を可能にしようというのが「Compositor Thread Architecture」です。

Life of a Pixel 2018 (4).png

(image from Life of a Pixel 2018)


プロの方々のコメントや編集リクエストをお待ちしています。よろしくお願いします。

この記事よりもLife of a Pixel 2018を読みましょう。ブラウザのレンダリングについて学ぶなら最高にオススメのスライドです。