how-web-works - ブラウザに www.google.com と入力すると、バックグラウンドで何が起こるでしょうか?

(What happens behind the scenes when we type www.google.com in a browser?)

Created at: 2015-10-17 12:39:35
Language:

ウェブのしくみ

ブラウザに google.com と入力すると、舞台裏で何が起こるでしょうか?

目次

Google の「g」キーを押す

「g」を押すだけで、ブラウザがイベントを受信し、オートコンプリート機構全体が高速化されます。ブラウザのアルゴリズムに応じて、またプライベート/シークレット モードであるかどうかに応じて、URL バーの下のドロップボックスにさまざまな提案が表示されます。これらのアルゴリズムのほとんどは、検索履歴とブックマークに基づいて結果に優先順位を付けます。「google.com」と入力するので問題はありませんが、そこに到達する前に多くのコードが実行され、キーを押すたびに候補が洗練されます。入力する前に「google.com」が表示される場合もあります。

「Enter」を押すと

ゼロ点を選択するには、キーボードの Enter キーを選択して、その範囲の一番下を押します。この時点で、Enter キーに固有の電気回路が (直接または容量的に) 閉じられます。これにより、少量の電流がキーボードのロジック回路に流れ込み、各キー スイッチの状態をスキャンし、スイッチが断続的に閉じることによる電気ノイズをデバウンスし、それをキーコード整数に変換します。 13. キーボード コントローラは、コンピュータに転送するためにキーコードをエンコードします。これは現在、ほぼ普遍的に、ユニバーサル シリアル バス (USB) または Bluetooth 接続を介して行われています。

USBキーボードの場合:

  • 生成されたキーコードは、内部キーボード回路メモリによって「エンドポイント」と呼ばれるレジスタに保存されます。
  • ホスト USB コントローラーは、その「エンドポイント」を ~10ms ごとにポーリングするため、そこに格納されているキーコード値を取得します。
  • この値は、最大速度 1.5 Mb/s (USB 2.0) で送信される USB SIE (シリアル インターフェイス エンジン) に送られます。
  • このシリアル信号は、コンピューターのホスト USB コントローラーでデコードされ、コンピューターのヒューマン インターフェイス デバイス (HID) ユニバーサル キーボード デバイス ドライバーによって解釈されます。
  • 次に、キーの値がオペレーティング システムのハードウェア抽象化レイヤーに渡されます。

タッチ スクリーン キーボードの場合:

  • ユーザーが最新の静電容量式タッチ スクリーンに指を置くと、微量の電流が指に転送されます。これにより、導電層の静電場を通る回路が完成し、画面上のそのポイントで電圧降下が発生します。次に、スクリーン コントローラは、「クリック」の座標を報告する割り込みを発生させます。
  • 次に、モバイル OS は、現在フォーカスされているアプリケーションに、その GUI 要素の 1 つ (現在は仮想キーボード アプリケーション ボタン) のクリック イベントを通知します。
  • 仮想キーボードは、「キーが押されました」というメッセージを OS に送り返すためのソフトウェア割り込みを発生させることができるようになりました。
  • この割り込みは、現在フォーカスされているアプリケーションに「キーが押された」イベントを通知します。

URL を解析する

ブラウザーの URL (Uniform Resource Locator) に次の情報が含まれるようになりました。

  • プロトコル "http": 「ハイパー テキスト転送プロトコル」を使用します
  • リソース "/": メイン (インデックス) ページを取得します

プロトコルまたは有効なドメイン名が指定されていない場合、ブラウザーは、アドレス ボックスに指定されたテキストをブラウザーの既定の Web 検索エンジンにフィードします。

HSTS リストを確認する (非推奨)

  • ブラウザは、「プリロードされた HSTS (HTTP Strict Transport Security)」リストをチェックします。これは、HTTPS 経由のみでの接続を要求した Web サイトのリストです。
  • Web サイトがリストにある場合、ブラウザーはその要求を HTTP ではなく HTTPS 経由で送信します。それ以外の場合、最初のリクエストは HTTP 経由で送信されます。

注: Web サイトは、HSTS リストに含まれていなくても HSTS ポリシーを使用できます。ユーザーによる Web サイトへの最初の HTTP 要求は、ユーザーが HTTPS 要求のみを送信することを要求する応答を受け取ります。ただし、この単一の HTTP 要求により、ユーザーがダウングレード攻撃に対して脆弱になる可能性があります。これが、最新の Web ブラウザーに HSTS リストが含まれている理由です。

最新のブラウザーは最初に https を要求します

DNS ルックアップ

