chrome瀏覽器設計文檔

https://sites.google.com/a/chromium.org/dev/developers/design-documents

 

Multi-process Architecture

This document describes Chromium's high-level architecture.

Problem

It's nearly impossible to build a rendering engine that never crashes or hangs. It's also nearly impossible to build a rendering engine that is perfectly secure.

In some ways, the current state of web browsers is like that of the single-user, co-operatively multi-tasked operating systems of the past. As a misbehaving application in such an operating system could take down the entire system, so can a misbehaving web page in a modern web browser. All it takes is one browser or plug-in bug to bring down the entire browser and all of the currently running tabs.

Modern operating systems are more robust because they put applications into separate processes that are walled off from one another. A crash in one application generally does not impair other applications or the integrity of the operating system, and each user's access to other users' data is restricted.

Architectural overview

We use separate processes for browser tabs to protect the overall application from bugs and glitches in the rendering engine. We also restrict access from each rendering engine process to others and to the rest of the system. In some ways, this brings to web browsing the benefits that memory protection and access control brought to operating systems.

We refer to the main process that runs the UI and manages tab and plugin processes as the "browser process" or "browser." Likewise, the tab-specific processes are called "render processes" or "renderers." The renderers use the WebKit open-source layout engine for interpreting and laying out HTML.

 

Managing render processes

Each render process has a global RenderProcess object that manages communication with the parent browser process and maintains global state. The browser maintains a corresponding RenderProcessHost for each render process, which manages browser state and communication for the renderer. The browser and the renderers communicate usingChromium's IPC system.

 

Managing views

Each render process has one or more RenderView objects, managed by the RenderProcess, which correspond to tabs of content. The corresponding RenderProcessHostmaintains a RenderViewHost corresponding to each view in the renderer. Each view is given a view ID that is used to differentiate multiple views in the same renderer. These IDs are unique inside one renderer but not within the browser, so identifying a view requires a RenderProcessHost and a view ID. Communication from the browser to a specific tab of content is done through these RenderViewHost objects, which know how to send messages through their RenderProcessHost to the RenderProcess and on to the RenderView.

 

Components and interfaces

In the render process:

  • The RenderProcess handles IPC with the corresponding RenderProcessHost in the browser. There is exactly one RenderProcess object per render process. This is how all browser ↔ renderer communication happens.
  • The RenderView object communicates with its corresponding RenderViewHost in the browser process (via the RenderProcess), and our WebKit embedding layer. This object represents the contents of one web page in a tab or popup window

 

In the browser process:

  • The Browser object represents a top-level browser window.
  • The RenderProcessHost object represents the browser side of a single browser ↔ renderer IPC connection. There is one RenderProcessHost in the browser process for each render process.
  • The RenderViewHost object encapsulates communication with the remote RenderView, and RenderWidgetHost handles the input and painting for RenderWidget in the browser.
For more detailed information on how this embedding works, see the How Chromium displays web pages design document.

Sharing the render process

In general, each new window or tab opens in a new process. The browser will spawn a new process and instruct it to create a single RenderView.

Sometimes it is necessary or desirable to share the render process between tabs or windows. A web application opens a new window that it expects to communicate with synchronously, for example, using window.open in JavaScript. In this case, when we create a new window or tab, we need to reuse the process that the window was opened with. We also have strategies to assign new tabs to existing processes if the total number of processes is too large, or if the user already has a process open navigated to that domain. These strategies are described in Process Models.

 

Detecting crashed or misbehaving renderers

Each IPC connection to a browser process watches the process handles. If these handles are signaled, the render process has crashed and the tabs are notified of the crash. For now, we show a "sad tab" screen that notifies the user that the renderer has crashed. The page can be reloaded by pressing the reload button or by starting a new navigation. When this happens, we notice that there is no process and create a new one.

 

Sandboxing the renderer

Given Webkit is running in a separate process, we have the opportunity to restrict its access to system resources. For example, we can ensure that the renderer's only access to the network is via its parent browser process. Likewise, we can restrict its access to the filesystem using the host operating system's built-in permissions.

In addition to restricting the renderer's access to the filesystem and network, we can also place limitations on its access to the user's display and related objects. We run each render process on a separate Windows "Desktop" which is not visible to the user. This prevents a compromised renderer from opening new windows or capturing keystrokes.

 

Giving back memory

