https://www.chromium.org/developers/design-documents/displaying-a-web-page-in-chrome
Chromiumがどのようにウェブページを表示するかをボトムアップに記述。マルチプロセスアーキテクチャも読む。マルチプロセスリソースローディングも面白い。
概念的アプリケーションレイヤー
(The original Google Doc for this illustration is http://goo.gl/MsEJX which is open for editing by any @chromium.org)
各階層は概念的アプリケーションレイヤー。下位レイヤーは上位レイヤに依存せず情報も持たない。
- WebKit: Safari, Chromiumその他WebKitベースのブラウザで使われる共用レンダリングエンジン。PortはWebKitの一部でプラットフォーム依存のシステムサービス(リソースロードやグラフィック)を統合する。
- Glue: WebKitの型をChromiumの型に変換する。これが我々のWebKit組み込みレイヤ。
- Renderer / Render host: Chromiumのマルチプロセス組み込みレイヤ。相互の通知やコマンドのプロキシ。
- WebContents: Contentモジュールのメインクラスで再利用可能なコンポーネント。マルチプロセスのHTMLレンダリングをビューへ容易に組み込み可能。コンテントモジュールのページ参照
- Browser: ブラウザウインドウを表す。多数のWebContentsからなる。
- Tab Helpers: 個別のオブジェクトでWebContentsと結びつく(WebContentsUserData混合)。ブラウザはこれらをWebContentsに結び付ける(faviconやinfobarなど)
WebKit
我々はオープンソースのWebKitをウェブページのレイアウトに使っている。コードはアップルからプルされ、/third_party/WebKitディレクトリにある。WebKitは主にWebCoreから構成されコアレイアウト機能をもつ。Javascriptを実行するJavaScriptCoreももつ。JavaScriptCoreはテスト目的で実行する。通常これはハイパフォーマンスなV8 JavaScriptエンジンに置き換えられている。AppleがWebKitと呼ぶレイヤでSafariなどではWebCoreとOS X間の組み込みAPIは実際には使っていない。我々はAppleからプルしたコードを一般にWebKitと呼んでいる。
WebKit port
最下位に我々のWebKit "port"がある。プラットフォーム固有の機能の実装でプラットフォーム独立のWebCoreとのインターフェースになる。ファイルはWebKitツリーにあり、chromiumディレクトリまたはChromiumサフィックスのファイルである。フォントレンダリングなどはプラットフォームごとに処理されなければならない。
- ネットワークトラフィックはマルチプロセスリソースローディングで処理され、レンダープロセスからOSを利用しない。
- グラフィックはAndroid用に開発されたSkiaグラフィックライブラリを利用する。これはクロスプラットフォームのグラフィックライブラリですべてのイメージと基本グラフィックを処理する(テキストは除く)。Skiaは/third_party/skiaにある。
WebKit glue
ChromiumとWebKitは型などを含め別のコーディングルールで記述されている。WebKit glueはその間に入りChromiumにとってやりやすいインターフェースを提供する。/WebKit/glueにある。ネーミングはWebKitオブジェクトと同じようになっているが"Web"が初めにつく。例えばWebCore::FrameはWebFrameになる。
"test shell"はベアボーンのウェブブラウザでWebKit portとglueのテストをする。
レンダープロセス
Chromiumのレンダープロセスはglueインタフェースを使いWebKit portを組み込む。多量のコードは含んでいない。その仕事はブラウザとのIPCチャネルのレンダラーサイドになることである。
レンダラーのもっとも重要なクラスはRenderViewである。/content/renderer/render_view_impl.ccにある。これがウェブページを表す。ブラウザから、またブラウザへのナビゲーション関連のコマンドを処理する。これはRenderWidgetから継承されるがこれは描画や入力を処理する。RenderViewはブラウザとの通信に(レンダープロセスごとに)グローバルなRenderProcessを使う。
FAQ:RenderWidgetとRenderViewの違いは?
RenderWidgetはWebCore::Widgetオブジェクトとマップされる。それは抽象インターフェースWebWidgetDelegateを実装することにより行う。RenderWidgetは基本的に1つのウインドウであり、入力と受け取り、描画の出力になる。RenderViewはRenderWidgetから継承され、タブやポップアップウインドウのコンテンツになり、ナビゲーション関連の命令を受け取る。RenderViewなしにRenderWidgetが存在するケースが1つだけありウェブページ上のセレクトボックスである。
レンダラー内のスレッド
レンダラーは2つのスレッドを持つ。1つはRenderViewやWebKitが動くものである。(途中)
ブラウザプロセス
下位レベルのブラウザプロセスのオブジェクト
ブラウザのIOスレッド上でレンダープロセスとのIPC通信が行われる。このスレッド上ですべてのネットワーク通信も扱われる。
メインスレッド(UIが動いている)でRenderProcessHostが初期化されるとき、新しくレンダラープロセスとChannelProxyというIPCオブジェクト(名前付きパイプ)がつくられる。このIPCオブジェクトはブラウザのIOスレッドで動き、名前付きパイプをリッスンし、メッセージをRenderProcessHostに返す。ResourceMessageFilterがこのチャネルにインストールされれば、メッセージをフィルタ出来る。
RenderProcessHostはビュー関連のメッセージを適切なRenderViewHostにディスパッチする責任がある。
上位レベルのブラウザプロセスのオブジェクト
ビュー固有のメッセージはRenderViewHost::OnMessageReceivedに到着する。ほとんどのメッセージはここで処理され残りはRenderWidgetHostへ回される。それぞれのプラットフォームでビュークラスがあり(RenderWidgetHostView[Aura|Gtk|Mac|Win])、ネイティブのビューシステムを実装する。
RenderView/Widgetの上位にWebContentsオブジェクトがある。ほとんどすべてのメッセージは結局のところこのオブジェクト上の関数コールに帰着する。WebContentsはウェブページのコンテンツを表す。コンテントオブジェクトの最上位モジュールである。矩形ビューにウェブページを表示する責任を持つ。詳しくはコンテントモジュールのページを参照。
WebContentsオブジェクトはTabContentsWrapperに含まれる。chromeディレクトリにありタブに責任を持つ。
イラストでの実例
ナビゲーションやスタートアップのさらなる実例はChromiumソースコードへの取り掛かりを参照。
"set cursor"メッセージの生涯
カーソルの切り替えはレンダラーからブラウザへ送られる典型的メッセージである。レンダラでは以下のことが起こる。
- Set cursorメッセージは入力イベントへの応答として内部的にWebKitが生成する。set cursorメッセージはRenderWidget::SetCursorから始まる。
- それはRenderWidget::Sendを呼び、メッセージをディスパッチする。このメソッドはRenderViewからもブラウザにメッセージを送るために使用される。そしてRenderThread::Sendを呼ぶ。
- これはIPC::SyncChannelを呼びブラウザへ送られる。
そしたらブラウザは次のことを行う。
- RenderProcessHost内にあるIPC::ChannelProxyはIOスレッドでこのメッセージを受け取る。
- RenderProcessHost::OnMessageReceivedがメッセージを受け取る。いくつかのメッセージはここで直接処理され、残りはRenderViewHostへ送られる。
- RenderViewHost::OnMessageReceivedにメッセージが到着する。多くのメッセージはここで処理される、我々のメッセージはここでは処理されない。
- 処理されなかったメッセージは自動的にRenderWidgetHostに送られる。我々のメッセージもここに送られる。
- メッセージマップがついにメッセージを受け取るりRenderWidgetHost::OnMsgSetCurorが呼ばれる。ここで適切なUI関数が呼ばれマウスカーソルがセットされる。
"mouse click"メッセージの生涯
マウスクリックメッセージはブラウザからレンダラーに送られる典型的メッセージ。
- RederWidgetHostViewWin::OnMouseEventがブラウザのUIスレッドで呼ばれる。これはForwardMouseEventToRendererを呼ぶ。
- このときマウス入力をプラットフォーム独立なWebMouseEventにまとめ、RenderWidgetHostに送る。
- RenderWidgetHost::ForwardInputEventはIPCメッセージViewMsg_HandleInputEventを作成、WebInputEventをその中にシリアライズする。そしてRenderWidgetHost::Sendする。
- RenderProcessHost::Sendに送られIPC::ChannelProxyで送られる。
- 内部的にはIPC:ChannelProxyはIOスレッドへのプロキシで、そこで名前付きパイプでレンダラーに送られる。
他のタイプのメッセージはWebContentsで作成されることに注意。例えばナビゲーション関連など。WebContentsからRenderViewHostへは同じような経路をたどる。
そしてレンダラーでは:
- メインスレッドのIPC::Channelがメッセージを受信し、IPC::ChannelProxyがレンダラースレッドでのプロキシになる。
- RenderView::OnMessageReceivedがメッセージを受け取る。多くのメッセージはここで処理される。ただしマウスクリックは別で、RenderWidget::OnMessageReceivedに転送されその後RederWidget::OnHandleInputEventへ転送される。
- WebWidgetImpl::HandleInputEventへ進み、WebKitのPlatformMouseEvnetクラスへ変換される。その後、WebCore::Widgetへ進む。