ブラウザは、入力されたドメインの IP アドレスを見つけようとします。DNS ルックアップは次のように進行します。

  • ブラウザー キャッシュ:ブラウザーは DNS レコードをしばらくの間キャッシュします。興味深いことに、OS は各 DNS レコードの有効期限をブラウザーに通知しないため、ブラウザーはそれらを一定期間キャッシュします (ブラウザーによって異なりますが、2 ~ 30 分)。
  • OS キャッシュ:ブラウザーのキャッシュに必要なレコードが含まれていない場合、ブラウザーはシステム コール (Windows では gethostbyname) を行います。OSには独自のキャッシュがあります。
  • ルーター キャッシュ:要求は、通常は独自の DNS キャッシュを持つルーターに続きます。
  • ISP DNS キャッシュ:次にチェックされる場所は、キャッシュ ISP の DNS サーバーです。もちろん、キャッシュ付き。
  • 再帰的検索: ISP の DNS サーバーは、ルート ネームサーバーから .com の最上位ネームサーバーを経由して Google のネームサーバーまで、再帰的検索を開始します。通常、DNS サーバーは .com ネームサーバーの名前をキャッシュに持っているため、ルート ネームサーバーにヒットする必要はありません。

以下は、再帰的な DNS 検索がどのようなものかを示す図です。

再帰的 DNS 検索

DNS に関する懸念事項の 1 つは、wikipedia.org や facebook.com などのドメイン全体が単一の IP アドレスにマップされているように見えることです。幸いなことに、ボトルネックを軽減する方法があります。

  • ラウンド ロビン DNSは、DNS ルックアップが 1 つではなく複数の IP アドレスを返すソリューションです。たとえば、facebook.com は実際には 4 つの IP アドレスにマップされます。
  • ロードバランサーは、特定の IP アドレスをリッスンし、要求を他のサーバーに転送するハードウェアの一部です。主要なサイトでは通常、高価な高性能ロード バランサが使用されます。
  • 地理的 DNSは、クライアントの地理的な場所に応じてドメイン名を異なる IP アドレスにマッピングすることで、スケーラビリティを向上させます。これは、異なるサーバーが共有状態を更新する必要がないように、静的コンテンツをホストするのに最適です。
  • エニーキャストは、単一の IP アドレスが複数の物理サーバーにマップされるルーティング手法です。残念ながら、エニーキャストは TCP とうまく適合せず、そのシナリオではめったに使用されません。

ほとんどの DNS サーバー自体はエニーキャストを使用して、DNS ルックアップの高可用性と低遅延を実現しています。エニーキャスト サービス (DNS は優れた例です) のユーザーは、常に「最も近い」(ルーティング プロトコルの観点から) DNS サーバーに接続します。これにより、待ち時間が短縮されるだけでなく、一定レベルの負荷分散が提供されます (コンシューマーがネットワーク全体に均等に分散されていると仮定します)。

ソケットのオープン + TLS ハンドシェイク

  • ブラウザーが宛先サーバーの IP アドレスを受信すると、URL から指定されたポート番号 (HTTP プロトコルのデフォルトはポート 80、HTTPS はポート 443) を取得し、socket という名前のシステム ライブラリ関数を呼び出します。TCP ソケットストリームを要求します。
  • クライアント コンピューターは、ClientHello メッセージをサーバーに送信し、その TLS バージョン、暗号アルゴリズムのリスト、および使用可能な圧縮方法を送信します。
  • サーバーは、TLS バージョン、選択された暗号、選択された圧縮方法、および CA (認証局) によって署名されたサーバーの公開証明書を含む ServerHello メッセージでクライアントに応答します。証明書には公開鍵が含まれており、クライアントはこの公開鍵を使用して、対称鍵が合意されるまで残りのハンドシェイクを暗号化します。
  • クライアントは、信頼できる CA のリストに対してサーバーのデジタル証明書を検証します。CA に基づいて信頼を確立できる場合、クライアントは疑似ランダム バイトの文字列を生成し、これをサーバーの公開鍵で暗号化します。これらのランダム バイトを使用して、対称キーを決定できます。
  • サーバーは、秘密鍵を使用してランダムなバイトを復号化し、これらのバイトを使用して対称マスター鍵の独自のコピーを生成します。
  • クライアントはサーバーに Finished メッセージを送信し、この時点までの送信のハッシュを対称キーで暗号化します。
  • サーバーは独自のハッシュを生成し、クライアントから送信されたハッシュを復号化して一致することを確認します。一致する場合は、対称キーで暗号化された独自の Finished メッセージをクライアントに送信します。
  • これ以降、TLS セッションは、合意された対称鍵で暗号化されたアプリケーション (HTTP) データを送信します。

HTTP プロトコル