Given renderers running in separate processes, it becomes straightforward to treat hidden tabs as lower priority. Normally, minimized processes on Windows have their memory automatically put into a pool of "available memory." In low-memory situations, Windows will swap this memory to disk before it swaps out higher-priority memory, helping to keep the user-visible programs more responsive. We can apply this same principle to hidden tabs. When a render process has no top-level tabs, we can release that process's "working set" size as a hint to the system to swap that memory out to disk first if necessary. Because we found that reducing the working set size also reduces tab switching performance when the user is switching between two tabs, we release this memory gradually. This means that if the user switches back to a recently used tab, that tab's memory is more likely to be paged in than less recently used tabs. Users with enough memory to run all their programs will not notice this process at all: Windows will only actually reclaim such data if it needs it, so there is no performance hit when there is ample memory.

This helps us get a more optimal memory footprint in low-memory situations. The memory associated with seldom-used background tabs can get entirely swapped out while foreground tabs' data can be entirely loaded into memory. In contrast, a single-process browser will have all tabs' data randomly distributed in its memory, and it is impossible to separate the used and unused data so cleanly, wasting both memory and performance.

 

Plug-ins

Firefox-style NPAPI plug-ins run in their own process, separate from renderers. This is described in detail in Plugin Architecture.

 

Resources

 

 

 

 

 

Inter-process Communication

Overview

Chromium has a multi-process architecture which means that we have a lot of processes communicating with each other. Our main inter-process communication primitive is the named pipe. On Linux & OS X, we use a socketpair(). A named pipe is allocated for each renderer process for communication with the browser process. The pipes are used in asynchronous mode to ensure that neither end is blocked waiting for the other.

 

IPC in the browser

Within the browser, communication with the renderers is done in a separate I/O thread. Messages to and from the views then have to be proxied over to the main thread using a ChannelProxy. The advantage of this scheme is that resource requests (for web pages, etc.), which are the most common and performance critical messages, can be handled entirely on the I/O thread and not block the user interface. These are done through the use of a ChannelProxy::MessageFilter which is inserted into the channel by the RenderProcessHost. This filter runs in the I/O thread, intercepts resource request messages, and forwards them directly to the resource dispatcher host. SeeMulti-process Resource Loading for more information on resource loading.

 

IPC in the renderer

Each renderer also has a thread that manages communication (in this case, the main thread), with the rendering and most processing happening on another thread (see the diagram in multi-process architecture). Most messages are sent from the browser to the WebKit thread through the main renderer thread and vice-versa. This extra thread is to support synchronous renderer-to-browser messages (see "Synchronous messages" below).

 

Messages

 

Types of messages

We have two primary types of messages: "routed" and "control." Routed messages are specific to a page, and will be routed to the view representing that page using the identifier for that view. For example, messages telling the view to paint, or notifications to display a context menu are routed messages.

Control messages are not specific to a given view and will be handled by the RenderProcess (renderer) or the RenderProcessHost (browser). For example, requests for resources or to modify the clipboard are not view-specific so are control messages.

Independent of the message type is whether the message is sent from the browser to the renderer, or from the renderer to the browser. Messages sent from the browser to the renderer are called View messages because they are being sent to the RenderView. Messages sent from the renderer to the browser are called ViewHost messages because they are being sentto the RenderViewHost. You will notice the messages defined in render_messages_internal.h are separated into these two categories.

Plugins also have separate processes. Like the render messages, there are PluginProcess messages (sent from the browser to the plugin process) and PluginProcessHostmessages (sent from the plugin process to the browser). These messages are all defined in plugin_messages_internal.h. The automation messages (for controlling the browser from the UI tests) are done in a similar manner.

 

Declaring messages

Special macros are used to declare messages. The messages sent between the renderer and the browser are all declared in render_messages_internal.h. There are two sections, one for "View" messages sent to the renderer, and one for "ViewHost" messages sent to the browser.

To declare a message from the renderer to the browser (a "ViewHost" message) that is specific to a view ("routed") that contains a URL and an integer as an argument, write:

IPC_MESSAGE_ROUTED2(ViewHostMsg_MyMessage, GURL, int)

To declare a control message from the browser to the renderer (a "View" message) that is not specific to a view ("control") that contains no parameters, write:

IPC_MESSAGE_CONTROL0(ViewMsg_MyMessage)

 

Pickling values

