目次
- はじめに
- 本書を読むにあたって
- 目次
第1章:ブラウザを知る──Webサイトを表示するアプリケーション
ブラウザの役割❶──Webクライアントとしてのブラウザ
クライアント/サーバモデル
- Webクライアント
- Webサーバ
インターネットとWeb
- 通信プロトコル
- HTTP
- URLによるリソースの指定
- DNS
ブラウザの役割❷──レンダリングエンジンとしてのブラウザ
Webサイトの構成
HTML
- HTMLトークン
- DOMツリー
CSS
- CSSトークン
- CSSOM
- レイアウトツリー/レンダーツリー
ブラウザの役割❸──JavaScriptエンジンとしてのブラウザ
JavaScript
- JavaScriptトークン
- 抽象構文木(AST)
ブラウザAPI
コアの役割を支えるためのさらなる機能
ストレージとキャッシュ
- ストレージ
- キャッシュ
拡張機能
PWA
UIにまつわる機能
マルチプロセスアーキテクチャ
プロセス
- ブラウザプロセス/レンダラプロセス
スレッド
- UIスレッド/メインスレッド
- ワーカースレッド
- [Column] iOS上でのブラウザアプリ
ブラウザのセキュリティ対策
サイト分離(Site Isolation)
同一生成元ポリシー(Same Origin Policy)
- オリジン間リソース共有(CORS)
コンテンツセキュリティポリシー(CSP)
本書のゴール・注意点
第2章:URLを分解する──リソースを指定する住所
URLとは
スキーム(scheme)
ホスト(host)
ポート番号(port)
パス(path)
クエリパラメータ(searchpart)
URLの構文解析の実装
ライブラリクレートの作成
実装するファイルの追加
Url構造体の作成
parseメソッドの作成
URLの分割の実装
- スキームの確認
- ホストの取得
- ポート番号の取得
- パス名の取得
- クエリパラメータの取得
parseメソッドの完成
ゲッタメソッドの追加
- [Column] clone()はなぜ必要?
ユニットテストによる動作確認
成功ケース
失敗ケース
テストの実行
第3章:HTTPを実装する──ネットワーク通信を支える約束事
HTTPとは
HTTPのバージョンの違い
- HTTP/1.1の特徴
- HTTP/2の特徴
- HTTP/3の特徴
HTTPの構成
- リクエストラインとは
- ステータスラインとは
- ヘッダとは
- ボディとは
HTTPクライアントの実装
サブプロジェクトの作成
- サブプロジェクトのCargo.tomlの変更
- ルートディレクトリのCargo.tomlの変更
- Features
- バイナリターゲットの設定
リクエストの構築
- HttpClientの作成
- ホスト名からIPアドレスへの変換
- ソケットアドレスの定義
- ストリームの構築
- リクエストラインの構築
- ヘッダの構築
リクエストの送信
レスポンスの受信
HTTPレスポンスの構築
- HttpResponse構造体の作成
- Header構造体の作成
- エラー構造体の作成
- 文字列の前処理
- ステータスラインの分割
- ヘッダとボディの分割
- HttpResponse構造体を返す
- ゲッタメソッドを追加する
ユニットテストによる動作確認
成功ケース
失敗ケース
テストの実行
WasabiOS上で動かす
http://example.comへのアクセス
- メイン関数の実装
- 実行
テストサーバとのやりとり
- テストページの作成
- ローカルサーバの実行
- localhost
- メイン関数の変更
- 実行
第4章:HTMLを解析する──HTMLからDOMツリーへの変換
HTMLとは
HTMLの構成要素
- タグ
- コンテンツ
- 要素
- 属性
DOMとは
- DOMツリーを構成するノード
HTMLの字句解析──トークン列の生成
字句解析とは
トークン化アルゴリズム
実装するディレクトリとファイルの作成
HtmlTokenizer構造体の作成
HtmlToken列挙型の作成
Attribute構造体の実装
ステートマシンの実装
- Iteratorの実装
- Data状態の実装
- TagOpen状態の実装
- 文字の再利用
- EndTagOpen状態の実装
- TagName状態の実装
- BeforeAttributeName状態の実装
- AttributeName状態の実装
- AfterAttributeName状態の実装
- BeforeAttributeValue状態の実装
- AttributeValueDoubleQuoted状態の実装
- AttributeValueSingleQuoted状態の実装
- AttributeValueUnquoted状態の実装
- AfterAttributeValueQuoted状態の実装
- SelfClosingStartTag状態の実装
- ScriptData状態の実装
- ScriptDataLessThanSign状態の実装
- ScriptDataEndTagOpen状態の実装
- ScriptDataEndTagName状態の実装
- 一時的なバッファの管理
ユニットテストによる字句解析の動作確認
空文字のテスト
開始タグと終了タグのテスト
属性のテスト
空要素タグのテスト
スクリプトタグのテスト
HTMLの構文解析──ツリーの構築
実装するディレクトリ,ファイルの作成
ノードの構造
- 循環参照問題
- ノードのゲッタ・セッタメソッドの実装
ノードの種類
Window構造体の作成
Element構造体の定義
Parser構造体の作成
ツリー構築アルゴリズム
- Initial状態の実装
- BeforeHtml状態の実装
- BeforeHead状態の実装
- InHead状態の実装
- AfterHead状態の実装
- InBody状態の実装
- Text状態の実装
- AfterBody状態の実装
- AfterAfterBody状態の実装
- [Column] 間違ったHTMLをできる限り描画するブラウザ
要素ノードの追加
開いている要素のスタックの管理
テキストノードの追加
段落タグ(<p>)の追加
- ElementKind列挙型に段落の追加
- InBody状態の変更
見出しタグ(<h1>,<h2>)の追加
- ElementKind列挙型に段落の追加
- InBody状態の変更
リンクタグ(<a>)の追加
- ElementKind列挙型に段落の追加
- InBody状態の変更
テキストの追加
- InBody状態の変更
ユニットテストによる構文解析の動作確認
PartialEqとEqトレイト
Node構造体にPartialEqトレイトの実装
空文字のテスト
bodyノードのテスト
テキストノードのテスト
複数ノードのテスト
WasabiOS上で動かす
メイン関数の変更
Browser構造体の作成
Page構造体の作成
HttpResponseからDOMツリーを作成
デバッグ用にDOMツリーを文字列に変換
実行
第5章:CSSで装飾する──CSSOMとレイアウトツリーの構築
CSSとは
CSSの構成要素
- セレクタ
- プロパティ
- 値
- 宣言ブロック
- ルール
CSSOM
レイアウトツリー
- フロー
- ボックスモデル
描画
CSSの字句解析──トークン列の生成
実装するディレクトリ・ファイルの作成
- [Column] HTMLを策定するWHATWGとCSSを策定するW3C
CssToken列挙型の作成
CssTokenizer構造体の作成
次のトークンを返すメソッドの実装
- 記号トークンを返す
- 文字列トークンを返す
- 数字トークンを返す
- 識別子トークンを返す
ユニットテストによる字句解析の動作確認
空文字のテスト
1つのルールのテスト
IDセレクタを持つルールのテスト
クラスセレクタを持つルールのテスト
複数のルールのテスト
CSSの構文解析──CSSOMの構築
実装するディレクトリ・ファイルの作成
CssParser構造体の作成
CSSOMのノードの作成
- ルートノード(StyleSheet)の作成
- ルールノード(QualifiedRule)の作成
- セレクタノード(Selector)の作成
- 宣言ノード(Declaration)の作成
- コンポーネント値ノード(Component value)の作成
CSSOMの構築
- 複数のルールの解釈
- 一つのルールの解釈
- セレクタの解釈
- 複数の宣言の解釈
- 1つの宣言の解釈
- 識別子の解釈
- コンポーネント値の解釈
ユニットテストによる構文解析の動作確認
空文字のテスト
1つのルールのテスト
IDセレクタのテスト
クラスセレクタのテスト
複数のルールのテスト
レイアウトツリーの構築
実装するディレクトリ・ファイルの作成
LayoutView構造体の作成
DOMツリーの特定の要素を取得する関数の作成
LayoutObject構造体の作成
- ゲッタ/セッタメソッドの追加
- ブロック要素とインライン要素
- LayoutPoint構造体の作成
- LayoutSize構造体の作成
ComputedStyleの作成
- ゲッタ/セッタメソッドの追加
- Color構造体の作成
- FontSize列挙型の作成
- DisplayType列挙型の作成
- TextDecoration列挙型の作成
レイアウトツリーの作成
レイアウトオブジェクトのインスタンス化
- ノードが選択されているかを判断するメソッド
- CSSルールの適用(Cascading)
- 指定値の決定(Defaulting)
- ブロック/インライン要素の最終決定
ノードの位置/サイズ情報の更新
- 定数の設定ファイル
- サイズの計算
- 位置の計算
ユニットテストによるレイアウトの動作確認
LayoutObject構造体にPartialEqトレイトの実装
テスト用の便利関数の作成
空文字のテスト
<body>タグのみのテスト
テキスト要素のテスト
bodyがdisplay:noneのテスト
複数の要素がhidden:noneのテスト
GUI描画のための準備
DisplayItem列挙型の作成
LayoutObjectノードの描画
- テキストを折り返す
DisplayItemの管理
- Page構造体にフィールドを追加する
- receive_responseメソッドを更新する
- create_frameメソッドを更新する
- set_layout_viewメソッドを追加する
- paint_treeメソッドを追加する
- DisplayItemのベクタのゲッタメソッドを追加する
第6章:GUIを実装する──ユーザーとのやりとり
GUIとは
GUIアプリケーションのウィンドウの作成
サブプロジェクトの作成
- サブプロジェクトのCargo.tomlの変更
- 実装するファイルの作成
背景となる白い四角を描画する
ツールバーを描画する
- 定数を追加する
- noliライブラリの描画API
- ツールバーを描画する
UIを開始するメソッドを追加する
アプリケーションの開始時にウィンドウを描画する
- Cargo.tomlを変更する
- main.rsを変更する
ユーザーの入力を取得
マウスの位置を取得する
マウスのクリックを取得する
文字を入力する
ツールバーをクリックして入力を開始する
- InputMode列挙型を作成する
- URLの文字を保存する
- URLの情報をツールバーに反映する
- ツールバーをクリックしてInputModeを変更する
マウスを描画する
- Cursor構造体を追加する
- WasabiUIにマウスカーソルを追加する
- マウスカーソルを描画する
アドレスバーからナビゲーション
Enterキーによってナビゲーションを開始する
コンテンツエリアをリセットする
ネットワークの実装をUIコンポーネントに渡す
- 関数ポインタ
- クロージャ
- handle_urlの実装
- handle_url関数ポインタを渡す
ページの内容の描画
テキストを描画する
- 文字を出力するAPIを使用する
- 描画するための関数を実装する
- 文字の大きさの型変換を行う
- update_uiメソッドを更新する
テキストリンクを描画する
- 文字を出力するAPIで下線を引く
- update_uiメソッドを更新する
四角を描画する
WasabiOSの上で動かす
リンククリックでナビゲーション
handle_mouse_inputメソッドを更新する
clicked関数を追加する
- DOMツリーのノードの指定した属性の値を取得する
- find_node_by_positionメソッドを追加する
- find_node_by_position_internal関数を追加する
WasabiOSの上で動かす
第7章:JavaScriptを動かす──ページの動的な変更
JavaScriptとは
インタプリタ,JIT,コンパイラ言語
動的なページと静的なページ
- サーバサイドレンダリングとクライアントサイドレンダリング
ブラウザAPI
ECMAScript
JavaScriptの加算/減算の実装
実装するディレクトリの作成
トークン列挙型の作成
JsLexer構造体の作成
次のトークンを返す関数の実装
- 記号トークンを返す
- 数字トークンを返す
ユニットテストによるレキサーの動作確認
- 空文字のテスト
- 1つの数字トークンのみのテスト
- 足し算のテスト
加算・減算の文法規則
- ECMAScriptで定義されている文法規則
- 実装する文法規則
抽象構文木(AST)の構築
- 式と文
- ノードの作成
- JsParser構造体の作成
- Program構造体の作成
- ASTを構築するメソッドの作成
- SourceElementの解釈
- Statementの解釈
- AssignmentExpressionの解釈
- AdditiveExpressionの解釈
- LeftHandSideExpressionの解釈
- MemberExpressionの解釈
- PrimaryExpressionの解釈
ユニットテストによるパーサの動作確認
- 空文字のテスト
- 1つの数値だけのテスト
- 足し算のテスト
ランタイムの実装
- JsRuntime構造体の作成
- ASTの実行
- 各ノードを評価するevalメソッドの実装
- RuntimeValue列挙型の作成
- RuntimeValueどうしの加算・減算
ユニットテストによるランタイムの動作確認
- 数値のみのテスト
- 足し算のテスト
- 引き算のテスト
JavaScriptの変数の実装
変数,キーワード,文字列トークンの追加
nextメソッドの変更
- キーワードトークンを返す
- 変数トークンを返す
- 文字列トークンを返す
レキサーのユニットテストの追加
- 変数の定義のテスト
- 変数の呼び出しのテスト
実装するBNFの確認
- ECMAScriptでの定義
- 実装する文法規則
ASTの変更
- ノードの追加
- Statementの解釈の変更
- VariableDeclarationの解釈
- Identifierの解釈
- Initialiserの解釈
- AssignmentExpressionの解釈の変更
- PrimaryExpressionの解釈の変更
パーサのユニットテストの追加
- 変数定義のテスト
- 変数呼び出しのテスト
ランタイムの変更
- 変数を扱うEnvironment構造体の追加
- 変数の取得
- 変数の追加と更新
- evalメソッドの変更
- RuntimeValueに文字列の追加
ランタイムのユニットテストの追加
- 変数定義のテスト
- 変数呼び出しのテスト
- 変数変更のテスト
JavaScriptの関数呼び出しの実装
レキサーの変更
レキサーのテストの変更
実装するBNFの確認
- ECMAScriptでの定義
- 実装する文法規則
ノードの追加
パーサの変更
- SourceElementの解釈の変更
- FunctionDeclarationの解釈
- FormalParameterListの解釈
- FunctionBodyの解釈
- Statementの解釈の変更
- LeftHandSideExpressionの解釈の変更
- Argumentsの解釈
- MemberExpressionの解釈の変更
ASTのユニットテストの追加
- 関数定義のテスト
- 引数付き関数定義のテスト
- 関数呼び出しのテスト
ランタイムの変更
- evalメソッドの変更
- Function構造体の追加
ランタイムのユニットテストの追加
- 関数定義/呼び出しのテスト
- 引数付き関数定義/呼び出しのテスト
- ローカル変数のテスト
ブラウザAPIの追加
getElementByIdメソッドのサポート
- MemberExpressionの解釈の変更
- ブラウザAPIを呼び出すメソッドの追加
- 特定のIDの要素を取得する便利関数
- RuntimeValueにHtmlElementを追加する
- ランタイムにDOMツリーを渡す
- ブラウザAPIを呼び出す
textContentによるDOMノードの操作
- MemberExpressionの解釈の変更
- AssignmentExpressionの解釈の変更
WasabiOS上で動かす
HTTPレスポンスを受け取ったときにJavaScriptを実行する
- <script>タグのコンテンツを取得する便利関数
テストページの追加
ローカルサーバの構築
おわりに
- 索引