Facebook/Gmail などの動的サイトは、ブラウザー キャッシュから提供されないことはほぼ確実です。動的ページはすぐに、またはすぐに期限切れになる (有効期限が過去に設定されている) からです。

使用されている Web ブラウザーが Google によって作成されたものである場合、ページを取得するために HTTP 要求を送信する代わりに、HTTP から SPDY プロトコルへの「アップグレード」についてサーバーと交渉するための要求を送信します。Chrome の最新バージョンでは、HTTP/2 を優先して SPDY が非推奨になっていることに注意してください。

GET http://www.google.com/ HTTP/1.1
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: google.com
Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]

GET リクエストは、取得する URL に「http://www.google.com/」という名前を付けます。ブラウザーは自身を識別し (User-Agent ヘッダー)、受け入れる応答の種類を示します (Accept および Accept-Encoding ヘッダー)。Connection ヘッダーは、以降の要求に対して TCP 接続を開いたままにしておくようにサーバーに要求します。

リクエストには、ブラウザがこのドメイン用に持っている Cookie も含まれています。おそらく既にご存じのとおり、Cookie はキーと値のペアであり、異なるページ リクエスト間の Web サイトの状態を追跡します。そのため、Cookie には、ログインしたユーザーの名前、サーバーによってユーザーに割り当てられた秘密の番号、ユーザーの設定の一部などが保存されます。Cookie はクライアント上のテキスト ファイルに保存され、すべてのリクエストでサーバー。

HTTP/1.1 では、応答の完了後に接続が閉じられることを送信者が通知するための "close" 接続オプションが定義されています。たとえば、接続: 閉じます。

リクエストとヘッダーを送信した後、Web ブラウザーは、リクエストの内容が完了したことを示す単一の空白の改行をサーバーに送信します。サーバーは、要求のステータスを示す応答コードで応答し、次の形式の応答で応答します: 200 OK [応答ヘッダー]

単一の改行が続き、www.google.comの HTML コンテンツのペイロードを送信します。その後、サーバーは接続を閉じるか、クライアントから送信されたヘッダーが要求した場合は、接続を開いたままにして、以降の要求に再利用できます。

Web ブラウザーによって送信された HTTP ヘッダーに、Web サーバーが Web ブラウザーによってキャッシュされたファイルのバージョンが最後の取得以降変更されていないかどうかを判断するのに十分な情報が含まれている場合 (つまり、Web ブラウザーに ETag ヘッダーが含まれている場合)、代わりに、 304 Not Modified [応答ヘッダー]の形式の要求で応答し、ペイロードなしで、Web ブラウザーは代わりにキャッシュから HTML を取得します。

HTML を解析した後、Web ブラウザー (およびサーバー) は、HTML ページによって参照されるすべてのリソース (画像、CSS、favicon.ico など) に対してこのプロセスを繰り返します。ただし、GET / HTTP/1.1 の代わりに、リクエストはGET /$になります。 ( www.google.comからの相対 URL ) HTTP/1.1。

HTML がwww.google.comとは異なるドメインのリソースを参照している場合、Web ブラウザは他のドメインの解決に関連する手順に戻り、そのドメインのこの時点までのすべての手順に従います。リクエストの Host ヘッダーは、google.com ではなく適切なサーバー名に設定されます。

落とし穴:

  • URL「http://facebook.com/」の末尾のスラッシュは重要です。この場合、ブラウザは安全にスラッシュを追加できます。http://example.com/folderOrFileの形式の URL の場合、folderOrFile がフォルダーなのかファイルなのかが明確でないため、ブラウザーは自動的にスラッシュを追加できません。このような場合、ブラウザーはスラッシュなしで URL にアクセスし、サーバーはリダイレクトで応答するため、不要なラウンドトリップが発生します。
  • サーバーは、「 http://google.com/ 」ではなく「 http://www.google.com/ 」に移動するようにブラウザに指示するために、 301 Moved Permanently 応答で応答する場合があります。サーバーが、ユーザーが見たい Web ページにすぐに応答するのではなく、リダイレクトを要求するのには興味深い理由があります。その理由の 1 つは、検索エンジンのランキングに関係しています。同じページに 2 つの URL がある場合は、http: //www.vasanth.com/とhttp://vasanth.com/と言います。、検索エンジンはそれらを2つの異なるサイトと見なし、それぞれのリンクが少ないため、ランキングが低くなります。検索エンジンは永続的なリダイレクト (301) を理解し、両方のソースからの着信リンクを 1 つのランキングに結合します。また、同じコンテンツの複数の URL はキャッシュに適していません。コンテンツに複数の名前がある場合、キャッシュに複数回表示される可能性があります。