Parameters are serialized and de-serialized to message bodies using the ParamTraits template. Specializations of this template are provided for most common types inipc_message_utils.h. If you define your own types, you will also have to define your own ParamTraits specialization for it.

Sometimes, a message has too many values to be reasonably put in a message. In this case, we define a separate structure to hold the values. For example, for theViewMsg_Navigate message, the ViewMsg_Navigate_Params structure is defined in render_messages.h. That file also defines the ParamTraits specializations for the structures.

 

Sending messages

You send messages through "channels" (see below). In the browser, the RenderProcessHost contains the channel used to send messages from the UI thread of the browser to the renderer. The RenderWidgetHost (base class for RenderViewHost) provides a Send function that is used for convenience.

Messages are sent by pointer and will be deleted by the IPC layer after they are dispatched. Therefore, once you can find the appropriate Send function, just call it with a new message:

Send(new ViewMsg_StopFinding(routing_id_));
Notice that you must specify the routing ID in order for the message to be routed to the correct View/ViewHost on the receiving end. Both the RenderWidgetHost (base class forRenderViewHost) and the RenderWidget (base class for RenderView) have routing_id_ members that you can use.

 

Handling messages

Messages are handled by implementing the IPC::Channel::Listener interface, the most important function on which is OnMessageReceived. We have a variety of macros to simplify message handling in this function, which can best be illustrated by example:

 

MyClass::OnMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(MyClass, message)
// Will call OnMyMessage with the message. The parameters of the message will be unpacked for you.
IPC_MESSAGE_HANDLER(ViewHostMsg_MyMessage, OnMyMessage)
...
IPC_MESSAGE_UNHANDLED_ERROR() // This will throw an exception for unhandled messages.
IPC_END_MESSAGE_MAP()
}

// This function will be called with the parameters extracted from the ViewHostMsg_MyMessage message.
MyClass::OnMyMessage(const GURL& url, int something) {
...
}

You can also use IPC_DEFINE_MESSAGE_MAP to implement the function definition for you as well. In this case, do not specify a message variable name, it will declare aOnMessageReceived function on the given class and implement its guts.

Other macros:

 

  • IPC_MESSAGE_FORWARD: This is the same as IPC_MESSAGE_HANDLER but you can specify your own class to send the message to, instead of sending it to the current class.
IPC_MESSAGE_FORWARD(ViewHostMsg_MyMessage, some_object_pointer, SomeObject::OnMyMessage)
  • IPC_MESSAGE_HANDLER_GENERIC: This allows you to write your own code, but you have to unpack the parameters from the message yourself:
IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_MyMessage, printf("Hello, world, I got the message."))

 

Security considerations

You must be very careful when unpacking messages in the browser. Since the renderer is sandboxed, one of the easiest ways to get out of the sandbox is to take advantage of insecure message unpacking. All parameters must be carefully validated and never trusted. Be particularly careful about signedness errors.

 

Channels

IPC::Channel (defined in chrome/common/ipc_channel.h) defines the methods for communicating across pipes. IPC::SyncChannel provides additional capabilities for synchronously waiting for responses to some messages (the renderer processes use this as described below in the "Synchronous messages" section, but the browser process never does).

Channels are not thread safe. We often want to send messages using a channel on another thread. For example, when the UI thread wants to send a message, it must go through the I/O thread. For this, we use a IPC::ChanelProxy. It has a similar API as the regular channel object, but proxies messages to another thread for sending them, and proxies messages back to the original thread when receiving them. It allows your object (typically on the UI thread) to install a IPC::ChannelProxy::Listener on the channel thread (typically the I/O thread) to filter out some messages from getting proxied over. We use this for resource requests and other requests that can be handled directly on the I/O thread. RenderProcessHost installs a ResourceMessageFilter object that does this filtering.

 

Synchronous messages

Some messages should be synchronous from the renderer's perspective. This happens mostly when there is a WebKit call to us that is supposed to return something, but that we must do in the browser. Examples of this type of messages are spell-checking and getting the cookies for JavaScript. Synchronous browser-to-renderer IPC is disallowed to prevent blocking the user-interface on a potentially flaky renderer.

Danger: Do not handle any synchronous messages in the UI thread! You must handle them only in the I/O thread. Otherwise, the application might deadlock because plug-ins require synchronous painting from the UI thread, and these will be blocked when the renderer is waiting for synchronous messages from the browser.

 

Declaring synchronous messages