注: HTTP 応答は、サーバーから返されたステータス コードで始まります。以下は、ステータス コードが示す内容の非常に簡単な要約です。

  • 1xx は情報メッセージのみを示します
  • 2xx は何らかの成功を示します
  • 3xx はクライアントを別の URL にリダイレクトします
  • 4xx は、クライアント側のエラーを示します
  • 5xx は、サーバー側のエラーを示します

HTTP サーバー要求ハンドル

HTTPD (HTTP デーモン) サーバーは、サーバー側で要求/応答を処理するサーバーです。最も一般的な HTTPD サーバーは、Linux の場合は Apache または nginx、Windows の場合は IIS です。

  • HTTPD (HTTP デーモン) が要求を受け取ります。

  • サーバーはリクエストを次のパラメータに分解します。

    • HTTP リクエスト メソッド (GET、POST、HEAD、PUT、および DELETE のいずれか)。アドレスバーに直接入力された URL の場合、これは GET になります。
    • ドメイン、この場合は google.com です。
    • 要求されたパス/ページ。この場合は - / (特定のパス/ページが要求されていないため、/ がデフォルトのパスです)。
    • サーバーは、サーバー上に google.com に対応する仮想ホストが構成されていることを確認します。
  • サーバーは、google.com が GET リクエストを受け入れることができることを確認します。

  • サーバーは、クライアントがこのメソッドの使用を許可されていることを確認します (IP、認証などによって)。

  • サーバーに書き換えモジュール (Apache の mod_rewrite や IIS の URL 書き換えなど) がインストールされている場合、構成されたルールのいずれかと要求を照合しようとします。一致するルールが見つかった場合、サーバーはそのルールを使用してリクエストを書き換えます。

  • サーバーはリクエストに対応するコンテンツを取得します。この場合、"/" がメイン ファイルであるため、インデックス ファイルにフォールバックします (これをオーバーライドできる場合もありますが、これが最も一般的な方法です)。

  • サーバーは、要求ハンドラーに従ってファイルを解析します。リクエスト ハンドラは、リクエストを読み取り、レスポンスの HTML を生成するプログラム (ASP.NET、PHP、Ruby など) です。Google が PHP で実行されている場合、サーバーは PHP を使用してインデックス ファイルを解釈し、出力をクライアントにストリーミングします。

注: すべての動的 Web サイトが直面する興味深い問題の 1 つは、データの保存方法です。小規模なサイトでは、多くの場合、データを格納するための単一の SQL データベースがありますが、大量のデータを格納するサイトや多数の訪問者がいるサイトでは、データベースを複数のマシンに分割する方法を見つける必要があります。ソリューションには、シャーディング (主キーに基づいて複数のデータベースにテーブルを分割すること)、レプリケーション、および一貫性のセマンティクスが弱められた簡素化されたデータベースの使用が含まれます。

サーバーの応答

サーバーが生成して送り返した応答は次のとおりです。

HTTP/1.1 200 OK
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
    pre-check=0
Expires: Sat, 01 Jan 2000 00:00:00 GMT
P3P: CP="DSP LAW"
Pragma: no-cache
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
X-Cnection: close
Transfer-Encoding: chunked
Date: Fri, 12 Feb 2010 09:05:55 GMT

2b3
��������T�n�@����[...]

応答全体は 36 kB で、それらの大部分はトリミングした末尾のバイト BLOB にあります。

Content-Encodingヘッダーは、応答本文が gzip アルゴリズムを使用して圧縮されていることをブラウザーに伝えます。BLOB を解凍すると、期待どおりの HTML が表示されます。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
      lang="en" id="google" class=" no_js">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-language" content="en" />
...

Content-Type を text/html に設定するヘッダーに注意してください。ヘッダーは、応答コンテンツをファイルとしてダウンロードするのではなく、HTML としてレンダリングするようブラウザーに指示します。ブラウザーはヘッダーを使用して応答を解釈する方法を決定しますが、URL の拡張子などの他の要因も考慮します。

ブラウザの舞台裏

サーバーがリソース (HTML、CSS、JS、画像など) をブラウザーに提供すると、次のプロセスが実行されます。

  • 解析 - HTML、CSS、JS
  • Rendering - DOM Tree の構築 → Render Tree → Render Tree のレイアウト → Render Tree のペイント

ブラウザの高レベル構造

  1. ユーザー インターフェイス:アドレス バー、戻る/進むボタン、ブックマーク メニューなどがあります。要求されたページが表示されるウィンドウを除く、ブラウザーのすべての部分が表示されます。

  2. ブラウザ エンジン: UI とレンダリング エンジンの間のアクションをマーシャリングします。

  3. レンダリング エンジン:要求されたコンテンツの表示を担当します。たとえば。レンダリング エンジンは HTML と CSS を解析し、解析されたコンテンツを画面に表示します。

  4. ネットワーク: HTTP 要求などのネットワーク呼び出しの場合、プラットフォームごとに異なる実装を使用します (プラットフォームに依存しないインターフェイスの背後で)。

  5. UI バックエンド:コンボ ボックスやウィンドウなどの基本的なウィジェットの描画に使用されます。このバックエンドは、プラットフォーム固有ではない汎用インターフェースを公開します。その下では、オペレーティング システムのユーザー インターフェイス メソッドを使用します。

  6. JavaScript エンジン: JavaScript コードの解析と実行に使用されるインタープリター。

  7. データ ストレージ:これは永続化レイヤーです。ブラウザーは、Cookie などのデータをローカルに保存する必要がある場合があります。ブラウザは、 localStorageIndexedDBFileSystemなどのストレージ メカニズムもサポートしています。

ブラウザ コンポーネント

可能な限り単純なケースから始めましょう: いくつかのテキストと 1 つの画像を含むプレーンな HTML ページです。この単純なページを処理するためにブラウザーは何をする必要があるでしょうか?

  1. 変換:ブラウザは、ディスクまたはネットワークから HTML の生のバイトを読み取り、ファイルの指定されたエンコーディング (UTF-8 など) に基づいて個々の文字に変換します。

  2. トークン化:ブラウザーは、文字列を W3C HTML5 標準で指定された個別のトークンに変換します (「」、「」など)。

    」および「山かっこ」内の他の文字列。各トークンには、特別な意味と一連のルールがあります。
  3. レキシング: 発行されたトークンは、プロパティとルールを定義する「オブジェクト」に変換されます。

  4. DOM の構築:最後に、HTML マークアップは異なるタグ間の関係を定義するため (一部のタグはタグ内に含まれます)、作成されたオブジェクトは、元のマークアップで定義された親子関係もキャプチャするツリー データ構造にリンクされます。 body オブジェクトの親、body は段落オブジェクトの親などです。

DOM 構築プロセス

このプロセス全体の最終的な出力は、ドキュメント オブジェクト モデル、つまり単純なページの「DOM」であり、ブラウザはこれをページの以降のすべての処理に使用します。

Every time the browser has to process HTML markup it has to step through all of the steps above: convert bytes to characters, identify tokens, convert tokens to nodes, and build the DOM tree. This entire process can take some time, especially if we have a large amount of HTML to process.

DevTools での DOM 構築のトレース

If you open up Chrome DevTools and record a timeline while the page is loaded, you can see the actual time taken to perform this step — in the example above, it took us ~5ms to convert a chunk of HTML bytes into a DOM tree. Of course, if the page was larger, as most pages are, this process might take significantly longer. You will see in our future sections on creating smooth animations that this can easily become your bottleneck if the browser has to process large amounts of HTML.

Rendering Engine

A rendering engine is a software component that takes marked up content (such as HTML, XML, image files, etc.) and formatting information (such as CSS, XSL, etc.) and displays the formatted content on the screen.

Browser Engine
Chrome Blink (a fork of WebKit)
Firefox Gecko
Safari Webkit
Opera Blink (Presto if < v15)
Internet Explorer Trident
Edge Blink (EdgeHTML if < v79)

WebKit is an open source rendering engine which started as an engine for the Linux platform and was modified by Apple to support Mac and Windows.

The rendering engine is single threaded. Almost everything, except network operations, happens in a single thread. In Firefox and Safari this is the main thread of the browser. In Chrome it's the tab process main thread. Network operations can be performed by several parallel threads. The number of parallel connections is limited (usually 6-13 connections per hostname).

The browser main thread is an event loop. It's an infinite loop that keeps the process alive. It waits for events (like layout and paint events) and processes them.

Note: Browsers such as Chrome run multiple instances of the rendering engine: one for each tab. Each tab runs in a separate process.

The Main flow

The rendering engine will start getting the contents of the requested document from the networking layer. This is usually done in 8KB chunks.

After that the basic flow of the rendering engine is:

Rendering engine basic flow

The rendering engine will start parsing the HTML document and convert elements to DOM nodes in a tree called the "content tree".

The engine will parse the style data, both in external CSS files and in style elements. Styling information together with visual instructions in the HTML will be used to create another tree: the render tree. The render tree contains rectangles with visual attributes like color and dimensions. The rectangles are in the right order to be displayed on the screen.

After the construction of the render tree it goes through a "layout" process. This means giving each node the exact coordinates where it should appear on the screen.

The next stage is painting-the render tree will be traversed and each node will be painted using the UI backend layer.