Synchronous messages are declared using the IPC_SYNC_MESSAGE_* macros. These macros have input and return parameters (non-synchronous messages lack the concept of return parameters). For a control function which takes two input parameters and returns one parameter, you would append 2_1 to the macro name to get:

 

IPC_SYNC_MESSAGE_CONTROL2_1(SomeMessage,  // Message name
GURL, //input_param1
int, //input_param2
std::string); //result

Likewise, you can also have messages that are routed to the view in which case you would replace "control" with "routed" to get IPC_SYNC_MESSAGE_ROUTED2_1. You can also have 0input or return parameters. Having no return parameters is used when the renderer must wait for the browser to do something, but needs no results. We use this for certain printing and clipboard operations.

 

Issuing synchronous messages

When the WebKit thread issues a synchronous IPC request, the request object (derived from IPC::SyncMessage) is dispatched to the main thread on the renderer through aIPC::SyncChannel object (the same one is also used to send all asynchronous messages). The SyncChannel will block the calling thread when it receives a synchronous message, and will only unblock it when the reply is received.

While the WebKit thread is waiting for the synchronous reply, the main thread is still receiving messages from the browser process. These messages will be added to the queue of the WebKit thread for processing when it wakes up. When the synchronous message reply is received, the thread will be un-blocked. Note that this means that the synchronous message reply can be processed out-of-order.

Synchronous messages are sent the same way normal messages are, with output parameters being given to the constructor. For example:

 

const GURL input_param("http://www.google.com/");
std::string result;
RenderThread::current()->Send(new MyMessage(input_param, &result));
printf("The result is %s/n", result.c_str());

 

Handling synchronous messages

Synchronous messages and asynchronous messages use the same IPC_MESSAGE_HANDLER, etc. macros for dispatching the message. The handler function for the message will have the same signature as the message constructor, and the function will simply write the output to the output parameter. For the above message you would add

IPC_MESSAGE_HANDLER(MyMessage, OnMyMessage)
to the OnMessageReceived function, and write:

 

void RenderProcessHost::OnMyMessage(GURL input_param, std::string* result) {
*result = input_param.spec() + " is not available";
}


Multi-process Resource Loading

Background

All network communication is handled by the main browser process. This is done not only so that the browser process can control each renderer's access to the network, but also so that we can maintain consistent session state across processes like cookies and cached data. It is also important because as a HTTP/1.1 user-agent, the browser as a whole should not open too many connections per host.

On Windows, we currently use WinHTTP. In the future, we plan to replace this with a custom HTTP implementation.  Compared to WinInet (which we have a legacy implementation for):

 

  1. Both come with all Windows versions we plan to support (Win2K and later).
  2. WinHTTP is better documented. WinInet has a more difficult API and spotty documentation.
  3. WinHTTP has a cleaner API with the support we need. A number of WinInet functions that are necessary for us (for example, for SSL state) are undocumented and unsupported, even though IE uses them.
  4. WinHTTP allows us to implement our own cache. WinInet always shares a cache with IE. We would like to control our own caching, and also sharing a cache with IE can cause problems since some sites may serve browser-specific pages.
  5. WinInet is better battle tested on account of being used by IE. WinHTTP is not used very much, and was designed for server use. It has problems with non-ASCII URL encodings sent by the server and keepalive connections. We have to provide workarounds for these issues.

Overview

Our multi-process application can be viewed in three layers. At the lowest layer is the WebKit library which renders pages. Above that are the renderer process (simplistically, one-per-tab), each of which contains one WebKit instance. Managing all the renderers is the browser process, which controls all network accesses.

 


 

 

WebKit

WebKit has a ResourceLoader object which is responsible for fetching data. Each loader has a ResourceHandle for performing the actual requests. The header file for this object is inside the WebKit code, and we are unwilling to fork it. Fortunately, it contains a d member which we can define. We remove the old implementation of ResourceHandleInternal and provide our own, located in webkit/glue/resource_handle_win.cc. Despite the name, it has nothing to do with Win32.

ResourceHandleInternal implements the virtual interface ResourceLoaderBridge::Peer, defined in webkit/glue/resource_loader_bridge.h. This is the callback interface used by the renderer to dispatch data and other events to WebKit.