It's important to understand that this is a gradual process. For better user experience, the rendering engine will try to display contents on the screen as soon as possible. It will not wait until all HTML is parsed before starting to build and layout the render tree. Parts of the content will be parsed and displayed, while the process continues with the rest of the contents that keeps coming from the network.

Given below is Webkit's flow:

Webkit main flow

Parsing Basics

Parsing: Translating the document to a structure the code can use. The result of parsing is usually a tree of nodes that represent the structure of the document.

Grammar: Parsing is based on the syntax rules the document obeys: the language or format it was written in. Every format you can parse must have deterministic grammar consisting of vocabulary and syntax rules. It is called a context free grammar.

Parsing can be separated into two sub processes: lexical analysis and syntax analysis.

Lexical analysis: The process of breaking the input into tokens. Tokens are the language vocabulary: the collection of valid building blocks.

Syntax analysis: The applying of the language syntax rules.

Parsers usually divide the work between two components: the lexer (sometimes called tokenizer) that is responsible for breaking the input into valid tokens, and the parser that is responsible for constructing the parse tree by analyzing the document structure according to the language syntax rules. The lexer knows how to strip irrelevant characters like white spaces and line breaks.

Source document to parse tree

The parsing process is iterative. The parser will usually ask the lexer for a new token and try to match the token with one of the syntax rules. If a rule is matched, a node corresponding to the token will be added to the parse tree and the parser will ask for another token.

If no rule matches, the parser will store the token internally, and keep asking for tokens until a rule matching all the internally stored tokens is found. If no rule is found then the parser will raise an exception. This means the document was not valid and contained syntax errors.

The job of the HTML parser is to parse the HTML markup into a parse tree. HTML definition is in a DTD (Document Type Definition) format. This format is used to define languages of the SGML family. The format contains definitions for all allowed elements, their attributes and hierarchy. As we saw earlier, the HTML DTD doesn't form a context free grammar.

HTML parsing algorithm consists of two stages: tokenization and tree construction.

Tokenization is the lexical analysis, parsing the input into tokens. Among HTML tokens are start tags, end tags, attribute names and attribute values. The tokenizer recognizes the token, gives it to the tree constructor, and consumes the next character for recognizing the next token, and so on until the end of the input.

HTML parsing flow

DOM Tree

The output tree (the "parse tree") is a tree of DOM element and attribute nodes. DOM is short for Document Object Model. It is the object presentation of the HTML document and the interface of HTML elements to the outside world like JavaScript. The root of the tree is the "Document" object.

The DOM has an almost one-to-one relation to the markup. For example:

<html>
  <body>
    <p>
      Hello World
    </p>
    <div> <img src="example.png"/></div>
  </body>
</html>

This markup would be translated to the following DOM tree:

DOM Tree

Why is the DOM slow?

The short answer is that the DOM is not slow. Adding & removing a DOM node is a few pointer swaps, not much more than setting a property on the JS object.

However, layout is slow. When you touch the DOM in any way, you set a dirty bit on the whole tree that tells the browser it needs to figure out where everything goes again. When JS hands control back to the browser, it invokes its layout algorithm (or more technically, it invokes its CSS recalc algorithm, then layout, then repaint, then re-compositing) to redraw the screen. The layout algorithm is quite complex - read the CSS spec to understand some of the rules - and that means it often has to make non-local decisions.