ResourceHandleInternal inside WebKit talks to the renderer (to initiate or cancel requests) via the ResourceLoaderBridge virtual interface. An implementation of this interface is retrieved by calling the static function ResourceLoaderBridge::Create, which the renderer implements. The test shell uses a different resource loader, so provides a different implementation, non-IPC version of ResourceLoaderBridge, located in webkit/tools/test_shell/simple_resource_loader_bridge.

Renderer

The renderer's implementation of ResourceLoaderBridge, called IPCResourceLoaderBridge, is located in renderer/resource_dispatcher. It uses the global ResourceDispatcher singleton object (one for each renderer process) to create a unique request ID and forward the request to the browser via IPC. Responses from the browser will reference this request ID, which can then be converted back to the ResourceLoaderBridge::Peer object (inside WebKit) by the resource dispatcher.

The conversion between resource requests and their data is all handled by common/resource_loader_ipc. The renderer and the browser both share this code so that the conversions are in sync.

Browser

The RenderProcessHost objects inside the browser receive the IPC requests from each renderer. It forwards these requests to the global ResourceDispatcherHost, using a pointer to the render process host (specifically, an implementation of ResourceDispatcherHost::Receiver) and the request ID generated by the renderer to uniquely identify the request.

Each request is then converted into a URLRequest object, which in turn forwards it to it's internal URLRequestJob that implements the specific protocol desired. When the URLRequest generates notifications, its ResourceDispatcherHost::Receiver and request ID are used to send the notification to the correct RenderProcessHost for sending back to the renderer. Since the ID generated by the renderer is preserved, it is able to correlate all responses with a specific request first generated by WebKit.

Cookies

All cookies are handled by our CookieMonster object in /net/base. We do not share cookies with WinInet. The cookie monster lives in the browser process which handles all network requests because cookies need to be the same across all tabs.

Pages can request cookies for a document via document.cookie. When this occurs, we send a synchronous message from the renderer to the browser requesting the cookie. While the browser is processing the cookie, the thread that WebKit works on is suspended. When the renderer's I/O thread receives the response from the browser, it un-suspends the thread and passes the result back to the JavaScript engine.

 

 

 

How Chromium Displays Web Pages

This document describes how web pages are displayed in Chromium from the bottom up. Be sure you have read the multi-process architecture design document. You will especially want to understand the block diagram of major components. You may also be interested in multi-process resource loading for how pages are fetched from the network.

Conceptual application layers

Each box represents a conceptual application layer. It should generally be possible to build a different browser by picking any layer and replacing the layers above it. Therefore, no layer should have knowledge of or dependencies on any higher-level layers.
  • WebKit: Rendering engine shared between Safari, Chromium, and all other WebKit-based browsers. The Port is a part of WebKit that integrates with platform dependent system services such as resource loading and graphics.
  • Glue: Converts WebKit types to Chromium types. This is our "WebKit embedding layer." It is the basis of two browsers, Chromium, and test_shell (which allows us to test WebKit).
  • Renderer / Render host: This is Chromium's "multi-process embedding layer." It proxies notifications and commands across the process boundary. You could imagine other multi-process browsers using this layer, and it should not have dependencies on other browser services.
  • Tab contents: Chrome-specific layer that represents the contents of a tab. It binds with application services such as the history system and the password manager. It should not, however, assume it's embedded inside a Chromium browser window (it's used by some other Chromium components like HTML dialogs).
  • Browser: Represents the browser window, it embeds multiple TabContentses.

WebKit

We use the WebKit open-source project to lay out web pages. This code is pulled from Apple and stored in the /third_party/WebKit directory. WebKit consists primarily of "WebCore" which represents the core layout functionality, and "JavaScriptCore" which runs JavaScript. We only run JavaScriptCore for testing purposes, normally we replace it with our high performance V8 JavaScript engine. We do not actually use the layer that Apple calls "WebKit," which is the embedding API between WebCore and OS X applications such as Safari. We normally refer to the code from Apple generically as "WebKit" for convenience.

The WebKit port

At the lowest level we have our WebKit "port." This is our implementation of required platform-specific functionality that interfaces with the platform-independent WebCore code. These files are located in the WebKit tree, typically in chromium directories or as Chromium-suffixed files. Much of our port is not actually OS-specific: you could think of it as the "Chromium port" of WebCore. Some parts, like font rendering, must be handled differently for each platform.

  • Network traffic is handled by our multi-process resource loading system rather than being handed off to the OS directly from the render process.
  • Graphics uses the Skia graphics library developed for Android. This is a cross-platform graphics library and handles all images and graphics primitives except for text. Skia is located in /third_party/skia. The main entrypoint for graphics operations is /webkit/port/platform/graphics/GraphicsContextSkia.cpp. It uses many other files in the same directory as well as from /base/gfx.