Worse, layout is triggered synchronously by accessing certain properties. Among those are getComputedStyleValue(), getBoundingClientWidth(), .offsetWidth, .offsetHeight, etc, which makes them stupidly easy to run into. Full list is here. Because of this, a lot of Angular and JQuery code is stupidly slow. One layout will blow your entire frame budget on a mobile device. When I measured Google Instant c. 2013, it caused 13 layouts in one query, and locked up the screen for nearly 2 seconds on a mobile device. (It's since been sped up.)

React doesn't help speed up layout - if you want butter-smooth animations on a mobile web browser, you need to resort to other techniques like limiting everything you do in a frame to operations that can be performed on the GPU. But what it does do is ensure that there is at most one layout performed each time you update the state of the page. That's often quite an improvement on the status quo.

Render Tree

While the DOM tree is being constructed, the browser constructs another tree, the render tree. This tree is of visual elements in the order in which they will be displayed. It is the visual representation of the document. The purpose of this tree is to enable painting the contents in their correct order.

A renderer knows how to lay out and paint itself and its children. Each renderer represents a rectangular area usually corresponding to a node's CSS box.

Render tree's relation to the DOM tree

The renderers correspond to DOM elements, but the relation is not one to one. Non-visual DOM elements will not be inserted in the render tree. An example is the "head" element. Also elements whose display value was assigned to "none" will not appear in the tree (whereas elements with "hidden" visibility will appear in the tree).

There are DOM elements which correspond to several visual objects. These are usually elements with complex structure that cannot be described by a single rectangle. For example, the "select" element has three renderers: one for the display area, one for the drop down list box and one for the button. Also when text is broken into multiple lines because the width is not sufficient for one line, the new lines will be added as extra renderers.

Some render objects correspond to a DOM node but not in the same place in the tree. Floats and absolutely positioned elements are out of flow, placed in a different part of the tree, and mapped to the real frame. A placeholder frame is where they should have been.

The render tree and the corresponding DOM tree

In WebKit the process of resolving the style and creating a renderer is called "attachment". Every DOM node has an "attach" method. Attachment is synchronous, node insertion to the DOM tree calls the new node "attach" method.

Building the render tree requires calculating the visual properties of each render object. This is done by calculating the style properties of each element. The style includes style sheets of various origins, inline style elements and visual properties in the HTML (like the "bgcolor" property).The later is translated to matching CSS style properties.

CSS Parsing

CSS Selectors are matched by browser engines from right to left. Keep in mind that when a browser is doing selector matching it has one element (the one it's trying to determine style for) and all your rules and their selectors and it needs to find which rules match the element. This is different from the usual jQuery thing, say, where you only have one selector and you need to find all the elements that match that selector.

A selector's specificity is calculated as follows:

  • Count 1 if the declaration it is from is a 'style' attribute rather than a rule with a selector, 0 otherwise (= a)
  • Count the number of ID selectors in the selector (= b)
  • Count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= c)
  • Count the number of element names and pseudo-elements in the selector (= d)
  • Ignore the universal selector

Concatenating the three numbers a-b-c-d (in a number system with a large base) gives the specificity. The number base you need to use is defined by the highest count you have in one of a, b, c and d.

Examples:

*               /* a=0 b=0 c=0 -> specificity =   0 */
LI              /* a=0 b=0 c=1 -> specificity =   1 */
UL LI           /* a=0 b=0 c=2 -> specificity =   2 */
UL OL+LI        /* a=0 b=0 c=3 -> specificity =   3 */
H1 + *[REL=up]  /* a=0 b=1 c=1 -> specificity =  11 */
UL OL LI.red    /* a=0 b=1 c=3 -> specificity =  13 */
LI.red.level    /* a=0 b=2 c=1 -> specificity =  21 */
#x34y           /* a=1 b=0 c=0 -> specificity = 100 */
#s12:not(FOO)   /* a=1 b=0 c=1 -> specificity = 101 */

Why does the CSSOM have a tree structure? When computing the final set of styles for any object on the page, the browser starts with the most general rule applicable to that node (e.g. if it is a child of body element, then all body styles apply) and then recursively refines the computed styles by applying more specific rules - i.e. the rules “cascade down”.

WebKit uses a flag that marks if all top level style sheets (including @imports) have been loaded. If the style is not fully loaded when attaching, place holders are used and it is marked in the document, and they will be recalculated once the style sheets were loaded.

Layout

When the renderer is created and added to the tree, it does not have a position and size. Calculating these values is called layout or reflow.

HTML uses a flow based layout model, meaning that most of the time it is possible to compute the geometry in a single pass. Elements later 'in the flow' typically do not affect the geometry of elements that are earlier 'in the flow', so layout can proceed left-to-right, top-to-bottom through the document. The coordinate system is relative to the root frame. Top and left coordinates are used.

Layout is a recursive process. It begins at the root renderer, which corresponds to the element of the HTML document. Layout continues recursively through some or all of the frame hierarchy, computing geometric information for each renderer that requires it.

The position of the root renderer is 0,0 and its dimensions are the viewport–the visible part of the browser window. All renderers have a "layout" or "reflow" method, each renderer invokes the layout method of its children that need layout.

In order not to do a full layout for every small change, browsers use a "dirty bit" system. A renderer that is changed or added marks itself and its children as "dirty": needing layout. There are two flags: "dirty", and "children are dirty" which means that although the renderer itself may be OK, it has at least one child that needs a layout.

The layout usually has the following pattern:

  • Parent renderer determines its own width.
  • Parent goes over children and:
    • Place the child renderer (sets its x and y).
    • Calls child layout if needed–they are dirty or we are in a global layout, or for some other reason–which calculates the child's height.
  • Parent uses children's accumulative heights and the heights of margins and padding to set its own height–this will be used by the parent renderer's parent.
  • ダーティ ビットを false に設定します。

また、レイアウトのスラッシングは、ページが「ロード」される前に、Web ブラウザーが Web ページを何度もリフローまたは再描画する必要があることにも注意してください。JavaScript が普及する前は、Web サイトのリフローと描画は通常 1 回だけでしたが、最近では JavaScript がページの読み込み時に実行されることがますます一般的になり、DOM が変更され、余分なリフローや再描画が発生する可能性があります。リフローの数と Web ページの複雑さによっては、特に携帯電話やタブレットなどの低電力デバイスでは、ページの読み込み時に大幅な遅延が発生する可能性があります。

ペインティング

描画段階では、レンダー ツリーが走査され、レンダラーの「paint()」メソッドが呼び出されてコンテンツが画面に表示されます。ペイントは UI インフラストラクチャ コンポーネントを使用します。

レイアウトと同様に、ペイントもグローバル (ツリー全体をペイント) またはインクリメンタルにすることができます。インクリメンタル ペインティングでは、一部のレンダラーがツリー全体に影響しない方法で変更されます。変更されたレンダラーは、画面上のその長方形を無効にします。これにより、OS はそれを「ダーティ リージョン」と見なし、「ペイント」イベントを生成します。OS はこれを巧みに実行し、複数のリージョンを 1 つに結合します。

再描画する前に、WebKit は古い四角形をビットマップとして保存します。次に、新しい長方形と古い長方形の間のデルタのみを描画します。ブラウザーは、変更に応じて最小限のアクションを実行しようとします。したがって、要素の色を変更すると、要素が再描画されるだけです。要素の位置を変更すると、要素、その子、場合によっては兄弟のレイアウトと再描画が発生します。DOM ノードを追加すると、ノードのレイアウトと再描画が発生します。「html」要素のフォントサイズを大きくするなどの大きな変更は、キャッシュの無効化、再レイアウト、およびツリー全体の再描画を引き起こします。

3 つの異なるポジショニング スキームがあります。

  • 標準:オブジェクトはドキュメント内の位置に従って配置されます。これは、Render Tree での場所が DOM ツリーでの場所と同じであり、ボックスのタイプとサイズに従って配置されることを意味します。
  • フロート:オブジェクトは最初に通常の流れのように配置され、次に可能な限り左または右に移動されます。
  • 絶対:オブジェクトは、DOM ツリーとは別の場所にあるレンダー ツリーに配置されます。

配置スキームは、「position」プロパティと「float」属性によって設定されます。

  • 静的および相対は通常の流れを引き起こします
  • 絶対および固定原因の絶対位置決め

静的配置では、位置は定義されず、デフォルトの配置が使用されます。他のスキームでは、作成者が位置を指定します: 上、下、左、右。

レイヤーは z-index CSS プロパティによって指定されます。ボックスの 3 番目の次元、つまり「z 軸」に沿った位置を表します。

ボックスはスタックに分割されます (スタック コンテキストと呼ばれます)。各スタックでは、後方要素が最初に描画され、前方要素がユーザーに近い上に描画されます。オーバーラップの場合、最前の要素は前の要素を非表示にします。スタックは、z-index プロパティに従って並べ替えられます。「z-index」プロパティを持つボックスは、ローカル スタックを形成します。

トリビア

ウェブの誕生

CERN の英国人科学者である Tim Berners-Lee は、1989 年に World Wide Web (WWW) を発明しました。Web はもともと、世界中の大学や研究所の科学者の間で自動的に情報を共有するという要求を満たすために考案され、開発されました。

CERN の、そして世界で最初の Web サイトは、World Wide Web プロジェクト自体に特化したもので、Berners-Lee の NeXT コンピュータでホストされていました。この Web サイトでは、Web の基本的な機能について説明していました。他の人のドキュメントにアクセスする方法と、独自のサーバーをセットアップする方法。NeXT マシン (元の Web サーバー) はまだ CERN にあります。最初のウェブサイトを復元するプロジェクトの一環として、2013 年に CERN は世界初のウェブサイトを元のアドレスに復元しました。

1993 年 4 月 30 日、CERN は World Wide Web ソフトウェアをパブリック ドメインにしました。CERN は、その普及を最大化するためのより確実な方法として、次のリリースをオープン ライセンスで利用できるようにしました。これらのアクションを通じて、Web サーバーを実行するために必要なソフトウェアを自由に利用できるようにし、基本的なブラウザーとコードのライブラリーを提供することで、Web は繁栄することができました。

もっと読む:

URL に移動すると実際に何が起こるか

ブラウザのしくみ: 最新の Web ブラウザの舞台裏

ブラウザで Web サイトを閲覧すると、具体的に何が起こるでしょうか?

いつ何が起こるか

ブラウザは実際にウェブサイトをどのようにレンダリングしますか

オブジェクト モデルの構築

Web のしくみ: Web 開発の初心者 (または実際には誰でも) のための入門書