The WebKit glue

The Chromium application uses different types, coding styles, and code layout than the third-party WebKit code. The WebKit "glue" provides a more convenient embedding API for WebKit using Google coding conventions and types (for example, we use std::string instead of WebCore::String and GURL instead of KURL). The glue code is located in /webkit/glue. The glue objects are typically named similar to the WebKit objects, but with "Web" at the beginning. For example, WebCore::Frame becomes WebFrame.

The WebKit "glue" layer insulates the rest of the Chromium code base from WebCore data types to help minimize the impact of WebCore changes on the Chromium code base. As such, WebCore data types are never used directly by Chromium. APIs are added to the WebKit "glue" for the benefit of Chromium when it needs to poke at some WebCore object.

The "test shell" application is a bare-bones web browser for testing our WebKit port and glue code. It uses the same glue interface for communicating with WebKit as Chromium does. It provides a simpler way for developers to test new code without having many complicated browser features, threads, and processes. This application is also used to run the automated WebKit tests.

The render process


Chromium's render process embeds our WebKit port using the glue interface. It does not contain very much code: its job is primarily to be the renderer side of the IPC channel to the browser..

The most important class in the renderer is the RenderView, located in /chrome/renderer/render_view.cc. This object represents a web page. It handles all navigation-related commands to and from the browser process. It derives from RenderWidget which provides painting and input event handling. The RenderView communicates with the browser process via the global (per render process) RenderProcess object.

FAQ: What's the difference between RenderWidget and RenderView? RenderWidget maps to one WebCore::Widget object by implementing the abstract interface in the glue layer called WebWidgetDelegate.. This is basically a Window on the screen that receives input events and that we paint into. A RenderView inherits from RenderWidget and is the contents of a tab or popup Window. It handles navigational commands in addition to the painting and input events of the widget. There is only one case where a RenderWidget exists without a RenderView, and that's for select boxes on the web page. These are the boxes with the down arrows that pop up a list of options. The select boxes must be rendered using a native window so that they can appear above everything else, and pop out of the frame if necessary. These windows need to paint and receive input, but there isn't a separate "web page" (RenderView) for them.

Threads in the renderer

Each renderer has two threads (see the multi-process architecture page for a diagram, or threading in Chromium for how to program with them). The render thread is where the main objects such as the RenderView and all WebKit code run. When it communicates to the browser, messages are first sent to the main thread, which in turn dispatches the message to the browser process. Among other things, this allows us to send messages synchronously from the renderer to the browser. This happens for a small set of operations where a result from the browser is required to continue. An example is getting the cookies for a page when requested by JavaScript. The renderer thread will block, and the main thread will queue all messages that are received until the correct response is found. Any messages received in the meantime are subsequently posted to the renderer thread for normal processing.

The browser process


Low-level browser process objects

All IPC communication with the render processes is done on the I/O thread of the browser. This thread also handles all network communication which keeps it from interfering with the user interface.

When a RenderProcessHost is initialized on the main thread (where the user interface runs), it creates the new renderer process and a ChannelProxy IPC object with a named pipe to the renderer. This object runs on the I/O thread of the browser, listening to the named pipe to the renderer, and automatically forwards all messages back to the RenderProcess on the UI thread. A ResourceMessageFilter will be installed in this channel which will filter out certain messages that can be handled directly on the I/O thread such as network requests. This filtering happens in ResourceMessageFilter::OnMessageReceived.

The RenderProcessHost on the UI thread is responsible for dispatching all view-specific messages to the appropriate RenderViewHost (it handles a limited number of non-view-specific messages itself). This dispatching happens in RenderProcessHost::OnMessageReceived.

High-level browser process objects

View-specific messages come into RenderViewHost::OnMessageReceived. Most of the messages are handled here, and the rest get forwarded to the RenderWidgetHost base class. These two objects map to the RenderView and the RenderWidget in the renderer (see "The Render Process" above for what these mean). On Microsoft Windows, we have aRenderWidgetHostHWND associated with each RenderWidgetHost that specifically manages events and drawing into a native HWND. Other systems will have a similar class for native input and painting.

Above the RenderView/Widget is the WebContents object, and most of the messages actually end up as function calls on that object. A WebContents represents the contents of a tab that shows web data. It derives from the generic TabContents class (there are a number of other specializations of TabContents for history and downloads, for example). It is the central switching point for most navigation and toplevel browser UI updating.

FAQ: Why are WebContents and RenderViewHost separate? These two objects provide different layers of functionality. You could think of RenderViewHost as Chromium's "multi-process embedding layer." RenderViewHost objects could be (but are not currently) used in other parts of the application to render content. For example, you could imagine a dialog box with a web view in it. This could use RenderViewHost to manage drawing and communication with the render process, but it would not have a "tab" or the normal navigation commands. The RenderViewHost forwards many messages to the WebContents via its RenderViewHostDelegate abstract interface. The WebContents handles the navigational state and anything related to the UI of the web browser. Our hypothetical dialog box wouldn't need any of this functionality and would only implement the parts of the RenderViewHostDelegateinterface that it cares about.

Illustrative examples

Additional examples covering navigation and startup are in Getting Around the Chromium Source Code.

Life of a "set cursor" message

Setting the cursor is an example of a typical message that is sent from the renderer to the browser. In the renderer, here is what happens.
  • Set cursor messages are generated by WebKit internally, typically in response to an input event. The set cursor message will start out in RenderWidget::SetCursor inchrome/renderer/render_widget.cc.
  • It will call RenderWidget::Send to dispatch the message. This method is also used by RenderView to send messages to the browser. It will call RenderThread::Send.
  • This will call the IPC::SyncChannel which will internally proxy the message to the main thread of the renderer and post it to the named pipe for sending to the browser.

Then the browser takes control:

  • The IPC::ChannelProxy in the RenderProcessHost receives all message on the I/O thread of the browser. It first sends them through the ResourceMessageFilter that dispatches network requests and related messages directly on the I/O thread. Since our message is not filtered out, it continues on to the UI thread of the browser (theIPC::ChannelProxy does this internally).
  • RenderProcessHost::OnMessageReceived in chrome/browser/render_process_host.cc gets the messages for all views in the corresponding render process. It handles several types of messages directly, and for the rest forwards to the appropriate RenderViewHost corresponding to the source RenderView that sent the message.
  • The message arrives at RenderViewHost::OnMessageReceived in chrome/browser/render_view_host.cc. Many messages are handled here, but ours is not because it's a message sent from the RenderWidget and handled by the RenderWidgetHost.
  • All unhandled messages in RenderViewHost are automatically forwarded to the RenderWidgetHost, including our set cursor message.
  • The message map in chrome/browser/render_widget_host.cc finally receives the message in RenderWidgetHost::OnMsgSetCursor and calls the appropriate UI function to set the mouse cursor.

Life of a "mouse click" message

Sending a mouse click is a typical example of a message going from the browser to the renderer.

  • The Windows message is received on the UI thread of the browser by RenderWidgetHostHWND::OnMouseEvent which then calls ForwardMouseEventToRenderer in the same class.
  • The forwarder function packages the input event into a cross-platform WebMouseEvent and ends up sending it to the RenderWigetHost it is associated with.
  • RenderWidgetHost::ForwardInputEvent creates an IPC message ViewMsg_HandleInputEvent, serializes the WebInputEvent to it, and callsRenderWigetHost::Send.
  • This just forwards to the owning RenderProcessHost::Send function, which in turn gives the message to the IPC::ChannelProxy.
  • Internally, the IPC::ChannelProxy will proxy the message to the I/O thread of the browser and write it to the named pipe of the corresponding renderer.

Note that many other types of messages are created in the WebContents, especially navigational ones. These follow a similar path from the WebContents to the RenderViewHost.

Then the renderer takes control:

  • IPC::Channel on the main thread of the renderer reads the message sent by the browser, and IPC::ChannelProxy proxies to the renderer thread.
  • RenderView::OnMessageReceived gets the message. Many types messages are handled here directly. Since the click message is not, it falls through (with all other unhandled messages) to RenderWidget::OnMessageReceived which in turn forwards it to RenderWidget::OnHandleInputEvent.
  • The input event is given to WebWidgetImpl::HandleInputEvent where it is converted to a WebKit PlatformMouseEvent class and given to the WebCore::Widget class inside WebKit.

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章